Remote Support Start download

TrueNAS API with Python: Automating Custom Reports

TrueNASPythonAutomationAPI
TrueNAS API with Python: Automating Custom Reports

TrueNAS has offered a comprehensive API for years. What used to be WebSocket-only is, since SCALE, also reachable via classic REST — and stably documented from the 24.x releases on. Anyone who wants to automate reports, pull monitoring data or script lifecycle operations no longer has to click through the WebUI.

This article shows concretely what can be done with the API: generate an API key, pull three typical reports (pool usage, snapshot age, SMART status) and a complete script for an 80% pool alert via email. For an introduction to TrueNAS API automation, this is the practical companion.

WebSocket or REST? When What?

TrueNAS SCALE offers two API flavors:

APIEndpointProperty
WebSocket (JSON-RPC)wss://truenas/websocketTrue real-time, event-based, full feature surface
RESThttps://truenas/api/v2.0/Stateless, easy entry, good for scripts

For scripted reports REST is usually the better choice: faster start, no session handling, classic HTTP libraries. For real-time events (e.g. a dashboard reacting to snapshot creations) WebSocket is right. This article uses REST because it is shorter and more robust for the typical reporting use case.

Generate an API Key

In TrueNAS under Credentials -> API Keys -> Add:

  • Name: e.g. python-reports
  • Reset Token: for later needs
  • Access: restrict to a few endpoints if possible

The API key is shown once and should immediately be copied into your secret manager — afterwards it cannot be read back.

Best practice:

  • One API key per script / service, with a descriptive name
  • Rotate keys regularly (annually)
  • Never commit keys in code — use .env, vault or environment variables
  • Revoke immediately on loss (same UI)

Hello World: Query Pool Usage

The simplest query — how full is the pool?

import os
import requests

TRUENAS_HOST = "https://truenas.example.com"
API_KEY = os.environ["TRUENAS_API_KEY"]
HEADERS = {"Authorization": f"Bearer {API_KEY}"}

# requests.get without verify=False — TLS cert should be valid
resp = requests.get(f"{TRUENAS_HOST}/api/v2.0/pool", headers=HEADERS, timeout=30)
resp.raise_for_status()

for pool in resp.json():
    used = pool["used"]["parsed"]      # bytes
    total = pool["size"]["parsed"]     # bytes
    pct = used / total * 100
    print(f"Pool {pool['name']}: {pct:.1f}% used ({used / 1e12:.2f} TB / {total / 1e12:.2f} TB)")

Example output:

Pool tank: 67.4% used (24.31 TB / 36.07 TB)
Pool fastpool: 41.2% used (3.30 TB / 8.00 TB)

Example 1: Snapshot Age per Dataset

Snapshots are useful as long as they rotate automatically — but when a snapshot task hangs, snapshots accumulate or the latest snapshots get too old. The following script reports the oldest snapshots per dataset:

import os
import requests
from datetime import datetime, timezone

TRUENAS_HOST = "https://truenas.example.com"
HEADERS = {"Authorization": f"Bearer {os.environ['TRUENAS_API_KEY']}"}

resp = requests.get(
    f"{TRUENAS_HOST}/api/v2.0/zfs/snapshot",
    headers=HEADERS,
    params={"limit": 0},
    timeout=60,
)
resp.raise_for_status()
snapshots = resp.json()

# group by dataset and find the latest snapshot per dataset
latest = {}
for snap in snapshots:
    ds = snap["dataset"]
    ts = snap["properties"]["creation"]["parsed"]   # unix timestamp
    if ds not in latest or ts > latest[ds]:
        latest[ds] = ts

now = datetime.now(tz=timezone.utc).timestamp()
print(f"{'Dataset':40} {'Last snap':25} {'Age':10}")
for ds, ts in sorted(latest.items()):
    dt = datetime.fromtimestamp(ts, tz=timezone.utc)
    age_h = (now - ts) / 3600
    flag = " <-- old!" if age_h > 48 else ""
    print(f"{ds:40} {dt.isoformat():25} {age_h:6.1f} h{flag}")

Run this script daily via cron and pipe the output into your monitoring or mail, and you have a simple snapshot health monitor.

Example 2: SMART Status of All Disks

Disk defects often show up in SMART values days in advance. The API returns SMART status per disk:

import os
import requests

TRUENAS_HOST = "https://truenas.example.com"
HEADERS = {"Authorization": f"Bearer {os.environ['TRUENAS_API_KEY']}"}

resp = requests.get(f"{TRUENAS_HOST}/api/v2.0/disk", headers=HEADERS, timeout=30)
resp.raise_for_status()

for disk in resp.json():
    name = disk["name"]
    model = disk.get("model", "")
    serial = disk.get("serial", "")
    # SMART status via a separate endpoint
    smart = requests.get(
        f"{TRUENAS_HOST}/api/v2.0/smart/test/results",
        headers=HEADERS,
        params={"disk": name},
        timeout=30,
    ).json()
    last = smart[0] if smart else None
    last_status = last["status"] if last else "no-test"
    flag = " <-- FAIL" if last and last["status"] != "SUCCESS" else ""
    print(f"{name:8} {model[:20]:20} SN={serial[:14]:14} last={last_status:10}{flag}")

The script lists every disk plus the last SMART test result. Disks without a test in the last 90 days or with a fail status are first candidates for a closer look — happily complemented with a local smartctl -a on TrueNAS.

Complete Script: 80% Pool Alert via Email

The practical example: check daily whether a pool exceeds 80% usage and, if so, send a mail to IT.

#!/usr/bin/env python3
"""TrueNAS pool usage alert.

Reads pool usage via the TrueNAS REST API and sends a mail if a pool exceeds
the threshold.
"""
import os
import smtplib
import sys
from email.mime.text import MIMEText

import requests

TRUENAS_HOST = os.environ["TRUENAS_HOST"]
API_KEY = os.environ["TRUENAS_API_KEY"]
THRESHOLD_PCT = float(os.environ.get("THRESHOLD_PCT", "80"))

SMTP_HOST = os.environ["SMTP_HOST"]
SMTP_PORT = int(os.environ.get("SMTP_PORT", "587"))
SMTP_USER = os.environ["SMTP_USER"]
SMTP_PASS = os.environ["SMTP_PASS"]
MAIL_FROM = os.environ["MAIL_FROM"]
MAIL_TO = os.environ["MAIL_TO"]

HEADERS = {"Authorization": f"Bearer {API_KEY}"}


def get_pools():
    resp = requests.get(
        f"{TRUENAS_HOST}/api/v2.0/pool",
        headers=HEADERS,
        timeout=30,
    )
    resp.raise_for_status()
    return resp.json()


def send_alert(subject, body):
    msg = MIMEText(body)
    msg["Subject"] = subject
    msg["From"] = MAIL_FROM
    msg["To"] = MAIL_TO
    with smtplib.SMTP(SMTP_HOST, SMTP_PORT) as smtp:
        smtp.starttls()
        smtp.login(SMTP_USER, SMTP_PASS)
        smtp.send_message(msg)


def main():
    pools = get_pools()
    alerts = []
    for pool in pools:
        used = pool["used"]["parsed"]
        total = pool["size"]["parsed"]
        pct = used / total * 100
        line = f"Pool {pool['name']}: {pct:.1f}% used ({used / 1e12:.2f} TB / {total / 1e12:.2f} TB)"
        print(line)
        if pct >= THRESHOLD_PCT:
            alerts.append(line)

    if alerts:
        body = "\n".join([
            f"Pool usage exceeds {THRESHOLD_PCT}% on {TRUENAS_HOST}:",
            "",
            *alerts,
            "",
            "Please check: TrueNAS WebUI -> Storage -> Pools.",
        ])
        send_alert(f"[TrueNAS] Pool usage >= {THRESHOLD_PCT}%", body)
        sys.exit(1)


if __name__ == "__main__":
    main()

Cron invocation:

# once daily at 07:00
0 7 * * * /usr/bin/env -i \
  TRUENAS_HOST=https://truenas.example.com \
  TRUENAS_API_KEY=$(cat /etc/truenas-api.key) \
  THRESHOLD_PCT=80 \
  SMTP_HOST=mail.example.com \
  SMTP_PORT=587 \
  SMTP_USER=alerts@example.com \
  SMTP_PASS=$(cat /etc/smtp.pass) \
  MAIL_FROM=alerts@example.com \
  MAIL_TO=it@example.com \
  /usr/bin/python3 /opt/truenas/pool-alert.py

In practice, the script should not sit directly in cron but be called from a wrapper that produces clean systemd journal logs. A systemd timer is cleaner than a pure cron job.

More Useful API Endpoints

EndpointUse case
/api/v2.0/replicationreplication status of all tasks
/api/v2.0/cloudsync/credentialscloud credentials for audit
/api/v2.0/system/infoTrueNAS version, uptime, hostname
/api/v2.0/alert/listcurrent alerts (WebUI bell)
/api/v2.0/reporting/get_dataperformance metrics (CPU, IO, network)
/api/v2.0/useruser management
/api/v2.0/sharing/smblist SMB shares

The full endpoint list lives in TrueNAS under System Settings -> API Keys -> API Docs — TrueNAS documents the API directly in the UI.

Security and Operations Tips

  • Valid TLS cert required — no verify=False in production scripts
  • Respect rate limits: batch queries instead of polling each disk individually
  • Minimize API key scope: better three keys for three purposes than one master key
  • Logs: every API action is logged on the TrueNAS side — use that audit trail
  • Error handling: handle timeouts, 401 (revoked key), 503 (TrueNAS reboot) cleanly
  • Idempotence: build scripts so that re-running causes no harm (especially POST/PUT)

When Own Scripts vs. Off-the-Shelf Tools?

ScenarioRecommendation
Standard monitoring (CPU, disk, memory)classic tools like Zabbix
TrueNAS-specific metricsAPI scripts, no Zabbix plugin required
Compliance reports / audit exportsAPI scripts with a defined output format
Bulk configurationAPI scripts, idempotent, in Git
One-off operationskeep using the TrueNAS WebUI

Conclusion

The TrueNAS API turns the NAS from “WebUI only” into a programmable platform. Three hours invested in a few Python scripts deliver reports that do not even exist in the WebUI — and automate routines that otherwise run manually. Anyone running multiple TrueNAS systems or producing compliance reports regularly has far more leverage here than with click routines.

Anyone starting their own automation should begin with a read-only use case (reports). Once that runs robustly, write operations (e.g. snapshot cleanup) come next — always with thorough testing on a test instance before production systems are touched.

More on these topics:

Need IT consulting?

Contact us for a no-obligation consultation on Proxmox, OPNsense, TrueNAS and more.

Get in touch