Skip to content

Command line

statable is the Stats API as a command. One static binary, no runtime, no configuration file it insists on. Every command prints and exits, so it composes with jq, awk, a cron entry or a CI step without anything having to be told it is not a terminal.

This page gets you to your first number. Commands is the full reference, Output and exit codes is what a script reads, and Configuration is how to point it somewhere else.

Install

brew install key-arg/tap/statable
go install github.com/key-arg/statable-cli/cmd/statable@latest
mise use -g "ubi:key-arg/statable-cli[exe=statable]"

The exe is required: ubi looks for a file named after the project, and the binary is statable.

An Arch package is built by the release pipeline and will be published as statable-bin, but it does not exist yet. AUR registration is closed while they deal with a wave of automated signups, so there is no account to publish it from. Arch and Omarchy users want go install or the archive until then.

There is no curl … | sh installer, on purpose. Archives, checksums and keyless signatures are on the releases page; cosign verify-blob against checksums.txt tells you which workflow and which commit produced what you downloaded.

Sign in

statable auth login

On a terminal this asks for a key and stores it in the system keyring. Anywhere else, a pipe, CI, an agent, it prints what to do next and exits rather than blocking on input nobody can type. For CI, set the key in the environment instead:

export STATABLE_API_KEY=stbl_...

The key is resolved from --key, then STATABLE_API_KEY, then the keyring, then a 0600 file. statable auth status always reports which of those the active key came from. If no keyring is available the command refuses and names --insecure-storage; it never quietly writes the key in the clear.

--key puts the secret in ps output and in your shell history, so the environment variable is the better habit.

On Windows the keyring is the Credential Manager and does protect the key. The --insecure-storage file does not: Windows maps a file mode onto the read-only attribute and nothing else, so a file written with 0600 is readable by every account on the machine. The command says so when it writes one.

Getting a key in the first place: API keys.

Read something

statable sites                       # what this key can read
statable sites use example.com       # remember a default

statable now                         # visitors active right now
statable stats --range 30d --compare previous_period
statable series --by day --range 30d
statable top pages
statable top countries --range month

top takes a short name, pages, sources, countries, browsers, goals, or a full dimension such as visit:utm_campaign. Run statable top with no argument to see the short names.

The rest of the surface:

statable props                       # custom property keys, and the event each belongs to
statable goals                       # the conversion goals defined on the site
statable snippet                     # the install tag, ready to paste
statable funnels                     # saved funnel definitions
statable funnel 45 --range 7d        # run one, step by step
statable subscription                # the plan state of the key's owner

statable goals lists the definitions; statable top goals reports how they converted. statable snippet prints the tag alone, so statable snippet | pbcopy puts something installable on the clipboard, and the script URL and site id are in the machine formats beside it.

Both are served from behind the API's write guard even though reading them needs only the read scope. On a deployment with the write surface switched off they answer 404 write_disabled, which reads as a missing site if you do not know that; the command says what is actually wrong instead.

Anything the commands do not cover

Everything else goes through query, which is POST /query with flags:

statable query --metric visitors,pageviews --range 7d \
               --dimension time:day --filter country=US

Filters use a short syntax: = is, != is not, ~ contains, !~ does not contain. Repeating --filter combines with AND; commas inside one flag are OR.

Past that there is statable api call, which reaches any endpoint at all, including the ones that write. See Configuration.

Where the numbers come from

Every command is the API underneath, so the Query reference and Endpoints reference describe exactly what each one returns. statable query is POST /query; statable top pages is that same call with one dimension and a default set of metrics.