Quick Start
This guide walks through the basics of using pynetbox to interact with NetBox.
Basic Connection
Import pynetbox and create an API connection:
import pynetbox
nb = pynetbox.api(
'http://localhost:8000',
token='d6f4e314a5b5fefd164995169f28ae32d987704f'
)
Connection Parameters
pynetbox.api() accepts the following parameters:
url(required): Base URL of your NetBox instance, including scheme (http://orhttps://).token(optional): API authentication token. Required for write operations and for endpoints that are not public.threading(optional, defaultFalse): Enable multithreaded page fetching for.all()and.filter(). See Threading.strict_filters(optional, defaultFalse): Validate filter parameters against NetBox's OpenAPI specification. See Filter Validation.
nb = pynetbox.api(
'http://localhost:8000',
token='your-token-here',
threading=True,
strict_filters=True,
)
API Structure
pynetbox mirrors NetBox's app structure. Each NetBox app is exposed as an attribute of the API object:
nb.circuits # Circuit management
nb.core # Core objects (data sources, jobs, object changes)
nb.dcim # Data Center Infrastructure Management
nb.extras # Tags, custom fields, webhooks, etc.
nb.ipam # IP Address Management
nb.tenancy # Tenants and contacts
nb.users # Users and permissions
nb.virtualization # Virtual machines and clusters
nb.vpn # VPN tunnels and terminations
nb.wireless # Wireless LANs and links
Each app exposes endpoints that map to NetBox API endpoints. Dashes in endpoint names are converted to underscores:
nb.dcim.devices
nb.dcim.sites
nb.dcim.racks
nb.ipam.ip_addresses # /api/ipam/ip-addresses/
nb.ipam.prefixes
Plugin Endpoints
Endpoints provided by NetBox plugins are accessible under the plugins namespace. As with built-in endpoints, dashes in plugin or endpoint names are converted to underscores:
# /api/plugins/my-plugin/objects/
nb.plugins.my_plugin.objects.all()
Querying Data
Getting All Objects
Use .all() to retrieve every object from an endpoint:
for device in nb.dcim.devices.all():
print(device.name)
RecordSet is a one-shot iterator
.all() and .filter() return a RecordSet, which can only be iterated once. To iterate the results multiple times, materialize them with list():
devices = list(nb.dcim.devices.all())
Filtering Objects
Use .filter() to query objects matching one or more criteria. Keyword arguments correspond to filter parameters supported by the NetBox endpoint:
# All devices with a specific role
leaf_switches = nb.dcim.devices.filter(role='leaf-switch')
# Multiple filters (AND)
devices = nb.dcim.devices.filter(
site='headquarters',
status='active',
role='access-switch',
)
# Filter on a custom field
devices = nb.dcim.devices.filter(cf_environment='production')
# Freeform search (passed as ?q=...)
results = nb.dcim.devices.filter('rack1')
Invalid filters do not raise an error by default
NetBox silently ignores unrecognized filter parameters, which means a typo can quietly return the entire table. Enable strict filter validation to catch these errors early.
Getting a Single Object
Use .get() to retrieve a single object. It returns None if no match is found and raises ValueError if more than one record matches:
# By ID
device = nb.dcim.devices.get(1)
# By a unique field
device = nb.dcim.devices.get(name='spine1')
# Combining filters when a single field is not unique
location = nb.dcim.locations.get(site='site-1', name='Row 1')
# get() returns None if no match is found
device = nb.dcim.devices.get(name='nonexistent')
if device is None:
print("Device not found")
Counting Objects
Use .count() to retrieve only the count of matching objects without fetching them:
total = nb.dcim.devices.count()
active = nb.dcim.devices.count(status='active')
Working with Records
Accessing Attributes
API fields are exposed as attributes on Record objects:
device = nb.dcim.devices.get(1)
print(device.name)
print(device.serial)
print(device.device_type.model) # Nested object
print(device.site.name)
Inspecting Available Attributes
device = nb.dcim.devices.get(1)
# As a dict
print(dict(device))
# Or get the data structure ready for an API payload
print(device.serialize())
Creating Objects
Use .create() to create new objects. Pass field values as keyword arguments:
# Create a site
new_site = nb.dcim.sites.create(
name='new-datacenter',
slug='new-datacenter',
status='planned',
)
# Create a device (NetBox 3.6+ uses `role`; pre-3.6 used `device_role`)
new_device = nb.dcim.devices.create(
name='new-switch',
device_type=1,
site=new_site.id,
role=5,
)
# Create an IP address with assignment to an interface
new_ip = nb.ipam.ip_addresses.create(
address='10.0.0.1/24',
status='active',
assigned_object_type='dcim.interface',
assigned_object_id=123,
)
To create multiple objects in a single request, pass a list of dicts:
nb.dcim.sites.create([
{'name': 'site-1', 'slug': 'site-1'},
{'name': 'site-2', 'slug': 'site-2'},
])
Updating Objects
There are two ways to update a single object.
Modify attributes and save()
device = nb.dcim.devices.get(1)
device.serial = 'ABC123'
device.asset_tag = 'ASSET001'
device.save()
save() sends a PATCH containing only the fields that have changed and returns True if the request succeeded.
Pass a dict to update()
device = nb.dcim.devices.get(1)
device.update({
'serial': 'ABC123',
'asset_tag': 'ASSET001',
})
Bulk Updates
To update multiple records in a single request, call .update() on the endpoint with a list of records or dicts. Each dict must include an id:
devices = nb.dcim.devices.filter(site='test1')
for device in devices:
device.status = 'active'
nb.dcim.devices.update(devices)
RecordSet also supports a chainable form:
nb.dcim.devices.filter(site_id=1).update(status='active')
Deleting Objects
Use .delete() to remove an object:
device = nb.dcim.devices.get(1)
device.delete()
To delete multiple objects in a single request, pass a list (or RecordSet) of records or IDs to the endpoint's .delete():
nb.dcim.devices.delete([1, 2, 3])
# Or, delete every record matching a filter:
nb.dcim.devices.filter(status='offline').delete()
Working with Choices
Use .choices() to retrieve the valid values for each choice field on an endpoint. The return value is a dict keyed by field name, with each value being a list of {value, label} entries:
choices = nb.dcim.devices.choices()
for choice in choices['status']:
print(choice['value'], choice['label'])
# active Active
# planned Planned
# staged Staged
# ...
Pagination
NetBox paginates list responses, but pynetbox fetches subsequent pages automatically as you iterate the result set. You can override the page size with limit:
# Fetch 100 records per page (instead of the server default)
devices = nb.dcim.devices.filter(limit=100)
# Start at a specific offset (requires a positive limit)
devices = nb.dcim.devices.filter(limit=100, offset=200)
Error Handling
pynetbox raises specific exceptions for different failure modes:
import pynetbox
try:
nb.dcim.devices.create(
name='test-device',
device_type=1,
site=1,
role=1,
)
except pynetbox.RequestError as e:
# HTTP error from NetBox (4xx/5xx)
print(f"Request failed: {e.error}")
except pynetbox.AllocationError as e:
# No room left in an available-ips / available-prefixes pool
print(f"Allocation failed: {e}")
except pynetbox.ContentError as e:
# Server returned a non-JSON response (usually a misconfigured URL)
print(f"Content error: {e}")
except pynetbox.ParameterValidationError as e:
# strict_filters=True and a filter parameter is invalid
print(f"Invalid filter: {e}")
Next Steps
- API Reference — detailed documentation of the core classes.
- Advanced Topics — threading, filter validation, custom sessions, file uploads.
- Endpoint-specific helpers:
- NetBox REST API documentation — the canonical reference for fields and filters.