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:
| API | Endpoint | Property |
|---|---|---|
| WebSocket (JSON-RPC) | wss://truenas/websocket | True real-time, event-based, full feature surface |
| REST | https://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
| Endpoint | Use case |
|---|---|
/api/v2.0/replication | replication status of all tasks |
/api/v2.0/cloudsync/credentials | cloud credentials for audit |
/api/v2.0/system/info | TrueNAS version, uptime, hostname |
/api/v2.0/alert/list | current alerts (WebUI bell) |
/api/v2.0/reporting/get_data | performance metrics (CPU, IO, network) |
/api/v2.0/user | user management |
/api/v2.0/sharing/smb | list 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=Falsein 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?
| Scenario | Recommendation |
|---|---|
| Standard monitoring (CPU, disk, memory) | classic tools like Zabbix |
| TrueNAS-specific metrics | API scripts, no Zabbix plugin required |
| Compliance reports / audit exports | API scripts with a defined output format |
| Bulk configuration | API scripts, idempotent, in Git |
| One-off operations | keep 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.
Related Articles
More on these topics:
More articles
TrueNAS Cloud Sync to Backblaze B2: Affordable Offsite Backup
TrueNAS Cloud Sync to Backblaze B2 as an offsite backup target: B2 application key, bucket setup, push mode, encryption and bandwidth management. With best practices for SMBs.
Cloud Backup Providers Compared: B2, Storj, Wasabi, AWS
Backblaze B2, Storj, Wasabi and AWS S3 compared as S3-compatible backup targets. Evaluation criteria for SMBs: price, egress, geo-redundancy, EU location, minimum retention — with a clear link to the 3-2-1 rule.
TrueNAS Spotlight: Full-Text Search and Search Index on the NAS
TrueNAS 26.04 brings a Search Index with full-text search across file names and content. How Apache Tika and Lucene work together, which use cases pay off, and what to watch for on privacy and resource consumption.