REST API overview
The PilotFox REST API: base URL, request and response format, ids, and the OpenAPI document behind the reference.
Everything the PilotFox dashboard shows comes from a REST API, and the same API is open to your own code: read your analytics into a report or a spreadsheet, manage goals and funnels, send events from your server, or record payments from a billing system PilotFox does not connect to.
Base URL
https://pilotfox.app/api/v1A self-hosted instance answers at its own address: PUBLIC_URL followed by /api/v1 (for example
https://analytics.example.com/api/v1). Every example in these docs uses https://pilotfox.app; replace it with
your instance's URL if you run your own.
A first request
Create an API key. In the dashboard, open Organization settings → API keys and create a key with the
analytics:read permission. The key is shown once; keep it on your server. See
Authentication with API keys.
Find your site id. It is the value of data-site in your tracking script tag, and the id of each site that
GET /api/v1/orgs/{orgId}/sites lists.
Ask for the last seven days.
curl -H "Authorization: Bearer $PILOTFOX_API_KEY" \
"https://pilotfox.app/api/v1/sites/SITE_ID/overview?last=7d"{
"data": {
"visitors": 8,
"sessions": 8,
"pageviews": 7,
"events": 2,
"bounce_rate": 85.71428571428571,
"avg_session_duration": 0.009285714285714286,
"median_session_duration": 0,
"views_per_session": 1
},
"meta": {
"window": {
"from": "2026-10-20T20:29:45.263Z",
"to": "2026-10-27T20:29:45.263Z",
"tz": "Europe/Berlin",
"bucket": "day"
},
"cache": "miss"
}
}Requests
- JSON in, JSON out. Send request bodies as
application/json. A JSON body may be up to 1 MiB; a larger one is answered413(payload_too_large). File uploads to imports aremultipart/form-datawith a limit of their own. - Query parameters carry the time window, filters and paging of the analytics endpoints. Filters are a JSON array
in one parameter, so URL-encode it (curl's
-G --data-urlencodedoes). See Time windows, filters and comparisons. - One credential per request: an API key, or (for the dashboard) a session cookie, or a share token. Sending two is
a
400. See Authentication. - From your server only. The API answers cross-origin browser requests only from the instance's own origin (and
any
DASHBOARD_ORIGINSa self-hosted instance configures), so a web page on another site cannot call it, and an API key in a web page would be readable by anyone. Browsers send data to PilotFox through the tracking script, which needs no key.
Responses
A successful response is an object with data and, on the analytics endpoints, meta:
{ "data": { "visitors": 8 }, "meta": { "window": { "from": "…", "to": "…", "tz": "Europe/Berlin", "bucket": "day" } } }meta field | When | What it holds |
|---|---|---|
window | analytics endpoints | The resolved window: from and to (ISO 8601 instants, UTC; from is null for all=true), the time zone tz and the bucket of time series. |
compare | when you asked for a comparison | from and to of the comparison window. |
totalCount | reports paged by limit and offset | How many rows exist across all pages. |
nextCursor | lists paged by cursor | The cursor of the next page, or null on the last one. |
truncated | map points, the session map timeline, live visitors | true when there were more rows than the answer holds. |
cache | cached reports | hit, miss or coalesced (another identical request was already running and its result was shared). |
Other endpoints (sites, API keys, goals, segments and the like) answer { "data": … } alone. Creating something answers
201, starting a background job 202, and some deletes and writes 204 with no body; each
reference page lists the statuses of its endpoint.
An error is an object with error, and the HTTP status matches it:
{ "error": { "code": "not_found", "message": "Site not found" } }The Errors page lists every code.
Field names, ids and times
- Analytics rows (overview, breakdowns, sessions, events, payments) use
snake_casefields:bounce_rate,session_id,visitor_id. Stored resources and their request bodies (sites, API keys, goals, funnels, segments, annotations, share links) usecamelCase:siteIds,expiresAt,isPublic. The bodies that record data, server-side ingest and the payments API, usesnake_caselike the rows they write:anonymous_id,payment_id. - 64-bit ids are strings, for example
"session_id": "10753433134694004327", because JSON numbers cannot hold them exactly. Keep them as strings in your code. - Times are ISO 8601 strings in UTC (
2026-10-27T20:29:45.263Z). - Sites are addressed by their public id (a short string such as
74lku1zte8m8fpyg): the{siteId}of every/api/v1/sites/{siteId}/…path, thedata-siteof the script tag and thesiteof an ingested event. - Organizations are addressed by their id, a UUID: the
{orgId}of every/api/v1/orgs/{orgId}/…path. It appears in the dashboard's address bar on organization pages (/orgs/{orgId}/settings).
Cached reports
Analytics reports are cached for a short time (30 seconds by default; QUERY_CACHE_TTL_SECONDS on a self-hosted
instance), shared by every caller asking the same question, and meta.cache says whether you got a cached result.
A caller holding site:read on the site (organization members, and API keys given that permission) can send
Cache-Control: no-cache to skip the cache; from any other caller the header is ignored. Lists of sessions, users,
raw events and payments, and the realtime endpoints, are never cached.
The OpenAPI document
Every instance describes its own API:
| URL | What |
|---|---|
https://pilotfox.app/api/docs/ | The interactive API reference, served by the instance. |
https://pilotfox.app/api/docs/openapi.json | The OpenAPI 3.1 document behind it, generated from the API's own request and response schemas (openapi.yaml too). |
The API reference in these docs is generated from the same document: one page per endpoint, with its parameters, request body and responses. Feed the document to a code generator to get a typed client.
In this section
Authentication
Create API keys, send them, limit them to sites, revoke them.
Permissions
What each permission allows, and which one every endpoint needs.
Errors
The error envelope, every code and its HTTP status.
Rate limits
Request budgets per key, and what a 429 looks like.
Pagination
Offsets for ranked reports, cursors for lists.
Query parameters
Time windows, time zones, comparisons, filters and segments.
API reference
Every endpoint, generated from the OpenAPI document.
Server-side ingest
Send events and identify users from your own server.