guides

SNMP Polling with Python (pysnmp): A Practical Guide

Poll SNMP from Python with pysnmp — get a single OID, walk a table with getbulk, handle SNMPv3 with USM parameters, and parse results into usable metrics.

By the SNMP Monitoring team · Reviewed July 2026

Reach for Python when the CLI stops being enough. A quick snmpget or snmpwalk is perfect for spot checks and shell scripts, but the moment you need to poll a fleet, reshape results into your own data structures, feed a database, or wire SNMP into an existing application, a real programming language earns its keep. pysnmp is the common pure-Python SNMP library, and it speaks v1, v2c, and v3 with the same get/getnext/getbulk operations you already know from Net-SNMP. This guide walks a single get, a table walk, v3, and parsing results into usable metrics. One caveat up front, and it matters: pysnmp's API has shifted across versions and forks, so treat the code here as illustrative of the shape and match the exact imports to the pysnmp you actually installed.

Parent: guides. Related: Net-SNMP, snmpget, disk sensor, PDU structure.

Installing pysnmp

Install it from PyPI into a virtual environment:

pip install pysnmp

That's the whole install. The important note is version sensitivity: different pysnmp releases and community forks have used different import paths and moved between a synchronous and an asyncio-based API. So before you copy any example — including the ones below — check which version pip gave you and consult that version's own documentation for the exact module names. The concepts (a get, a walk, USM parameters for v3) are stable; the import lines are the part that drifts.

Get a single OID

The high-level command generators mirror the CLI tools one-to-one — a "get command" is the programmatic snmpget. Conceptually you assemble four things, then read back the variable bindings:

  1. An SNMP engine — the object that drives the protocol machinery.
  2. Credentials — a community for v2c (or USM parameters for v3).
  3. A transport target — the device address and UDP port.
  4. One or more OIDs — wrapped as object types to query.

In code that looks like this:

from pysnmp.hlapi import *

iterator = getCmd(
    SnmpEngine(),
    CommunityData("S3cr3tR0str1ng", mpModel=1),   # mpModel=1 => v2c
    UdpTransportTarget(("10.0.0.20", 161)),
    ContextData(),
    ObjectType(ObjectIdentity("1.3.6.1.2.1.1.3.0")),   # sysUpTime
)

errorIndication, errorStatus, errorIndex, varBinds = next(iterator)
for name, val in varBinds:
    print(f"{name} = {val}")

Running it prints the bound value, just like the CLI would:

SNMPv2-MIB::sysUpTime.0 = 18342200

Note the three result channels: errorIndication (a transport/engine problem — a timeout, an unreachable host), errorStatus (an SNMP-level error the agent returned, like noSuchName), and the varBinds themselves. Check the first two before trusting the third. mpModel=0 selects v1, mpModel=1 selects v2c.

Walk a table

To read a table — interfaces, the storage table — you iterate the getnext (or getbulk) generator, which keeps returning rows until it walks off the end of the subtree. bulkCmd is the getbulk form and is far more efficient over the wire for large tables:

for (errInd, errStat, errIdx, varBinds) in bulkCmd(
    SnmpEngine(),
    CommunityData("S3cr3tR0str1ng", mpModel=1),
    UdpTransportTarget(("10.0.0.20", 161)),
    ContextData(),
    0, 25,   # non-repeaters, max-repetitions
    ObjectType(ObjectIdentity("1.3.6.1.2.1.25.2.3.1")),   # hrStorageEntry
    lexicographicMode=False,
):
    if errInd or errStat:
        break
    for name, val in varBinds:
        print(f"{name} = {val}")

lexicographicMode=False stops the walk at the end of the requested subtree instead of running to the end of the MIB. This is the pattern behind reading every interface's counters (interface sensor) or every volume's usage (disk sensor).

SNMPv3

For v3 you swap the community for USM (User-based Security Model) parameters — the same user, auth, and privacy settings you'd pass to snmpget -v3. In pysnmp that's a UsmUserData object carrying:

  • Username — the v3 security name.
  • Authentication — the protocol (e.g. SHA) and the auth passphrase/key.
  • Privacy — the encryption protocol (e.g. AES) and the privacy passphrase/key.
  • Security level — implied by which of the above you supply: user only (noAuthNoPriv), user + auth (authNoPriv), or user + auth + priv (authPriv).

You pass that UsmUserData where the CommunityData went, and everything else — the get, the walk, the parsing — stays the same. The auth/privacy protocol constants are among the imports that vary by pysnmp version, so confirm the exact names for your install. The security-level model itself is standard and covered on SNMPv3 setup.

Parsing and using results

Printing varbinds is the demo; real code turns them into structured data. Each varbind is an object-name / value pair, and the value is a typed SNMP object (an Integer, Counter64, OctetString), so cast it before doing math — int(val) for a counter, str(val) for a description. A common pattern is to collect a table walk into a dict keyed by index, then compute derived metrics. For disk usage you'd read hrStorageUsed and hrStorageSize at the same index and calculate used ÷ size × 100, exactly as the CLI approach does — the difference is you now have the number in a variable you can threshold, store, or emit. For counters like interface octets, remember to compute a rate across two polls rather than reporting the raw ever-climbing total. Understanding that varbinds come back inside a PDU helps here — see PDU structure.

Pitfalls

A few things reliably bite people writing pysnmp code:

PitfallSymptomFix
API / version driftImportError on hlapi pathsMatch imports to your installed pysnmp version/fork
TimeoutserrorIndication set, no dataCheck reachability, community/v3, firewall; raise timeout/retries
Type handlingString math or wrong valuesCast varbind values (int()/str()) before use
Counter totalsNonsensical "traffic" numbersCompute rate across two polls, not the raw counter
Walking too farRows from unrelated MIBsSet lexicographicMode=False to bound the subtree

The first row is the big one — most "my pysnmp example broke" reports are an import path or sync/async change between versions, not a logic error. Pin your pysnmp version in requirements.txt so an upgrade doesn't silently rewrite the API under you.

Frequently asked questions

How do I poll SNMP with Python?

Use the pysnmp library. Install it with pip install pysnmp, then use its high-level command generators — a get command for a single OID, a getnext or getbulk command to walk a table. You supply the credentials (a community for v2c or USM parameters for v3), a UDP transport target, and the OID, then read the returned variable bindings, checking for transport and SNMP errors first. It mirrors the Net-SNMP CLI tools programmatically.

Does pysnmp support SNMPv3?

Yes. pysnmp supports v1, v2c, and v3. For v3 you provide USM parameters — a username, an authentication protocol and key, and a privacy (encryption) protocol and key — instead of a community string. Which of those you supply sets the security level (noAuthNoPriv, authNoPriv, or authPriv). The exact protocol constant names vary by pysnmp version, so check your installed version's docs.

How do I walk a table in pysnmp?

Iterate the getnext or getbulk command generator over the table's base OID (for example the storage or interface table entry). Each iteration yields a batch of variable bindings for the next rows; keep going until the walk leaves the subtree. Set lexicographicMode=False so it stops at the end of your requested subtree rather than continuing to the end of the MIB. getbulk is more efficient for large tables.

Why did my pysnmp code break after an update?

Almost always an API change. pysnmp has shifted import paths and moved between synchronous and asyncio-based interfaces across versions and forks, so code written for one release can fail to import on another. Pin the version in your requirements file, and when you do upgrade, re-check the import paths and the sync-vs-async style against that version's documentation rather than assuming the old code still applies.

Key takeaways

  • Script SNMP in Python when the CLI isn't enough — fleets, custom data shapes, database feeds, app integration.
  • pysnmp (pip install pysnmp) speaks v1/v2c/v3 with get/getnext/getbulk command generators mirroring the CLI.
  • Treat example imports as version-sensitive — pysnmp's API drifts across versions/forks; match your install and pin the version.
  • Check errorIndication and errorStatus before the varbinds; cast typed values before math; compute rate for counters.
  • Use lexicographicMode=False to bound a table walk to its subtree.
  • For an independent off-box path beyond your own scripts, see external monitoring with ostr.io.

Monitor it from outside the network

SNMP only tells you a box is healthy while something is still asking. ostr.io polls your endpoints externally and fires email + SMS alerts the moment a reading crosses your line — or the agent goes silent.