REST API
The NetBox Custom Objects plugin exposes full CRUD operations through the standard NetBox REST API, rooted at /api/plugins/custom-objects/. The root of the API lists endpoints for Custom Object Types, their fields and constraints, the linked-objects helper, and a dynamically generated endpoint for each Custom Object Type you can view:
{
"custom-object-type-constraints": "https://netbox/api/plugins/custom-objects/custom-object-type-constraints/",
"custom-object-type-fields": "https://netbox/api/plugins/custom-objects/custom-object-type-fields/",
"custom-object-types": "https://netbox/api/plugins/custom-objects/custom-object-types/",
"linked-objects": "https://netbox/api/plugins/custom-objects/linked-objects/",
"dhcp_scope": "https://netbox/api/plugins/custom-objects/dhcp_scope/"
}The endpoint name for each Custom Object Type is its slug - a type with slug dhcp_scope is reachable at /api/plugins/custom-objects/dhcp_scope/.
Requests are authenticated as for the rest of NetBox's REST API, and NetBox's object permissions apply. For example, to list the objects of a type with a v2 token:
curl -H "Authorization: Bearer nbt_<key>.<token>" \
-H "Accept: application/json" \
https://netbox/api/plugins/custom-objects/dhcp_scope/The examples below show the JSON body of each request; send it with -H "Content-Type: application/json" and --data.
For schema management - exporting Custom Object Type definitions, previewing diffs, and applying schemas to other instances - see the Portable Schema documentation, which also covers the schema/preview/ and schema/apply/ endpoints.
Custom Object Types
Create a Custom Object Type with a POST to /api/plugins/custom-objects/custom-object-types/:
{
"name": "server",
"slug": "server",
"description": "Server inventory objects",
"verbose_name": "Server",
"verbose_name_plural": "Servers"
}| Field | Required | Description |
|---|---|---|
name | yes | Internal name. Must be lowercase alphanumeric with underscores only (e.g. dhcp_scope). Must be unique. |
slug | yes | URL-safe slug used in API paths and navigation. Must be unique. |
verbose_name | no | Singular display name shown in the UI. Defaults to the title-cased name. |
verbose_name_plural | no | Plural display name. Defaults to verbose_name + "s". |
description | no | Short description (max 200 characters). |
version | no | PEP 440 version string (e.g. 1.0.0). Used by the portable schema feature. |
group_name | no | Groups similar Custom Object Types together in the navigation menu. |
display_expression | no | Jinja expression that renders each object's display name, e.g. {{ hostname }} ({{ cpu_cores }} cores). Overrides the primary field. |
config_context_enabled | no | Gives objects of this type config context support (see Config context). Can only be set when the type is created. Default: false. |
tags | no | List of NetBox tag IDs to attach to this Custom Object Type. |
Responses also include the type's fields (each field's full representation), its schema_document (see Portable Schema), and the read-only table_model_name and object_type_name of the model that backs the type.
Custom Object Type Fields
Define the schema of a Custom Object Type by creating fields with POST requests to /api/plugins/custom-objects/custom-object-type-fields/, referencing the ID of the Custom Object Type:
{
"custom_object_type": 9,
"name": "internal_id",
"label": "Internal ID",
"type": "integer",
"required": true,
"validation_minimum": 0,
"validation_maximum": 9999
}Available type values are:
| Type | Description |
|---|---|
text | Short text field |
longtext | Long text field with textarea widget |
integer | Integer number |
decimal | Decimal number |
boolean | True/false |
date | Date |
datetime | Date and time |
url | URL |
json | JSON value |
select | Single selection from a choice set |
multiselect | Multiple selections from a choice set |
object | Reference to a single object |
multiobject | Reference to multiple objects of the same type |
color | RGB color as six lowercase hexadecimal digits, e.g. 00ff00 |
mac_address | EUI-48 MAC address; accepted in any common format, returned as 00:1A:2B:3C:4D:5E |
array | List of values (or of fixed-size tuples) of one element type |
coordinates | Latitude and longitude |
Common attributes available on all field types:
| Attribute | Required | Description |
|---|---|---|
name | yes | Internal field name. Must be lowercase alphanumeric with underscores only (e.g. rack_unit). Must be unique within the Custom Object Type. |
type | no | Field type (see table above). Default: text. |
label | no | Display name shown in the UI. Defaults to name with underscores replaced by spaces and the first letter capitalized. |
description | no | Help text shown in forms. |
group_name | no | Fields sharing the same group name are displayed together. |
required | no | Whether a value must be provided. Default: false. |
unique | no | Whether values must be unique across all objects of this type. Default: false. Not supported for boolean, multiobject or coordinates fields. |
primary | no | Whether this field's value is used as the object's display name. |
context | no | Whether this field's value is shown when the object is referenced by another object. |
default | no | Default value (must be a valid JSON value). |
weight | no | Display order weight. Higher values appear lower. Default: 100. |
search_weight | no | Search relevance weight. Lower values are more important; 0 disables search indexing. Default: 500. |
filter_logic | no | "loose" (substring match), "exact" (exact match), or "disabled". Default: "loose". |
ui_visible | no | "always", "if-set", or "hidden". Default: "always". |
ui_editable | no | "yes", "no" (read-only), or "hidden". Default: "yes". |
is_cloneable | no | Whether the field value is copied when cloning an object. Default: false. |
comments | no | Free-form notes about this field (supports Markdown). |
deprecated | no | Marks the field as deprecated. This is informational only: the field keeps working as before. It can't be set in the web UI. Default: false. |
deprecated_since | no | PEP 440 version string when the field was deprecated (e.g. "2.0.0"). |
scheduled_removal | no | PEP 440 version string when the field is planned for removal (e.g. "3.0.0"). |
schema_id | (read-only) | Stable, auto-assigned identifier used by the portable schema feature. See Portable Schema. |
Text Fields
Field types: text, longtext (and url, below)
{
"custom_object_type": 9,
"name": "hostname",
"label": "Hostname",
"type": "text",
"required": true,
"validation_regex": "^[a-zA-Z0-9-]+$"
}| Attribute | Description |
|---|---|
validation_regex | Regular expression enforced on field values. |
URL Fields
Field type: url
A URL field also accepts validation_regex. Each object stores a link title alongside the URL, which the API reads and writes as <name>_title, e.g. "docs": "https://wiki.example.com/server-001", "docs_title": "Runbook".
Coordinates Fields
Field type: coordinates
A coordinates field stores a latitude and a longitude. Each object reads and writes them as two values, <name>_latitude and <name>_longitude, e.g. "location_latitude": 40.7128, "location_longitude": -74.006. Set both or neither.
Numeric Fields
Field types: integer, decimal
{
"custom_object_type": 9,
"name": "cpu_cores",
"label": "CPU Cores",
"type": "integer",
"validation_minimum": 1,
"validation_maximum": 128
}| Attribute | Description |
|---|---|
validation_minimum | Minimum allowed value. |
validation_maximum | Maximum allowed value. |
Array Fields
Field type: array
{
"custom_object_type": 9,
"name": "waypoints",
"label": "Waypoints",
"type": "array",
"array_element_type": "integer",
"array_tuple_size": 3,
"array_min_length": 2
}| Attribute | Description |
|---|---|
array_element_type | text, integer, decimal, boolean, date or datetime. Required. Cannot be changed after creation. |
array_tuple_size | Store a list of tuples of this many values (at least 2); null for a list of single values. Cannot be changed after creation. |
array_min_length, array_max_length | Minimum and maximum number of items (at least 1 when set). |
validation_regex | For text elements: a regular expression every value must match. |
validation_minimum, validation_maximum | For integer and decimal elements: the allowed range of every value. |
An object's value is a JSON list, e.g. "waypoints": [[0, 0, 0], [10, 0, 5]].
Choice Fields
Field types: select, multiselect
{
"custom_object_type": 9,
"name": "environment",
"label": "Environment",
"type": "select",
"choice_set": 5
}| Attribute | Description |
|---|---|
choice_set | ID of a NetBox Custom Field Choice Set. Required. |
Object Reference Fields
Field types: object, multiobject
For non-polymorphic object or multiobject fields, specify the content type using app_label and model:
{
"custom_object_type": 9,
"name": "primary_device",
"label": "Primary Device",
"type": "object",
"app_label": "dcim",
"model": "device"
}{
"custom_object_type": 9,
"name": "device_list",
"label": "Device List",
"type": "multiobject",
"app_label": "dcim",
"model": "device"
}Additional attributes for object reference fields:
| Attribute | Description |
|---|---|
app_label | Django app label of the related model (write-only). Required for non-polymorphic object fields. |
model | Model name of the related model (write-only). Required alongside app_label. |
related_object_type | (read-only) Nested representation of the referenced object type. |
is_polymorphic | When true, the field accepts references to objects of multiple different types. Requires related_object_types_input instead of app_label/model. Cannot be changed after the field is created. Default: false. |
related_object_types_input | List of {"app_label": ..., "model": ...} dicts (write-only). Required for polymorphic fields. |
related_object_types | (read-only) The allowed object types for polymorphic fields, as a list of {"id": ..., "app_label": ..., "model": ...}. |
related_object_filter | JSON query_params dict used to filter the object selection drop-down. |
related_name | Reverse relation accessor name on the related object (e.g. ssl_profiles allows obj.ssl_profiles.all()). |
on_delete_behavior | Action when the referenced object is deleted: "set_null" (default), "cascade", or "protect". Applies only to object fields. Creating any other type of field with it returns 400 Bad Request. |
An object or multiobject field can reference any Custom Object Type as well as any core NetBox object. To reference another Custom Object Type, set app_label to "custom-objects" and model to the target Custom Object Type's slug. For example:
{
"custom_object_type": 9,
"name": "parent_circuit",
"type": "object",
"app_label": "custom-objects",
"model": "circuit"
}Polymorphic Object Reference Fields
A polymorphic object or multiobject field can reference objects of multiple different types. Set is_polymorphic: true and provide the allowed types via related_object_types_input:
{
"custom_object_type": 9,
"name": "linked_resource",
"label": "Linked Resource",
"type": "object",
"is_polymorphic": true,
"related_object_types_input": [
{"app_label": "dcim", "model": "device"},
{"app_label": "dcim", "model": "rack"},
{"app_label": "custom-objects", "model": "server"}
]
}The is_polymorphic flag and the set of allowed related_object_types cannot be changed after the field is created. To convert between a polymorphic and a non-polymorphic field, delete and recreate the field.
Custom Object Type Constraints
Require a combination of field values to be unique by creating a constraint with a POST request to /api/plugins/custom-objects/custom-object-type-constraints/. fields lists the IDs of the fields it covers:
{
"custom_object_type": 9,
"name": "vendor_serial",
"fields": [41, 42],
"case_insensitive": false,
"nulls_distinct": true
}| Attribute | Required | Description |
|---|---|---|
custom_object_type | yes | ID of the Custom Object Type. |
name | yes | Internal name. Lowercase alphanumeric with underscores only; unique within the Custom Object Type. |
type | no | unique (the default) or check. |
fields | for unique | IDs of the fields whose combined values must be unique: at least two, or one when case_insensitive is true or there's a condition. Omitted or [] for check. Responses list each field's id, url, name and schema_id. |
case_insensitive | no | For unique: compare text, longtext and url fields without regard to case. Default: false. |
nulls_distinct | no | For unique: when true, objects that leave any constrained field empty never conflict. Default: true. |
condition | no | Apply the constraint only to objects matching all of these predicates, e.g. [{"field": 43, "lookup": "isnull", "value": true}]. field is a field ID (responses show the brief field object); lookup is exact, n (does not equal, or is empty), in (value is a list), isnull (value is true or false), or gt, gte, lt or lte (integer, decimal, date and datetime fields). A value of {"field": <ID>} compares with another field of the same kind. Object fields can only be tested with isnull. Default: []. |
rule | for check | Predicates, in the same form as condition, that every object (matching the condition, if any) must satisfy. A comparison passes when a value it compares is empty. |
error_message | no | For check: the error returned when an object breaks the rule. Default: a description of the rule. |
description | no | A short description. |
A check constraint names its fields in its rule instead:
{
"custom_object_type": 9,
"name": "end_after_start",
"type": "check",
"rule": [{"field": 45, "lookup": "gt", "value": {"field": 44}}],
"error_message": "The end date must be after the start date."
}Creating or changing a constraint returns 400 Bad Request if existing objects already violate it. Writes to custom objects that would violate a constraint also return 400 Bad Request.
Custom Objects
Once a Custom Object Type's schema is defined, create Custom Objects (instances) with a POST to /api/plugins/custom-objects/<slug>/, where <slug> is the slug of the Custom Object Type:
{
"internal_id": 102,
"hostname": "server-001",
"cpu_cores": 8,
"environment": "production",
"device_list": [34, 1],
"primary_device": 16
}For select fields, pass the choice's value; for multiselect fields, a list of values. For non-polymorphic object and multiobject fields, pass the primary key (integer) of the referenced object - or a list of primary keys, for multiobject. For polymorphic fields, pass a dict identifying both the content type and the object (a list of them, for multiobject):
{
"linked_resource": {"app_label": "dcim", "model": "device", "object_id": 7}
}content_type_id may be used in place of app_label + model, and id may be used in place of object_id.
The response includes the created object with its assigned ID and standard metadata. Values are read back in a richer form than they are written:
selectvalues are returned as{"value": ..., "label": ...}, andmultiselectvalues as a list of them.objectandmultiobjectvalues are returned as brief representations of the referenced objects.- Polymorphic values are returned as
{"_content_type": "<app_label>.<model>", "id": ..., "display": ...}. To write one back, replace_content_typewithapp_labelandmodel, or withcontent_type_id.
{
"id": 15,
"url": "https://netbox/api/plugins/custom-objects/server/15/",
"display": "server-001",
"owner": null,
"created": "2026-01-15T10:30:00.358032Z",
"last_updated": "2026-01-15T10:30:00.382330Z",
"tags": [],
"cpu_cores": 8,
"device_list": [
{
"id": 34,
"url": "https://netbox/api/dcim/devices/34/",
"display": "dev2",
"name": "dev2",
"description": ""
},
{
"id": 1,
"url": "https://netbox/api/dcim/devices/1/",
"display": "dev3",
"name": "dev3",
"description": ""
}
],
"environment": {
"value": "production",
"label": "Production"
},
"hostname": "server-001",
"internal_id": 102,
"primary_device": {
"id": 16,
"url": "https://netbox/api/dcim/devices/16/",
"display": "dev1",
"name": "dev1",
"description": ""
}
}display is the object's display name, and owner is NetBox's object owner. With ?brief=true, objects are returned as just id, url and display. As with core endpoints, ?fields= and ?omit= select which fields are returned.
Config context
For a Custom Object Type with config context support enabled, each object also
includes local_context_data (writable) and config_context (read-only): the
rendered context, as a Device or VM has. See
Config Context for which source contexts apply.
Rendering costs extra queries, so leave it out with ?omit=config_context when
you don't need it.
If the type has a custom field named config_context, that field is returned
instead of the rendered context.
Custom Validation
NetBox's CUSTOM_VALIDATORS setting is supported for Custom Objects. Use netbox_custom_objects.<cot-slug> as the key, where <cot-slug> is the slug of the Custom Object Type:
# configuration.py
CUSTOM_VALIDATORS = {
"netbox_custom_objects.server": [
{
"hostname": {"min_length": 3, "max_length": 64},
"cpu_cores": {"min": 1},
}
]
}Validators are enforced on both API writes and UI form submissions. Any violation raises an HTTP 400 error (API) or a form validation error (UI).
The key must use the Custom Object Type slug (e.g. server), not the type's name or its internal model name.
Linked Objects
Any NetBox object - whether a core object such as Device or Site, or a Custom Object - can be referenced by one or more Custom Objects via object or multiobject fields. To retrieve all Custom Objects that link to a given object, query the linked-objects endpoint:
GET /api/plugins/custom-objects/linked-objects/?object_type=<object_type>&object_id=<object_id>Both query parameters are required:
| Parameter | Description |
|---|---|
object_type | Target model in app_label.model form, e.g. dcim.device. |
object_id | Primary key of the target object. |
Example response:
{
"count": 1,
"results": [
{
"custom_object_type": {
"id": 9,
"url": "https://netbox/api/plugins/custom-objects/custom-object-types/9/",
"name": "server",
"slug": "server",
"description": "Server inventory objects"
},
"field_name": "primary_device",
"object": {
"id": 15,
"url": "https://netbox/api/plugins/custom-objects/server/15/",
"display": "server-001",
...
}
}
]
}object is the full representation of the custom object, as returned by its own endpoint. Results aren't paginated. A missing or invalid parameter returns 400 Bad Request, and a target object you can't view returns 404 Not Found.
OpenAPI schema
The Custom Object endpoints appear in NetBox's OpenAPI schema (/api/schema/)
once, generically, with the Custom Object Type's slug as the custom_object_type
path parameter:
/api/plugins/custom-objects/{custom_object_type}//api/plugins/custom-objects/{custom_object_type}/{id}/
The CustomObject schema lists the fields every custom object has (id, url,
display, owner, tags, created, last_updated) and allows additional
properties, since each type's custom fields are defined at runtime. Clients
generated from the schema therefore treat custom fields as free-form properties.
The schema doesn't describe each Custom Object Type's own fields; use the
Custom Object Type Fields endpoint to discover them.
Browsable API
As with other NetBox objects, you can view the API output for Custom Objects in a browser by prepending /api/ to the URL - for example, /api/plugins/custom-objects/dhcp_scope/:
HTTP 200 OK
Allow: GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept
{
"count": 1,
"next": null,
"previous": null,
"results": [
{
"id": 1,
"url": "https://netbox/api/plugins/custom-objects/dhcp_scope/1/",
"display": "DHCP Scope 1",
"owner": null,
"created": "2026-01-15T10:30:00.358032Z",
"last_updated": "2026-01-15T10:30:00.358032Z",
"tags": [],
"range": {
"id": 1,
"url": "https://netbox/api/ipam/ip-ranges/1/",
"display": "192.168.0.1-100/24",
"family": {
"value": 4,
"label": "IPv4"
},
"start_address": "192.168.0.1/24",
"end_address": "192.168.0.100/24",
"description": ""
}
}
]
}Other Operations
GET, PUT, PATCH, and DELETE requests are supported on all of the above, using the detail URL for each object:
| Resource | Detail URL |
|---|---|
| Custom Object Type | /api/plugins/custom-objects/custom-object-types/<id>/ |
| Custom Object Type Field | /api/plugins/custom-objects/custom-object-type-fields/<id>/ |
| Custom Object Type Constraint | /api/plugins/custom-objects/custom-object-type-constraints/<id>/ |
| Custom Object | /api/plugins/custom-objects/<slug>/<id>/ |
Standard NetBox filter parameters (e.g. q=, tag=, created__gte=) work against the list endpoints. Each Custom Object Type's endpoint also has a filter for each of its fields; see Filtering Custom Objects.
The Custom Object Type and Custom Object Type Field list endpoints accept filters for their own attributes, for example ?slug=dhcp_scope or ?name=hostname. Fields can also be filtered by the type they belong to (?custom_object_type_id=9 or ?custom_object_type=dhcp_scope) and by field type (?type=integer).
Filtering Custom Objects
Each field of a Custom Object Type gets a filter on that type's endpoint, named after the field, unless the field's Filter logic is disabled. The select, multiselect, color, mac_address and multiobject filters can be repeated to match any of several values, e.g. ?environment=production&environment=staging; the others take a single value.
| Field type | Filter |
|---|---|
text, longtext, url | ?<field>= matches part of the value when filter logic is loose (the default), or the whole value when it's exact. Either way, the core NetBox lookup suffixes are available, e.g. ?<field>__isw= (starts with) or ?<field>__n= (does not equal). |
json | ?<field>= matches part of the JSON text. |
integer, decimal | ?<field>= matches the value exactly; suffixes such as __gte, __lt and __n are also available. |
date, datetime | ?<field>= matches the value exactly. |
boolean | ?<field>=true or false. |
select | ?<field>=<value> matches the choice value. |
multiselect | ?<field>=<value> matches objects whose selections include the value. |
color, mac_address | ?<field>= matches the value exactly. |
object | ?<field>=<id> or ?<field>_id=<id> matches the referenced object's ID. |
multiobject | ?<field>=<id> matches objects that reference that ID. |
polymorphic object / multiobject | One filter per allowed type, named <field>_<app_label>_<model>, e.g. ?linked_resource_dcim_device=7. |
array | ?<field>=a,b matches objects whose list includes every one of the comma-separated values; ?<field>__len= matches the number of items. |
coordinates | ?<field>_latitude= and ?<field>_longitude=. |
Bulk Operations
All four list endpoints (types, fields, constraints and each type's objects) accept bulk writes, the same as NetBox core endpoints. Send a list of objects, each identifying its target by id:
| Method | Body | Effect |
|---|---|---|
POST | [{...}, {...}] | Create several objects |
PATCH | [{"id": 1, "description": "new"}, ...] | Partially update each listed object |
PUT | [{"id": 1, ...all fields...}, ...] | Fully update each listed object |
DELETE | [{"id": 1}, {"id": 2}] | Delete each listed object |
Each request is applied atomically: if any listed object cannot be created, updated, or deleted, none of them are changed. Deleting a Custom Object Type drops its database table along with every object of that type.
Custom object endpoints don't support NetBox's ?background=true option for running bulk writes as a background job; requests that use it return 400 Bad Request.
Export Templates
A Custom Object list endpoint can render a NetBox export template instead of returning JSON, as core list endpoints do.
- Create an export template and add the Custom Object Type to its Object types.
- Request the type's list endpoint with
?export=set to the export template's name.
For example, with a Custom Object Type whose slug is dhcp_scope and an export template named Scopes CSV:
GET /api/plugins/custom-objects/dhcp_scope/?export=Scopes%20CSV&status=activeThe template's queryset contains every object that matches the request's filters (here status=active) and that you have permission to view. Exports aren't paginated, so limit and offset have no effect. The response uses the template's MIME type and file settings.
The response is a 404 if no export template with that name is assigned to this Custom Object Type, or, on NetBox 4.6.1 and later, if you don't have permission to view the template.
Releases
Custom Object Types now support unique and check constraints, with optional conditions. Unique constraints support case-insensitive matching and can treat em...
Troubleshooting
If your problem isn't covered here, check the existing issues or start a discussion. If you are a NetBox Labs customer, you can also contact support.