Docs

TurboBulk

TurboBulk is a high-performance bulk data API for NetBox that loads, updates, deletes, and exports tens of thousands of objects per second - orders of magnitude faster than the REST API. It uses PostgreSQL COPY, staging tables, and Apache Parquet to achieve throughput of 5,000–25,000 rows/sec across all NetBox object types.

Availability: Premium tier on NetBox Cloud. Enterprise support coming soon.

Client library, detailed docs, and examples: netboxlabs/netbox-turbobulk-public


When to use TurboBulk

Use caseExample
Initial data populationMigrate an existing DCIM/IPAM into NetBox
Ongoing syncsNightly import of 500K IP addresses from an external source
Bulk exports for analyticsExport all devices + interfaces to a data warehouse
Large-scale changesRe-assign 100K interfaces to a new VLAN group
Branch-based workflowsLoad proposed changes into a NetBox branch, review, merge

When NOT to use TurboBulk

  • < 1,000 objects - the REST API is fine at this scale.
  • Interactive single-object edits - use the NetBox UI or REST API.
  • Workflows that depend on synchronous custom validators - TurboBulk does not execute Django model validators (DB constraints are still enforced).
  • Workflows that depend on per-object webhooks firing in real time - events are dispatched asynchronously after the entire operation completes.

TurboBulk vs. Replica Cache

TurboBulk is for bulk reads and writes with real-time consistency against the primary database. Replica Cache is a read-only API backed by a replica with zero primary-DB load and outage resilience. They are complementary - use Replica Cache for high-frequency reads and TurboBulk for bulk writes and exports.


Capabilities

CapabilityDescription
Bulk insertInsert new rows from JSONL or Parquet files
Bulk upsertInsert or update on conflict (configurable conflict fields or named constraints)
Bulk deleteDelete rows by primary key or custom key fields, with cascading FK nullification
Bulk exportExport full tables or filtered subsets to JSONL (gzipped) or Parquet
Export cachingServer-side cache with TTL - repeated exports return instantly via cache key
Dry-run validationValidate data without committing; returns errors and rolls back
Validation modesnone (skip), auto (SQL pre-validation for IP/prefix), full (Django full_clean() per row)
BranchingOperate within a NetBox branch (requires netbox-branching plugin)
ChangelogsGenerate ObjectChange records for audit trail (opt-in per operation)
Event dispatchFire webhooks and event rules after operation completes (async)
Save hooksReplicate model save() side-effects as bulk SQL (computed fields, denormalization)

All write operations are fully atomic - they succeed completely or roll back with no partial state.


Getting started

Prerequisites

  • NetBox Cloud premium tier with TurboBulk enabled
  • A NetBox API token with appropriate object permissions
  • Python 3.10+

Install the client

pip install turbobulk-client

Quick example

import os
from turbobulk import TurboBulkClient

client = TurboBulkClient(
    url=os.environ["NETBOX_URL"],
    token=os.environ["NETBOX_TOKEN"],
)

# Bulk insert sites
data = [
    {"name": "Site-A", "slug": "site-a", "status": "active"},
    {"name": "Site-B", "slug": "site-b", "status": "active"},
]
result = client.load("dcim.site", data, mode="insert")
print(f"Inserted {result['rows_inserted']} sites")

# Bulk export all sites
export = client.export("dcim.site")
print(f"Exported {export['rows_exported']} sites")

For more examples, see the example scripts in the public repo.


Data formats

TurboBulk supports two wire formats:

FormatExtensionBest forNotes
JSONL.jsonl, .jsonl.gzSimplicity, small-to-medium datasetsOne JSON object per line. Gzip supported. Default format.
Parquet.parquetMaximum throughput, 100K+ rowsColumnar, compressed, strongly typed. Requires PyArrow.

Format is auto-detected from file content, Content-Type header, or filename extension. When in doubt, start with JSONL - switch to Parquet when throughput matters.


Important caveats

FK columns must use the _id suffix

This is the most common source of errors. Foreign key fields must reference the database column name (with _id suffix), not the Django relation name:

{"name": "switch-01", "site_id": 1, "role_id": 3, "device_type_id": 5}

Not: {"name": "switch-01", "site": 1, "role": 3, "device_type": 5}

Use GET /api/plugins/turbobulk/models/{model}/ to discover the exact field names and types for any model.

Exported JSON types are not always the column types

In a JSONL export, several field types arrive as a different JSON type than db_type or arrow_type would suggest. Each field in the schema response carries a jsonl_type giving the type actually written, so clients do not have to infer it:

Field typearrow_typeIn JSONLWhy
DecimalFielddecimal128(p,s)"42.50" stringSerialised via json.dumps(default=str)
JSONFieldstringJSON stringEncoded before writing; an empty object becomes null
ArrayFieldstringJSON arrayLists are written through unchanged
DateTimeField / DateField / TimeFieldtimestamp / date32 / time64ISO stringisoformat()
ForeignKeyint64integerThe related primary key, never a nested object

A custom_field_data value therefore needs parsing twice - once as the JSONL line, then again as the string it holds.

Each field also carries:

  • jsonl_nullable - whether the export can write null, which is wider than nullable. An empty JSON object is written as null even where the column is NOT NULL, so custom_field_data is nullable: false and jsonl_nullable: true.
  • array_item_type - the JSON type of the items when jsonl_type is array.
  • choice_labels - value to human label for choice fields. The export writes the bare value (active), so this is the only source for the label a UI would show (Active).

_tags is not a model field: it is appended when include_tags is requested (the default) and holds an array of tag slugs. The schema lists it under export_only_columns, described with the same keys a field carries, so fields plus export_only_columns can be concatenated and treated as one typed list describing an export row exactly.

jsonl_format_version gives the version of the value encoding above. It is bumped when the JSON type or encoding of any column changes, so a consumer can pin the contract rather than discovering a change by misreading values. Adding or removing a column does not bump it - columns are already discoverable per model.

Custom validators are not executed

TurboBulk bypasses the Django ORM for writes. Database constraints (NOT NULL, UNIQUE, FK) are enforced by PostgreSQL. Built-in validation is available via validation_mode (auto or full), but custom model validators and clean() methods are not called.

Write operations are disabled by default

Write operations are disabled by default for safety. Contact NetBox Labs to enable writes and tune the TurboBulk configuration for safety and performance in your environment. We recommend starting with export-only operations to validate your workflow before enabling writes.

Events are dispatched asynchronously

Webhooks and event rules fire after the entire operation completes, not per-object during the operation. Custom scripts are not triggered. Set dispatch_events: true in your request to enable event dispatch.

For branch-scoped operations, event dispatch is skipped: events for branch changes fire when the branch is merged into main.


Limits

LimitValue
Maximum upload size1 GB
Job timeout1 hour
NetBox version4.5+

API endpoints

All endpoints are under /api/plugins/turbobulk/. Authentication is via NetBox API token, or - on NetBox Cloud instances with platform authorization enabled - a NetBox Identity Token (NIT) presented as a Bearer token.

MethodEndpointDescription
GET/models/List all available models and their capabilities
GET/models/{app_label.model_name}/Get full schema for a model (fields, types, constraints)
POST/load/Bulk insert or upsert from JSONL/Parquet
POST/delete/Bulk delete by primary key or custom key fields
POST/export/Bulk export to JSONL or Parquet (async job)
GET/jobs/{job_id}/Check job status and results
GET/jobs/{job_id}/download/Download export result file
GET/cache/{cache_key}/download/Download cached export by cache key

For full request/response schemas and parameter details, see the API Reference.


Resources

I want to...Link
Get started with the client libraryQuickstart
See working code examplesExamples
Read full API documentationAPI Reference
Understand validation, hooks, and changelogsUser Guide
Use TurboBulk with NetBox BranchingBranching Guide
Troubleshoot errorsTroubleshooting
Report an issue or request a featureGitHub Issues
Get supportContact NetBox Labs

On this page