A small, dependency-free Go CLI for the UniFi Network Integration API.
Unofficial tool. This project is not affiliated with, endorsed by, or sponsored by Ubiquiti Inc. "UniFi" and "Ubiquiti" are trademarks of their respective owner. Use at your own risk.
- Overview
- Install
- Quickstart
- Configuration
- Global Flags
- Commands
- Full API coverage
- Output Formats
- Pagination
- Safety: Mutating Commands
- Security
- Claude skill
- Exit Codes
- Troubleshooting
- Contributing
- License
unifi talks to the UniFi Network Integration API at:
https://{host}/proxy/network/integration/v1
Authentication is via the X-API-KEY header. Requires UniFi Network 9.x+ and UniFi OS 9.3.43+.
Obtain an API key from the UniFi console: Settings → Control Plane / Integrations → API Keys (also available via unifi.ui.com Site Manager).
Note: Local UniFi consoles use self-signed TLS certificates. Pass
--insecure(or setUNIFI_INSECURE=true) for any local console.
Prerequisites: Go 1.23+, and a controller running UniFi Network 9.x+ on UniFi OS 9.3.43+.
go install github.com/colindickson/unifi/cmd/unifi@latestThis drops the unifi binary in $(go env GOPATH)/bin — make sure that
directory is on your PATH.
# Build binary to ./unifi
make build
# Build, install the binary to ~/bin, and link the Claude skill (see below)
make install
# Override the install location
make install INSTALL_DIR=/usr/local/bin
make installputs the binary in~/binby default, which is not on thePATHon most systems. Either add~/binto yourPATHor install elsewhere withmake install INSTALL_DIR=/usr/local/bin.
make install also runs make install-skill, which symlinks the bundled
unifi-network-manager Claude skill into ~/.claude/skills/. The symlink
points back at skills/unifi-network-manager/ in this repo, so edits to the
skill are picked up live. Manage it independently with:
make install-skill # create/refresh the symlink (idempotent)
make uninstall-skill # remove the symlink (only if it is a symlink)make uninstall removes both the binary and the skill symlink. Run
make help to list every target.
# 1. Install (see above), then confirm it runs.
unifi --help
# 2. Get an API key from your console:
# Settings → Control Plane / Integrations → API Keys
# 3. Save your connection settings. Pass the key via the environment so it
# never lands in your shell history (local consoles need --insecure):
UNIFI_API_KEY=<your-key> unifi configure --host 192.168.1.1 --insecure
# 4. Verify connectivity.
unifi info
# 5. Discover your site, then list devices.
unifi sites list
unifi devices list --site <site-id>Configuration is resolved in order: flags > environment variables > config file (highest to lowest precedence).
| Flag | Description |
|---|---|
--host |
Controller host (IP or hostname) |
--api-key |
API key |
--site |
Site ID |
--insecure |
Skip TLS certificate verification |
| Variable | Description |
|---|---|
UNIFI_HOST |
Controller host |
UNIFI_API_KEY |
API key |
UNIFI_SITE |
Site ID |
UNIFI_INSECURE |
Skip TLS verification — truthy values: 1, true, yes, on |
UNIFI_OUTPUT |
Output format — json (default) or table |
The config file is stored at ~/.config/unifi/config.json (mode 0600). Override the path with $UNIFI_CONFIG.
Recognized fields: host, api_key, site, insecure, and output (json or table).
Write settings with the configure command. Prefer supplying the key via the
UNIFI_API_KEY environment variable so it is not recorded in your shell
history or visible in the process list:
UNIFI_API_KEY=<your-key> unifi configure --host 192.168.1.1 --insecureAvoid
--api-key <key>on the command line on shared systems. Arguments are visible to other local users (viaps//proc) and are saved to your shell history. UseUNIFI_API_KEYor the config file instead. See Security.
| Flag | Description | Default |
|---|---|---|
--host <host> |
Controller host | — |
--api-key <key> |
API key | — |
--site <id> |
Site ID | — |
--insecure |
Skip TLS certificate verification | false |
-o, --output <table|json> |
Output format (table is opt-in) | json |
--limit <n> |
Page size for list commands | 25 |
--all |
Auto-paginate and fetch all pages | false |
--yes |
Confirm mutating commands | false |
-h, --help |
Show help | — |
| Command | Purpose |
|---|---|
configure |
Save connection settings |
info |
Show controller info |
sites |
List sites |
devices |
List/get/restart/port-cycle devices |
clients |
List/get/authorize clients |
vouchers |
Manage hotspot vouchers |
api |
Universal passthrough to any API endpoint — see Full API coverage |
networks, firewall, acl-rules, dns, traffic-lists, wans, vpn-servers, radius-profiles, device-tags, countries |
Typed resource commands — see Full API coverage |
unifi configure [flags]Writes the supplied flags to the config file. Existing values are preserved for any flags not supplied.
UNIFI_API_KEY=my-api-key unifi configure --host 192.168.1.1 --insecure
# Saved configuration to ~/.config/unifi/config.jsonunifi info [flags]Returns JSON with application/controller metadata.
unifi info # uses saved config; or pass --host / --insecure ad hocunifi sites list [flags]Lists all sites. Table output shows ID and NAME.
unifi sites list
# ID NAME
# 66b1e2f4a1b2c3d4e5f60001 Defaultunifi sites list -o jsonunifi devices list [--site <id>] [--limit N] [--all] [flags]Lists devices for the configured site. Table output shows ID, NAME, MODEL, STATE, and MAC.
unifi devices list --site default
# ID NAME MODEL STATE MAC
# 66b1e2f4a1b2c3d4e5f60002 Living Room AP U6-Lite online aa:bb:cc:dd:ee:ffunifi devices get <device-id> [flags]Shows full JSON detail for a single device.
unifi devices get 66b1e2f4a1b2c3d4e5f60002unifi devices stats <device-id> [flags]Shows the latest statistics for a device. The default JSON output is the full
object (uptimeSec, cpuUtilizationPct, memoryUtilizationPct, load averages,
uplink.txRateBps/rxRateBps, per-radio data). In table mode (-o table) it
renders a readable health summary instead.
unifi devices stats 66b1e2f4a1b2c3d4e5f60002unifi devices stats 66b1e2f4a1b2c3d4e5f60002 -o table
# UPTIME 11h 57m
# CPU 20.2%
# MEMORY 84.7%
# LOAD (1/5/15m) 1.27 / 1.15 / 1.21
# UPLINK TX 99.14 Kbps
# UPLINK RX 117.47 Kbps
# LAST HEARTBEAT 2026-06-01T02:53:55Zunifi devices restart <device-id> --yes [flags]Sends a RESTART action to the device. Requires --yes.
unifi devices restart 66b1e2f4a1b2c3d4e5f60002 --yesunifi devices port-cycle <device-id> --port N --yes [flags]Power-cycles the specified port (--port N is required). Requires --yes.
unifi devices port-cycle 66b1e2f4a1b2c3d4e5f60002 --port 3 --yesunifi clients list [--site <id>] [--limit N] [--all] [flags]Lists clients for the configured site. Table output shows ID, NAME, IP, and MAC.
unifi clients list
# ID NAME IP MAC
# 66b1e2f4a1b2c3d4e5f60003 My Laptop 192.168.1.42 11:22:33:44:55:66unifi clients get <client-id> [flags]Shows full JSON detail for a single client.
unifi clients get 66b1e2f4a1b2c3d4e5f60003unifi clients authorize <client-id> --yes [flags]Authorizes a guest client (AUTHORIZE_GUEST action). Requires --yes.
unifi clients authorize 66b1e2f4a1b2c3d4e5f60003 --yesunifi vouchers list [--site <id>] [--limit N] [--all] [flags]Lists hotspot vouchers. Always outputs JSON (both table and json modes).
unifi vouchers listunifi vouchers create [--name N] [--count C] [--minutes M] [--quota-mb MB] [--guests G] [--rx-rate Kbps] [--tx-rate Kbps] --yes [flags]| Flag | Description | Default |
|---|---|---|
--name |
Voucher name | — |
--count |
Number of vouchers to create | 1 |
--minutes |
Time limit in minutes | 1440 (24 h) |
--quota-mb |
Data usage limit in MB | unlimited |
--guests |
Authorized guest limit | unlimited |
--rx-rate |
Download rate limit in Kbps | unlimited |
--tx-rate |
Upload rate limit in Kbps | unlimited |
Requires --yes.
# Create 5 day-pass vouchers with a 1 GB data cap and 10/2 Mbps rate limits
unifi vouchers create --count 5 --minutes 1440 --quota-mb 1024 --rx-rate 10000 --tx-rate 2000 --name "Day Pass" --yesunifi vouchers delete <voucher-id> --yes [flags]Deletes a voucher. Requires --yes.
unifi vouchers delete 66b1e2f4a1b2c3d4e5f60099 --yesBeyond the curated sites/devices/clients/vouchers commands above, unifi exposes every UniFi Network Integration API endpoint through two complementary mechanisms:
- Typed resource commands — a convenience layer for the common, well-known resource groups (networks, WLANs, firewall, DNS, etc.). Consistent
list/get/create/update/deleteverbs, friendly table output, and automatic{site}resolution. - The
apipassthrough — a universal catch-all that issues a raw request to any path. Use it for anything not covered by a typed command, including undocumented or future-firmware endpoints. It always works because you supply the exact method and path.
All
create/update/deletetyped commands, and any non-GETapicall, require the--yesflag.
unifi api <METHOD> <path> [--data <json>] [--data-file <file|->] [--query k=v]... [flags]<METHOD>is one ofGET,POST,PUT,PATCH,DELETE(case-insensitive).<path>is the API path relative to the integration base. A leading/is added if missing.- A
{site}or:sitetoken anywhere in the path is replaced with the resolved site (URL-escaped), so you don't have to hardcode the site ID. --data/--data-filesupply a JSON request body and are mutually exclusive; the body must be valid JSON.--data-file -reads the body from stdin.--query k=vis repeatable and adds URL query parameters.- Non-
GETmethods require--yes. - Output is always JSON.
# GET firewall policies for the resolved site
unifi api GET /sites/{site}/firewall/policies
# Create a network (POST with an inline body) — requires --yes
unifi api POST /sites/{site}/networks \
--data '{"name":"IoT","purpose":"corporate","vlan":40}' --yes
# Delete a DNS policy — requires --yes
unifi api DELETE /sites/{site}/dns/policies/66b1e2f4a1b2c3d4e5f6abcd --yes
# Use --query for filtering / pagination knobs
unifi api GET /sites/{site}/clients --query limit=50 --query offset=0
# Read the body from a file (or stdin with -)
unifi api PUT /sites/{site}/networks/<id> --data-file ./network.json --yes
# Reach a resource that has no typed command on your firmware (e.g. WLANs)
unifi api GET /sites/{site}/wlansEach group supports list, get, create, update, and delete unless noted otherwise. All paths are site-scoped (/sites/{site}/...) except countries. Paths below are verified against UniFi Network application v10.4.57.
| Group | Path | Ops |
|---|---|---|
networks |
/sites/{site}/networks |
list, get, create, update, delete |
firewall zones |
/sites/{site}/firewall/zones |
list, get, create, update, delete |
firewall policies |
/sites/{site}/firewall/policies |
list, get, create, update, delete |
acl-rules |
/sites/{site}/acl-rules |
list, get, create, update, delete |
dns |
/sites/{site}/dns/policies |
list, get, create, update, delete |
traffic-lists |
/sites/{site}/traffic-matching-lists |
list, get, create, update, delete |
wans |
/sites/{site}/wans |
list, get, create, update, delete |
vpn-servers |
/sites/{site}/vpn/servers |
list, get, create, update, delete |
radius-profiles |
/sites/{site}/radius/profiles |
list, get, create, update, delete |
device-tags |
/sites/{site}/device-tags |
list, get, create, update, delete |
countries |
/countries |
list |
Not yet in the integration API on all firmware:
wlans(WiFi/SSIDs),port-forwards, andtraffic-routesreturned 404 on v10.4.57 and so have no typed command. If your firmware exposes them, reach them via theapipassthrough — e.g.unifi api GET /sites/{site}/wlans.
Generic command forms:
unifi <group> list
unifi <group> get <id>
unifi <group> create --data '<json>' --yes
unifi <group> update <id> --data '<json>' --yes
unifi <group> delete <id> --yesFor firewall, the sub-resource (zones or policies) comes first: unifi firewall <sub> <action> [id].
In table mode (-o table), list shows ID, NAME, and ENABLED columns; the default JSON mode shows the full objects.
unifi firewall policies list
unifi firewall zones get 66b1e2f4a1b2c3d4e5f60010
unifi networks list -o table
unifi dns create --data '{"name":"block-ads","action":"BLOCK"}' --yes
unifi wans list
unifi countries listSource of truth: the exact schemas and paths for your console are documented in the UniFi Network app under Settings → Integrations. The typed paths above are best-effort against the official API surface; if a typed command's path differs on your firmware, fall back to
unifi api <METHOD> <path>using the path from that documentation. Theapipassthrough accepts whatever path that doc specifies.
The default output format is json (machine-readable, recommended for scripting and automation). For human-readable tables, opt in with -o table, UNIFI_OUTPUT=table, or "output": "table" in the config file.
| Mode | Behavior |
|---|---|
json (default) |
Raw API JSON, pretty-printed — recommended for scripting and automation |
table (-o table) |
Human-readable columns for list commands; JSON pretty-print for single-item and action responses |
# Pipe device list into jq (json is the default)
unifi devices list | jq '.[].name'
# Human-readable table
unifi devices list -o tableList commands default to --limit 25. Use --all to auto-paginate and retrieve every item across all pages (page size 200 internally).
unifi clients list --all
unifi devices list --limit 50Mutating commands (devices restart, devices port-cycle, clients authorize, vouchers create, vouchers delete, every typed-resource create/update/delete, and any non-GET api call) always require the --yes flag, regardless of output format. The guard is independent of the -o/UNIFI_OUTPUT setting.
# Always requires --yes
unifi devices restart <id> --yesunifi authenticates with an API key. Handle it carefully:
- Prefer
UNIFI_API_KEYorunifi configureover--api-keyon the command line — command-line arguments are visible to other local users (ps,/proc) and are written to shell history. - The config file (
~/.config/unifi/config.json) stores the key in plaintext with0600permissions. Don't commit it or loosen its permissions;unifiwarns if it finds the file readable by other users. - TLS verification is on by default. Only use
--insecure/UNIFI_INSECURE=truefor local consoles with self-signed certificates, and only when you trust the network path to the controller. The API key is stripped if the controller redirects to a different host. --data-filereads from the local filesystem with your full permissions; don't point it at untrusted input in automated pipelines.
To report a vulnerability, see SECURITY.md.
This repo bundles a Claude Code skill, unifi-network-manager, that teaches
Claude when and how to drive this CLI (site discovery, read-before-write,
the api passthrough, and --yes confirmation for mutations). The source lives
at skills/unifi-network-manager/SKILL.md and is symlinked into
~/.claude/skills/ by make install (or make install-skill). Because it is a
symlink, edits to the skill in this repo are live in Claude immediately.
The skill is optional — make install links it as a convenience, but the
CLI works without it. Install or remove it independently with make install-skill / make uninstall-skill.
⚠️ Using this skill with an AI agent is at your own risk. The skill is a best-effort convenience for driving the CLI with Claude. It can issue commands that change your network — restarting devices, cycling PoE ports, creating or deleting firewall policies, networks, and other resources. The project and its authors accept no responsibility for any outcome, including downtime, misconfiguration, lockout, or data loss, that results from an AI agent operating this tool. You are solely responsible for the actions of any agent you run, for reviewing what it proposes before confirming, and for keeping it scoped to networks you are authorized to manage. Mutating commands always require explicit--yesconfirmation — keep a human in the loop.
| Code | Meaning |
|---|---|
0 |
Success |
1 |
Runtime or API error |
2 |
Unknown command or usage error |
| Symptom | Fix |
|---|---|
x509: certificate signed by unknown authority |
Pass --insecure or set UNIFI_INSECURE=true |
API error 401 / 403 |
Bad or missing API key — check --api-key / UNIFI_API_KEY |
API error 429 |
Rate limited — the CLI prints the retry-after value in seconds |
host is required |
Run unifi configure --host ... or set UNIFI_HOST |
config file ... has permissions ... |
Restrict it: chmod 600 ~/.config/unifi/config.json |
Contributions are welcome — see CONTRIBUTING.md. The project
is standard-library only and developed test-first; run make test (which
includes the race detector) before sending a PR.
Released under the MIT License. © 2026 Colin Dickson.