Library / SDK
netbox-community/pynetbox avatar
netbox-community/pynetbox

pynetbox and the case for treating NetBox as an object graph

Python API client library for Netbox.

687 stars193 forksPythonApache-2.0

At a glance

What is it?
pynetbox maps NetBox's applications onto attributes and its endpoints onto methods, so a query reads like the NetBox data model, and it adds the two things a plain requests script gets wrong: pagination and silent filter failures. Its sharp edge is the filter validation feature, which the NetBox API itself refuses to do.
Who is it for?
pynetbox suits a team automating NetBox from Python, because the app and endpoint attribute model makes scripts read like the data model and the filter validation catches the failure mode where a mistyped filter silently returns an entire table.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 2 days ago.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Applications as attributes, endpoints as methods

pynetbox is a Python API client library for NetBox, and the design decision that defines it is that NetBox's applications become attributes of the api object, and in turn those applications have an attribute for each endpoint. Each endpoint carries a handful of methods for acting on it.

The whole thing starts with an import and an instantiation:

code
import pynetbox
nb = pynetbox.api(
    'http://localhost:8000',
    token='d6f4e314a5b5fefd164995169f28ae32d987704f'
)

The first argument is the NetBox URL. The named arguments are optional in most cases, with the token being the one you need in order to write rather than only read.

The payoff of the attribute model is that a query is spelled the way NetBox thinks. To get everything in the devices endpoint:

code
>>> devices = nb.dcim.devices.all()
>>> for device in devices:
...     print(device.name)
...
test1-leaf1
test1-leaf2
test1-leaf3
>>>

The dcim is the NetBox application, devices is the endpoint, all() is the method, and each returned object carries the fields of the device. There is no endpoint URL to construct and no response dictionary to unwrap, which is the difference between a client library and a wrapper around requests.

The full API is documented on GitHub Pages, and the project ships an mkdocs setup with a docs/ directory, so the reference is generated rather than hand written.

The all() and filter() methods return generators, and that is a trap

There is one behaviour in the library that will cost you an afternoon if you do not know it in advance, and the documentation is explicit about it.

The all() and filter() methods are generators and return an object that can be iterated over only once. If you are going to iterate over it repeatedly you need to either call the all() method again, or encapsulate the results in a list object:

code
>>> devices = list(nb.dcim.devices.all())

The reason is pagination. NetBox returns results in pages, and a generator is the natural way to stream them without holding the whole table in memory, which matters when you are pulling every device in a large site. The cost is that the second pass over the same generator yields nothing, and a script that filters a list and then prints it can appear to work while silently printing nothing.

The fix is a one-word change, and the documentation puts it right next to the example. A script that wants to iterate twice converts to a list once, and pays the memory once.

This is a good example of a client library optimising for the large case at the cost of a slightly surprising small case, and the documentation at least tells you so.

Threading needs MAX_PAGE_SIZE set to something other than zero

pynetbox supports multithreaded calls for filter() and all() queries, and enabling it is a single argument:

python
nb = pynetbox.api(
    'http://localhost:8000',
    threading=True,
)

The catch is on the server side, and the documentation is very clear that it is highly recommended you have MAX_PAGE_SIZE in your NetBox install set to anything except 0 or None, with the default value of 1000 usually a good value to use.

The reasoning is about what the server does with the request. A maximum page size of zero or none means the server will return the whole result set in one response, which is a single request and a single large payload. Threading helps when the work is many small requests, because the client can have several in flight at once. Setting the page size to a sane number makes the server return bounded pages, and the client can then fetch them in parallel. Without that, the threading buys nothing because there is only one request to overlap.

So the feature has a server-side precondition, which is a slightly unusual coupling for a client library but an honest one, since the alternative would be the client silently issuing requests the server answers in one lump.

Filter validation against the OpenAPI spec, because NetBox will not do it

This is the feature that most clearly justifies using a client library here rather than writing requests calls.

NetBox does not validate filters passed to the GET API endpoints, which are accessed with get() and filter(). If a filter is incorrect, NetBox silently returns the entire database table content.

That is a genuinely dangerous failure mode. A script that filters for devices at a particular site and gets the whole device table back will write that into a report, or compare it against an inventory, and nothing will raise. The script succeeds and the output is wrong.

Pynetbox allows checking the provided parameters against the NetBox OpenAPI specification before making the call and raising an exception if a parameter is incorrect. Globally, by setting strict_filters=True when initialising the api object:

python
nb = pynetbox.api(
    'http://localhost:8000',
    strict_filters=True,
)

And per request, in both directions:

python
# Disable for one request when enabled globally.
# Will not raise an exception and return the entire Device table.
nb.dcim.devices.filter(non_existing_filter="aaaa", strict_filters=False)

# Enable for one request when not enabled globally.
# Will raise an exception.
nb.dcim.devices.filter(non_existing_filter="aaaa", strict_filters=True)

The fact that the override exists per request tells you the authors know strict mode is not universally right, and the comment on the first case spells out the consequence of turning it off, which is that the entire Device table comes back.

Version pairing, and why it is not automatic

The compatibility table in the project documentation is the other thing to read before installing, and it is a table of paired versions rather than a range.

Each pynetbox version listed has been tested with its corresponding NetBox version. The current row pairs NetBox 4.7 with pynetbox 7.9.0, 4.6 with 7.8.0 and 7.7.0, 4.5 with 7.6.1 and 7.6.0, and 4.4, 4.3, 4.2 and 4.1 all with 7.5.0. Older rows go back to NetBox 3.3 paired with 7.0.0.

There is also a floor note: version 6.7 and later of the library only supports NetBox 3.3 and above.

So a client version is not a floating library, it is a specific pairing with a specific server version, and the pairing is validated by the maintainers rather than inferred. A team that upgrades NetBox has to know which client version matches, and a team that writes automation against a NetBox install has to pin the client that was tested with it.

The release history follows the same pattern from the other side. The tags are v7.7.0 from 2026-05-05, v7.8.0 from 2026-06-18 and v7.9.0 from 2026-10-08, and the last push to the repository was on 2026-10-09.

The package metadata is consistent with a project that has been through tooling changes. The build uses setuptools with setuptools_scm for a dynamic version, requires Python 3.12 or newer, and the classifiers cover Python 3.12, 3.13 and 3.14. The dependencies are deliberately few, packaging plus requests and urllib3, with a comment explaining that urllib3 is not imported directly but is constrained as a transitive dependency of requests.

Running the tests, and what the integration suite costs

The test instructions are worth reading because the split between unit and integration tests reflects how the library is validated.

You create and activate a virtual environment in the project directory to isolate the dependencies:

bash
python3 -m venv venv
source venv/bin/activate

Then install with the development extras:

bash
pip install -e ".[dev]"

The unit tests mock every HTTP response and need no external services:

bash
pytest tests --ignore=tests/integration

The integration tests run against real NetBox instances, they require Docker to be installed and running, and they download and launch netbox-docker containers during the run:

bash
pytest tests/integration --netbox-versions 4.6

That last detail is the most useful thing in the section. The integration suite pins specific NetBox versions, which is the practical form of the compatibility table, and it means a change to the library is checked against actual server versions rather than against a mock that might not match the API behaviour.

Before opening a pull request the guide says to run the lint gate the same way CI does:

bash
pre-commit run --all-files

The repository carries a .pre-commit-config.yaml and the top level also holds a CHANGELOG.md, a LICENSE, an AGENTS.md and a CLAUDE.md, which is the usual shape for a project that has been worked on by coding agents as well as by people.

Editorial conclusion

pynetbox suits a team automating NetBox from Python, because the app and endpoint attribute model makes scripts read like the data model and the filter validation catches the failure mode where a mistyped filter silently returns an entire table. It does not suit a project on a NetBox version below 3.3, and it does not suit anyone who wants the library to keep up with every upstream release automatically, because the compatibility table ties a client version to a server version. Before you pin a version, check the compatibility table for your NetBox release, set MAX_PAGE_SIZE to something other than zero if you intend to use threading, and note that the last push was on 2026-10-09 with v7.9.0 published on 2026-10-08.

Frequently asked questions

How do I install pynetbox?

Run pip install pynetbox, or clone the repository and run pip install . from the project directory. The package requires Python 3.12 or newer and depends on packaging plus requests and urllib3.

Why do my repeated iterations over an all() query return nothing?

Because the all() and filter() methods are generators and the returned object can be iterated over only once. If you need to iterate repeatedly, either call all() again or wrap the results in a list, for example list(nb.dcim.devices.all()).

What does strict_filters do in pynetbox?

NetBox does not validate filters on its GET endpoints and silently returns the whole table if a filter is wrong. strict_filters makes pynetbox check the parameters against the NetBox OpenAPI specification before the call and raise an exception if one is incorrect. It can be set globally on the api object or per request.

Which pynetbox version should I use for my NetBox install?

Each client version is tested with a corresponding server version. The table pairs NetBox 4.7 with pynetbox 7.9.0, 4.6 with 7.8.0 and 7.7.0, 4.5 with 7.6.1 and 7.6.0, and 4.4 down to 4.1 with 7.5.0. Version 6.7 and later of the library only supports NetBox 3.3 and above.

What do I need before using threaded queries in pynetbox?

MAX_PAGE_SIZE in your NetBox install has to be set to anything other than 0 or None, and 1000 is usually a good value. Then pass threading=True to the api constructor. Without a bounded page size the server returns everything in one response and there is nothing for the threads to overlap.

Official sources

  1. Issues
  2. License: Apache-2.0
  3. netbox-community/pynetbox on GitHub
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/netbox-community-pynetbox.svg)](https://hysenlabs.com/projects/netbox-community-pynetbox)

Was this useful?