Using the OPNsense API with Python

The OPNsense firewall exposes a REST API for automation and integration. This post shows how to use the API from Python to retrieve core information over HTTPS, using an ACME/Let’s Encrypt certificate.


What you’ll do

  • Create a dedicated API user
  • Call core endpoints: system status, firmware status, interface statistics
  • Use Python requests with HTTP Basic authentication over TLS

Prerequisites

  • OPNsense GUI reachable at https://opnsense.maksonlee.com:8444 (adjust to your host/port)
  • Valid ACME/Let’s Encrypt certificate
  • API key/secret from System → Access → Users (least-privilege permissions)

Create the API user (add privileges here, then generate the key)

  • Go to System → Access → Users and click +.
    • Username: e.g., api-automation
    • Fill the required fields.
    • Effective Privileges: open the picker now (while still on the create form) and add exactly:
      • System: Status
      • System: Firmware
      • Diagnostics: Netstat
    • Click Save.
  • Back on the Users list, click the key icon next to your new user (tooltip: Create and download API key for this user).
    • Copy/download the API key and API secret (these are the Basic Auth username/password for the API). Keep them safe.

Why these privileges?
They unlock the endpoints used below:

  • GET /api/core/system/status
  • GET /api/core/firmware/status
  • GET /api/diagnostics/interface/get_interface_statistics

Python example

Save as opnsense_api_example.py:

import json
import requests
from requests.auth import HTTPBasicAuth

BASE = "https://opnsense.maksonlee.com:8444"
KEY  = "YOUR_API_KEY_HERE"
SEC  = "YOUR_API_SECRET_HERE"

session = requests.Session()
session.auth = HTTPBasicAuth(KEY, SEC)
session.headers.update({"Accept": "application/json"})

# 1) System status
resp = session.get(f"{BASE}/api/core/system/status", timeout=15, verify=True)
resp.raise_for_status()
print("[system/status]")
print(json.dumps(resp.json(), indent=2))

# 2) Firmware status
resp = session.get(f"{BASE}/api/core/firmware/status", timeout=15, verify=True)
resp.raise_for_status()
print("\n[firmware/status]")
print(json.dumps(resp.json(), indent=2))

# 3) Interface statistics (note the snake_case endpoint)
resp = session.get(f"{BASE}/api/diagnostics/interface/get_interface_statistics", timeout=15, verify=True)
resp.raise_for_status()
iface_stats = resp.json()
print("\n[diagnostics/interface/get_interface_statistics]")
print(json.dumps(iface_stats, indent=2))

# Optional: quick RX/TX summary if present
rows = iface_stats.get("rows") or iface_stats.get("interfaces") or []
if isinstance(rows, list) and rows:
    print("\n[summary]")
    for it in rows:
        name = it.get("name") or it.get("interface") or "?"
        rx = it.get("ibytes") or it.get("rxbytes") or it.get("rx") or "?"
        tx = it.get("obytes") or it.get("txbytes") or it.get("tx") or "?"
        print(f"{name}: RX={rx} TX={tx}")

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top