A command-line interface for Apitally, built for agents.
Apitally is a simple API monitoring and analytics tool that makes it easy to understand API usage, monitor performance, and troubleshoot issues.
Learn more about Apitally on our π website or check out the π documentation.
- Stream API request logs from Apitally to a local DuckDB database
- Run arbitrary SQL queries against that database to analyze API request data
- Single binary with bundled DuckDB, no runtime dependencies, written in Rust (it's fast)
Install the CLI with the standalone installer script:
# On macOS and Linux
curl -fsSL https://apitally.io/cli/install.sh | sh# On Windows
powershell -ExecutionPolicy Bypass -c "irm https://apitally.io/cli/install.ps1 | iex"Alternatively, you can download the binary for your platform from the latest release on GitHub.
To use the CLI, you need an API key. You can create one in the Apitally dashboard under Settings β API keys.
Then run the auth command to configure your API key interactively:
apitally authOr provide the key directly:
apitally auth --api-key "your-api-key"The API key is saved to ~/.apitally/auth.json.
You can also set the API key via the APITALLY_API_KEY environment variable or pass the --api-key flag to any command.
Run apitally --help to see all commands and options.
apitally apps [--db <path>]
List all apps in your team. Use this to get app IDs for other commands.
Outputs newline-delimited JSON (one object per line). If you provide the --db example.duckdb flag, data is written to the apps table in the provided database instead. Existing records will be updated. If the database file doesn't exist, it will be created.
Example command:
apitally appsExample output (without --db flag):
{"id":1,"name":"Example API 1","framework":"FastAPI","client_id":"76bf09e2-8996-4dd0-bdb5-ccdc3a48f64c","envs":[{"id":1,"name":"prod","created_at":"2026-01-01T00:00:00.000000Z","last_sync_at":"2026-01-01T01:00:00.000000Z"}],"created_at":"2026-01-01T00:00:00.000000Z"}
{"id":2,"name":"Example API 2","framework":"FastAPI","client_id":"339c08bb-5e88-4cba-a24d-be9d80fbd096","envs":[{"id":2,"name":"prod","created_at":"2026-01-02T00:00:00.000000Z","last_sync_at":"2026-01-02T01:00:00.000000Z"}],"created_at":"2026-01-02T00:00:00.000000Z"}apitally consumers <app-id> [--requests-since <datetime>] [--db <path>]
List all consumers for an app. Use this to get consumer details to combine with request log data, which only includes consumer IDs.
Use the --requests-since flag to only return consumers that have made requests since a specific date/time (ISO 8601 format).
Outputs newline-delimited JSON (one object per line). If you provide the --db example.duckdb flag, data is written to the consumers table in the provided database instead. Existing records will be updated. If the database file doesn't exist, it will be created.
Example command:
apitally consumers 1 --requests-since "2026-01-01T00:00:00Z"Example output (without --db flag):
{"id":1,"identifier":"bob@example.com","name":"Bob","group":{"id":1,"name":"Admins"},"created_at":"2026-01-01T00:00:00Z","last_request_at":"2026-01-01T01:00:00Z"}
{"id":2,"identifier":"alice@example.com","name":"Alice","group":null,"created_at":"2026-01-02T00:00:00Z","last_request_at":"2026-01-02T02:00:00Z"}apitally request-logs <app-id> \
--since <datetime> [--until <datetime>] \
[--fields <json>] [--filters <json>] [--limit <n>] \
[--db <path>]
Retrieve request log data for an app.
The time range is --since inclusive and --until exclusive. If --until is not provided, it defaults to now. If a timestamp does not include a timezone, UTC is assumed.
Outputs newline-delimited JSON (one object per line). If you provide the --db example.duckdb flag, data is written to the request_logs table in the provided database instead. Existing records will be updated. If the database file doesn't exist, it will be created.
Results are ordered by timestamp ascending and capped at 1,000,000 records. Requests to endpoints marked as excluded in the Apitally dashboard are not returned.
Use the --fields flag to pass a JSON array of fields to include. If omitted, default fields are returned.
| Field | Type | Default |
|---|---|---|
timestamp |
string (datetime) | β |
request_uuid |
string (ID) | β |
app_env |
string | β |
method |
string | β |
path |
string | β |
url |
string | β |
consumer_id |
int (ID) | β |
request_headers |
array of tuples | |
request_size |
int | β |
request_body_json |
string (JSON) | |
status_code |
int | β |
response_time_ms |
int | β |
response_headers |
array of tuples | |
response_size |
int | β |
response_body_json |
string (JSON) | |
client_ip |
string | β |
client_country_iso_code |
string | β |
exception_type |
string | |
exception_message |
string | |
exception_stacktrace |
string | |
sentry_event_id |
string (ID) | |
trace_id |
string (ID) |
Use the --filters flag to pass a JSON array of filter objects with field, op, and value keys. Multiple filters are combined with a logical AND. Supported operators are:
- String fields:
eq,neq,in,not_in,like,not_like,ilike,not_ilike - Numeric fields:
eq,neq,gt,gte,lt,lte,in,not_in - Header fields:
eq,neq,in,not_in,like,not_like,ilike,not_ilike,exists,not_exists - ID fields:
eq,neq,in,not_in
For in and not_in, value must be a JSON array. For header fields, also provide key for the header name. For exists and not_exists, omit value.
Example command:
apitally request-logs 1 \
--since "2026-01-01T00:00:00Z" \
--filters '[{"field":"status_code","op":"gte","value":400}]' \
--limit 2Example output (without --db flag):
{"timestamp":"2026-01-01T00:15:00.000Z","request_uuid":"2fbc1df6-3124-4ed1-a376-7d2c64e4d5cf","app_env":"prod","method":"GET","path":"/test/1","url":"https://api.example.com/test/1","consumer_id":1,"request_size":0,"status_code":404,"response_time_ms":122,"response_size":66,"client_ip":"203.0.113.10","client_country_iso_code":"DE"}
{"timestamp":"2026-01-01T00:16:00.000Z","request_uuid":"c6d32f8a-0bc1-43c1-b6c5-7d04363dc97c","app_env":"prod","method":"GET","path":"/test/2","url":"https://api.example.com/test/2","consumer_id":1,"request_size":0,"status_code":500,"response_time_ms":68,"response_size":66,"client_ip":"198.51.100.22","client_country_iso_code":"US"}apitally sql [<query>] --db <path>Run a SQL query against a local DuckDB database and output the result as newline-delimited JSON (one object per line). If the query argument is omitted, the query is read from stdin.
Available tables are apps, app_envs, consumers, and request_logs.
DuckDB's SQL dialect closely matches PostgreSQL's semantics.
Example commands:
apitally sql \
"SELECT timestamp, method, path, status_code FROM request_logs WHERE status_code >= 400" \
--db example.duckdbecho "SELECT COUNT(*) FROM request_logs" | apitally sql --db example.duckdbExample output:
{"timestamp":"2026-01-01T00:16:00.000Z","method":"POST","path":"/users","status_code":500}
{"timestamp":"2026-01-01T00:15:00.000Z","method":"GET","path":"/users/{userId}","status_code":404}| Code | Meaning |
|---|---|
| 0 | Success |
| 1 | General / unknown error |
| 2 | Usage error (invalid arguments, missing required flags) |
| 3 | Authentication error (missing or invalid API key) |
| 4 | Input error (invalid argument values) |
| 5 | API / network error |
If you need help please create a new discussion on GitHub or email us at support@apitally.io. We'll get back to you as soon as possible.