Skip to content

REST API overview

Base URL, bearer authentication, the JSON envelope, error codes, rate limits, date ranges, the filter DSL and cursor paging.

The API is JSON over HTTPS. Every path below is relative to https://statsy.co/api/v1. The hosted API also answers on https://api.statsy.co, which is what the CLI uses by default.

Authentication

Send an API token as a bearer token. There is no other header to set.

bash
curl -H "Authorization: Bearer sta_your_token" \
  "https://statsy.co/api/v1/sites/ws_yoursitekey/overview?from=30d"

In a path, :site accepts either the site’s public id or its ws_ public key. A missing or revoked token gives 401.

Response envelope

Every JSON response uses one of two shapes.

json
{ "status": "success", "data": { } }

{ "status": "error", "error": { "code": "bad_dimension", "message": "..." } }

The CSV export is the one exception: it streams text/csv with a Content-Disposition filename instead.

Errors

StatusTypical codesMeaning
400validation_error, bad_range, bad_dimension, bad_filters, bad_cursorThe request was malformed. The message says which part.
401unauthorized, invalid_tokenNo bearer token, or the token is unknown or revoked.
402trial_expired, plan_cancelled, plan_limitReads still work; writes are paused until you pick a plan, or a plan limit was reached.
403forbidden, session_requiredYour role or token kind is not allowed to do this.
404site_not_found, goal_not_found, not_foundNo such site, resource or route.
409already_member, widget_limit, last_ownerThe request conflicts with the current state.
429rate_limited, export_busyToo many requests. Wait and retry.
500internal_errorSomething went wrong on our side.

Rate limits

Token-authenticated requests are limited to 60 per minute; requests from a signed-in dashboard session get 600. Every response carries X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset, and a 429 adds Retry-After in seconds. A few endpoints have their own, tighter limits, noted on their pages.

Dates, presets and time zones

Analytics endpoints share the same range parameters. from takes either a preset or a date; to takes a date and defaults to now.

PresetMeans
nowThe last 30 minutes, always from raw events.
24hA rolling 24 hours.
7dSeven calendar days in the site’s time zone, up to now.
30dThirty calendar days. This is the default when from is omitted.
90dNinety calendar days.
12moThe start of the month eleven months ago, up to now.

For exact windows pass an ISO timestamp or YYYY-MM-DD. A bare date is read as UTC midnight. from must be before to, and a range may not exceed 400 days, or you get 400 bad_range.

tz sets the IANA time zone for day and month boundaries. It defaults to the site’s time zone, and an unrecognised value falls back to UTC. Add compare=prev to get the equally long window immediately before yours alongside the current one.

Note. Responses include source and clamped. Raw events are kept 90 days, so a long range that needs raw events is clamped to the last 90 days and clamped becomes true. Rolled-up totals go back as far as the site does.

Filters

filters is a URL-encoded JSON array of {k, op, v} objects, up to 20, all combined with AND.

bash
curl -G -H "Authorization: Bearer sta_your_token" \
  --data-urlencode 'filters=[{"k":"country","op":"is","v":"US"},{"k":"path","op":"contains","v":"/blog"}]' \
  "https://statsy.co/api/v1/sites/ws_yoursitekey/breakdown?dimension=channel"

Keys: path, entry_path, referrer_host, channel, utm_source, utm_medium, utm_campaign, country, region, city, device, browser, os, lang, goal and post. Operators: is (the default), not, contains and not_contains; the two contains forms ignore case.

segment=<id> loads a saved filter set and adds its filters to yours. Filtering on entry_path, region, city, lang, goal or post forces the query onto raw events, so the range is clamped to 90 days.

Paging

List endpoints take limit and cursor. A cursor is an opaque string; pass back the one the previous response returned, and stop when it is null. The field name differs by endpoint: breakdowns and visitors return cursor, replays return next_cursor, and payments return nextCursor.

Last updated · Markdown version