miniupnpc

raw JSON →
2.3.3 verified Fri May 01 auth: no python

Python bindings for the MiniUPnP library, used to control UPnP-enabled Internet Gateway Devices (IGD) for port forwarding and NAT traversal. Version 2.3.3 is the latest release; updates are infrequent (months to years between releases).

pip install miniupnpc
error AttributeError: module 'miniupnpc' has no attribute 'UPnP'
cause Trying to import with `import miniupnpc` and then using `miniupnpc.UPnP()` without checking the import path. In some distributions (e.g., Debian), the package provides `miniupnpc.UPnP` correctly, but on some wheels the class is exposed only if imported as `from miniupnpc import UPnP`.
fix
Use from miniupnpc import UPnP instead of importing the module and accessing the attribute.
error ImportError: No module named miniupnpc
cause The miniupnpc library is not installed, or you are using a virtual environment without the package.
fix
Install the library via pip install miniupnpc (or python -m pip install miniupnpc if pip is not in PATH).
error TypeError: string indices must be integers
cause Calling a method that expects an integer index with a string, e.g., `upnp.getgenericportmapping('0')` instead of `upnp.getgenericportmapping(0)`. This often happens when iterating over discovered devices or ports.
fix
Ensure indices are integers, not strings. For example, use for i in range(upnp.getportmappingnumberofentries()): then upnp.getgenericportmapping(i).
error URLError: <urlopen error [Errno 101] Network is unreachable>
cause The UPnP device (router) is not reachable, or the machine is on a network without UPnP support (e.g., some public WiFi or VPN).
fix
Verify you are on a network with a UPnP-enabled router. Try running the upnpc command-line tool from the miniupnpc package to confirm discovery works.
breaking In version 2.x, `addportmapping()` requires the remote host argument as an empty string (not None). Passing None may cause a TypeError.
fix Use `upnp.addportmapping(port, 'TCP', internal_ip, internal_port, desc, '')` — last argument must be a string.
deprecated The `UPnP.getstatus()` method (available in older 1.x) is removed in 2.x. Use `UPnP.getspecificportmappingentry()` or iterate mappings.
fix Use `upnp.getgenericportmapping(index)` for numbered mappings or `upnp.getspecificportmappingentry(external_port, protocol)` for a specific rule.
gotcha `UPnP.discover()` returns the number of devices found, but does not throw on zero devices. Always check the return value to avoid later errors.
fix After `discover()`, check `if num > 0:` before calling `selectigd()`, otherwise it may raise a runtime error or silently fail.
gotcha Port mappings added via `addportmapping()` may persist on the router even after your program exits. Always clean up if you don't want permanent rules.
fix Call `deleteportmapping()` or use a `try/finally` block to remove mappings when done.
gotcha Some routers reject mappings from IPs not on the same subnet, or have maximum mapping limits. The library does not validate this, so you may get silent failures.
fix Check the return code of `addportmapping()` (0 = success, non-zero = failure) and log it. Use diagnostic tools like `upnpc -a` from the command line if available.
breaking Python 2 support was dropped in version 2.0. Python 3.4+ (now 3.5+) is required.
fix Upgrade to Python 3.5+.
deprecated The `addportmapping` method's `desc` argument was previously optional; now it is required (type: str). Passing no description will cause a TypeError.
fix Always provide a description string, even if empty. Use `upnp.addportmapping(port, proto, int_ip, int_port, 'mapping')`.

Discover UPnP device, get external IP, add and remove a TCP port mapping.

from miniupnpc import UPnP
import os

upnp = UPnP()
upnp.discoverdelay = 200
upnp.discover()
upnp.selectigd()

print(f"External IP: {upnp.externalipaddress()}")
print(f"Internal IP: {upnp.lanaddr}")

# Add a port mapping
upnp.addportmapping(
    8888,      # external port
    'TCP',
    upnp.lanaddr,
    8888,      # internal port
    'test mapping',
    ''         # remote host (empty = any)
)
print('Port mapping added')

# Remove mapping
upnp.deleteportmapping(8888, 'TCP')
print('Port mapping removed')