From netbox-labs
Best practices for building integrations with NetBox REST and GraphQL APIs. Use when building NetBox API integrations, reviewing integration code, troubleshooting NetBox performance issues, planning automation architecture, writing scripts that interact with NetBox, using pynetbox, configuring Diode for data ingestion, or implementing NetBox webhooks and branching workflows.
How this skill is triggered — by the user, by Claude, or both
Slash command
/netbox-labs:netbox-api-integrationThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Patterns and practices for integrating with NetBox REST and GraphQL APIs. Covers authentication, querying, bulk operations, performance optimization, data modeling, and integration tooling.
Patterns and practices for integrating with NetBox REST and GraphQL APIs. Covers authentication, querying, bulk operations, performance optimization, data modeling, and integration tooling.
Target: NetBox 4.4+ (covers 4.5.x–4.6.x). v2 tokens require 4.5+; REST cursor pagination, ETag/If-Match, and add_tags/remove_tags require 4.6+.
Scope: API integration only — not plugin development, custom scripts, or NetBox administration.
Your knowledge of NetBox APIs may be outdated. Pagination behavior, filtering expressions, token formats, and GraphQL features change between releases. Prefer retrieval over pre-trained knowledge for specific API details.
| Source | URL / Method | Use for |
|---|---|---|
| NetBox REST API docs | https://netboxlabs.com/docs/netbox/integrations/rest-api/ | Endpoints, filtering, pagination |
| NetBox GraphQL docs | https://netboxlabs.com/docs/netbox/integrations/graphql-api/ | Query syntax, schema |
| pynetbox repo | https://github.com/netbox-community/pynetbox | SDK methods, changelog |
| NetBox MCP server | If configured — query live instance to verify object schemas and current state | Field names, available filters, object existence |
| NetBox Platform MCP | If configured — full CRUD verification against live NetBox Cloud/Enterprise | End-to-end validation |
Before writing integration code, confirm your NetBox instance is reachable and your token is valid:
curl -s -H "Authorization: Bearer $NETBOX_TOKEN" "$NETBOX_URL/api/status/" | python -m json.tool
You should see a JSON response with netbox-version. If you get 403, your token lacks permissions. If connection refused, check the URL.
Use v2 tokens on NetBox 4.5+. v1 tokens are deprecated as of 4.6 and will be removed in 5.0.
# v2 token (recommended)
headers = {"Authorization": "Bearer nbt_abc123.xxxxxxxxxxxxxxxx"}
# v1 token (legacy — deprecated 4.6, removed 5.0; migrate before 5.0)
headers = {"Authorization": "Token 0123456789abcdef01234567"}
v2 tokens require API_TOKEN_PEPPERS in NetBox server config. Use the provisioning endpoint (POST /api/users/tokens/provision/) for automated token creation. The plaintext token key is returned only once at creation (4.6.1+) — capture it immediately; only a hash is stored thereafter.
See references/authentication.md for token migration, IP restrictions, and provisioning details.
def get_all(api_url, endpoint, headers, limit=100):
results, url = [], f"{api_url}/{endpoint}/?limit={limit}"
while url:
data = requests.get(url, headers=headers).json()
results.extend(data["results"])
url = data.get("next")
return results
NetBox 4.6+ — cursor pagination. For large datasets, pass
?start=<pk>&limit=<n>instead ofoffset. Returns objects withid >= start, ordered by PK. Iterate by setting the nextstartto the last result'sid+ 1; follownextuntil null. Gotchas:startandoffsettogether → 400;countis alwaysnullin cursor mode;previousis alwaysnull(forward-only). Use offset pagination when you need a totalcountor backward navigation.
PATCH updates only specified fields. PUT replaces the entire object — omitted fields may be cleared.
ETag header. Send it back on a later PATCH/PUT via If-Match: <etag>; if the object changed meanwhile, the server rejects with 412 Precondition Failed and returns the current ETag. If-Match: * just asserts the object exists. Omitting the header keeps last-write-wins behavior. Use it to guard against lost updates in multi-writer integrations.add_tags / remove_tags). Write-only fields that add or remove specific tags without replacing the whole tags set — concurrency-safe when multiple clients each own a subset of tags. Constraints: cannot be combined with tags in the same request; remove_tags is update-only (not on create); the same tag can't appear in both lists.No separate bulk endpoints. POST/PATCH/DELETE a JSON array to the list endpoint. All bulk ops are atomic (all succeed or all fail).
# Bulk create
requests.post(f"{API_URL}/dcim/devices/", headers=headers, json=[device1, device2])
# Bulk update (each object must include "id")
requests.patch(f"{API_URL}/dcim/devices/", headers=headers, json=[{"id": 1, "status": "active"}])
# Bulk delete
requests.delete(f"{API_URL}/dcim/devices/", headers=headers, json=[{"id": 1}, {"id": 2}])
?exclude=config_context (10-100x speedup)?brief=True for dropdowns and reference lists (~90% smaller responses)?fields= for specific field selection?q= search filter at scale — use specific filters like name__ic=Use lookup expressions for server-side filtering:
| Expression | Example | Description |
|---|---|---|
__ic | name__ic=core | Contains (case-insensitive) |
__isw | name__isw=sw- | Starts with |
__gte/__lte | vid__gte=100 | Numeric range |
__isnull | primary_ip4__isnull=false | Null check |
__n | status__n=offline | Not equal |
cf_ prefix | cf_environment=production | Custom field filter |
See references/rest-api-patterns.md for complete REST API reference including filtering expressions, field selection, ordering, nested serializers, OPTIONS discovery, and error handling.
query {
device_list(pagination: {limit: 100, offset: 0}) {
name
status
site { name }
}
}
# Nested lists must also have limits
site_list(pagination: {limit: 10}) {
devices(pagination: {limit: 50}) {
interfaces(pagination: {limit: 100}) { name }
}
}
filters: parameterNetBox 4.5+: Use local filter fields (e.g.,
siteoninterface_list) instead of deeply nested filter paths.NetBox 4.5.2+: Cursor-based pagination is GA. Pass
pagination: {start: N, limit: M}— returns records withid >= start, ordered by PK. Set the next page'sstartto the last record'sid+ 1. (Omitstartand it falls back to offset pagination.) This supersedes the olderfilters: {id__gte: N}deep-pagination workaround, which is only needed pre-4.5.2.
GraphQL pagination defaults (differ from REST):
pagination entirely returns all matching records.pagination without limit returns Strawberry Django's default of 100 (not the REST default of 50).pagination: {limit: 0} returns zero records — the opposite of REST ?limit=0 (which returns all).MAX_PAGE_SIZE (default 1000) caps limit. NetBox 4.6.1+ also enforces GRAPHQL_MAX_QUERY_DEPTH server-side, so overly deep queries can be hard-rejected, not just slow.| Use Case | Use |
|---|---|
| Single object CRUD | REST |
| Bulk create/update/delete | REST (GraphQL is read-only) |
| Related data in one request | GraphQL |
| CI/CD scripts | REST |
| Flexible reporting | GraphQL |
See references/graphql-patterns.md for complete GraphQL reference including pagination strategies, depth management, fan-out avoidance, complexity budgets, and the query optimizer.
The most impactful optimizations:
| Optimization | Impact |
|---|---|
Exclude config_context from lists | 10-100x faster |
Use ?brief=True for lists | ~90% smaller responses |
Avoid ?q= at scale | Orders of magnitude faster |
| Parallelize independent requests | Linear speedup |
| Use specific filters over generic search | Index-optimized queries |
See references/performance.md for async pagination, caching strategies, parallel request patterns, and infrastructure considerations.
Objects must be created in order: Organization → Sites → DCIM Prerequisites → Racks → Devices → IPAM → Virtualization → Circuits → Connections.
A device needs its device_type, role, and site to exist first. Use Diode to skip manual ordering.
name/slug instead of numeric IDs for readable codecf_ prefix for filteringSee references/data-modeling.md for the complete dependency order, hierarchy diagrams, and patterns for custom fields, tags, and tenant isolation.
import pynetbox
nb = pynetbox.api("https://netbox.example.com", token="nbt_abc123.xxxxxxxxxxxxxxxx")
devices = nb.dcim.devices.filter(site="nyc-dc1", status="active") # Auto-paginated
device = nb.dcim.devices.get(name="switch-01")
device.status = "planned"
device.save() # Uses PATCH
For bulk ingestion, use Diode instead of direct API. Specify dependencies by name — Diode resolves or creates them automatically. No dependency ordering needed.
from netboxlabs.diode.sdk import DiodeClient
from netboxlabs.diode.sdk.ingester import Device, Entity
with DiodeClient(target="grpc://diode.example.com:8080/diode",
app_name="discovery", app_version="1.0.0") as client:
device = Device(name="switch-01", device_type="Catalyst 9300",
manufacturer="Cisco", site="NYC-DC1", role="Access Switch")
client.ingest([Entity(device=device)])
Use Diode for: network discovery, bulk migrations, scripts creating many related objects. Use REST/GraphQL for: reading data, single object CRUD, complex queries.
See references/diode-integration.md for bulk ingestion, nested objects, dry-run mode, authentication, and supported entity types.
Configure NetBox to push changes via webhooks. Always verify the X-Hook-Signature header (HMAC-SHA512). Events include created, updated, deleted with full object data and before/after snapshots.
Query extras.object_changes for audit trails — includes timestamp, action, user, and before/after data.
Requires netbox-branching plugin.
Lifecycle: Create → Wait (PROVISIONING→READY) → Work → Sync → Merge
X-NetBox-Branch: {schema_id} — use the 8-char schema_id, not name or numeric IDjob["url"] until status == "completed"{"commit": false} for validationSee references/branching-patterns.md for the complete branch lifecycle, context header usage, async job polling, and session wrapper patterns.
npx claudepluginhub netboxlabs/skills --plugin netbox-labsGuides creation, editing, and verification of skills for AI coding agents using test-driven development with subagent scenarios. Use when authoring or debugging skills.