Reference
HTTP API
Send events from any language with no browser involved, and read your stats programmatically. Base URL https://api.thunderstats.com.
Authentication
Create an API key under Settings → API Keys. Keys are shown once at creation. Pass one as a bearer token:
Authorization: Bearer ts_your_api_key
- A key is scoped to a single site. It can never read or write another site, even one you own.
- A key is read-only except for the collect endpoints below. It cannot change settings, manage goals, or touch your account.
Send a custom event
Records a conversion or any other named action. Returns 202 {"ok": true}.
| Field | Type | Notes |
|---|---|---|
name | string | Required. Up to 128 characters. Matches goals and funnel steps by this name. |
url | string | Required. Must be an http(s) URL on your site's domain or a subdomain, otherwise the request is rejected with 403. The query string is stripped before storage. |
props | object | Up to 20 keys; values string or number, strings up to 256 characters. |
revenue | number | Any finite number. Negative values are allowed for refunds. |
ip | string | The visitor's IP. Used to resolve country and city and to derive the session, then discarded — never stored. |
ua | string | The visitor's User-Agent, used for browser, OS and device. |
country / city | string | Set these to skip the IP lookup. country is an ISO 3166 alpha-2 code in uppercase, e.g. US. |
session_id | string | Your own session identifier, 8–32 characters of [A-Za-z0-9_-]. Use it instead of IP + user agent grouping. |
timestamp | number | Unix seconds. Defaults to now. Must be within the last 2 years and no more than 5 minutes in the future. |
curl -X POST https://api.thunderstats.com/api/collect/event \\
-H "Authorization: Bearer ts_your_api_key" \\
-H "Content-Type: application/json" \\
-d '{
"name": "purchase",
"url": "https://yoursite.com/checkout/thanks",
"props": { "plan": "pro", "seats": 3 },
"revenue": 29.00,
"ip": "203.0.113.10",
"ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) Safari/605.1"
}'
How events join a visit
If you do not pass session_id, ThunderStats derives one by hashing the IP, user agent, site ID and current UTC date. Two requests with the same IP and user agent on the same day therefore land in the same session.
Send the visitor's IP and user agent, not your server's. If every event carries your server's details, every visitor collapses into one session. If you send nothing, each event becomes its own session — the event still counts, but it is not attributed to the visit that caused it.
Send a pageview
Records a pageview. Same authentication, same domain check, same session rules. Returns 202.
| Field | Type | Notes |
|---|---|---|
url | string | Required. The full URL of the page. UTM parameters are parsed from it and stored separately. |
referrer | string | The referring URL. Reduced to origin + path. |
ip, ua, country, city, session_id, timestamp | — | Identical to the custom event endpoint above. |
language | string | Visitor language, up to 64 characters. |
Send pageviews in bulk
Takes {"events": [ ... ]} with up to 100 pageview objects, each shaped exactly like the single endpoint. Returns 202 with the number accepted and the number rejected for a domain mismatch:
{ "ok": true, "accepted": 97, "rejected_url": 2, "ignored_bot": 1, "ignored_quota": 0 }
Invalid events are skipped individually rather than failing the batch. Events whose ua is a known crawler, HTTP client or monitoring tool are acknowledged but not stored, on every collect endpoint — the same filter the JavaScript snippet applies. The single-event endpoints answer {"ok": true, "ignored": "bot"} for those.
Every site has a daily event quota set by its plan (UTC day). Once it is reached, further events that day are acknowledged with 202 but not stored: "ignored": "quota" on the single-event endpoints, ignored_quota in the batch response. Do not retry them.
Reading your data
The same API key reads any per-site endpoint with GET. Every read takes ?site_id=, which must match the key's site.
Quota. Every request made with an API key counts toward the site's daily API quota (UTC day), except the collect endpoints, which count toward the event quota instead. The default depends on the plan; higher limits are available on request. Each response carries X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset (unix seconds). Once the quota is used up the API answers 429 with a Retry-After header until the next reset. Today's usage, per key, is shown under Settings → API Keys.
| Endpoint | Returns |
|---|---|
/api/stats/overview | Pageviews, visitors, bounce rate, pages per visit, with period-over-period change. |
/api/stats/all | Every overview widget in one request — pages, referrers, devices, countries, trend and more. |
/api/stats/pages | Top pages. |
/api/stats/referrers | Top referring domains. |
/api/stats/channels | Traffic grouped into Direct, Organic Search, Social, Paid, Email, Referral. |
/api/stats/countries, /cities, /devices, /browsers, /os | Audience breakdowns. |
/api/stats/trend | A time series for the selected period. |
/api/stats/realtime | Active visitors and what they are looking at now. |
/api/stats/sessions | Individual visitor sessions. |
/api/events | Custom event names with counts, visitors and revenue. |
/api/events/recent | Individual occurrences of one event, with properties and session IDs. Takes event_name and an optional limit (max 100). |
/api/events/summary | The top events for the period with their most common property values, in one request. Optional limit (max 10 events); up to 3 property keys per event and 4 values per key. |
/api/events/log | Every custom event in the period, newest first, with total. Optional event_name, free-text q (matches name, URL and properties), the segment filters (country, device_type, utm_source, …), limit (max 100) and offset (max 10,000). |
/api/events/prop-keys | Which property keys an event carries. Takes event_name. |
/api/events/props | Counts grouped by the value of one property. Takes event_name and prop_key. |
/api/goals/stats | Every goal with conversions and conversion rate. |
/api/funnels | Saved funnels and their results. |
Selecting a period
Pass period as one of today, 7d, 30d, 90d, 12mo, or custom. With custom, also pass date_from and date_to as YYYY-MM-DD. An unrecognised value falls back to 30d.
Filtering
Most read endpoints accept these filters, which combine with AND:
country, city, browser, os, device_type, referrer_domain, path, utm_source, utm_medium, utm_campaign, utm_content, utm_term
Comma-separate values for OR, and prefix with ! to negate:
# visitors from the US or Canada
?site_id=abc&period=7d&country=US,CA
# everything except bot-heavy referrers
?site_id=abc&period=30d&referrer_domain=!spam.example
# one city, mobile only
?site_id=abc&period=7d&city=Berlin&device_type=mobile
Rate limits
The collect endpoints allow a burst of 120 events and a sustained 20 events per second, per site. Pageviews and custom events share one budget. Over the limit you get 429; retry after a moment.
Errors
| Status | Meaning |
|---|---|
202 | Accepted. The event is queued for storage. |
400 | The payload failed validation. The error field says which field and why. |
401 | Missing, malformed, revoked or expired API key. |
403 | The url hostname does not belong to the key's site, or the key is not allowed on that endpoint. |
404 | Unknown site_id, or a site the key cannot reach. |
429 | Rate limited. |
Errors return JSON: {"error": "timestamp must be within the last 2 years..."}