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:

Header
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

POST/api/collect/event

Records a conversion or any other named action. Returns 202 {"ok": true}.

FieldTypeNotes
namestringRequired. Up to 128 characters. Matches goals and funnel steps by this name.
urlstringRequired. 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.
propsobjectUp to 20 keys; values string or number, strings up to 256 characters.
revenuenumberAny finite number. Negative values are allowed for refunds.
ipstringThe visitor's IP. Used to resolve country and city and to derive the session, then discarded — never stored.
uastringThe visitor's User-Agent, used for browser, OS and device.
country / citystringSet these to skip the IP lookup. country is an ISO 3166 alpha-2 code in uppercase, e.g. US.
session_idstringYour own session identifier, 8–32 characters of [A-Za-z0-9_-]. Use it instead of IP + user agent grouping.
timestampnumberUnix seconds. Defaults to now. Must be within the last 2 years and no more than 5 minutes in the future.
curl
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.

This is the thing integrations get wrong

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

POST/api/collect

Records a pageview. Same authentication, same domain check, same session rules. Returns 202.

FieldTypeNotes
urlstringRequired. The full URL of the page. UTM parameters are parsed from it and stored separately.
referrerstringThe referring URL. Reduced to origin + path.
ip, ua, country, city, session_id, timestampIdentical to the custom event endpoint above.
languagestringVisitor language, up to 64 characters.

Send pageviews in bulk

POST/api/collect/batch

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:

Response
{ "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.

EndpointReturns
/api/stats/overviewPageviews, visitors, bounce rate, pages per visit, with period-over-period change.
/api/stats/allEvery overview widget in one request — pages, referrers, devices, countries, trend and more.
/api/stats/pagesTop pages.
/api/stats/referrersTop referring domains.
/api/stats/channelsTraffic grouped into Direct, Organic Search, Social, Paid, Email, Referral.
/api/stats/countries, /cities, /devices, /browsers, /osAudience breakdowns.
/api/stats/trendA time series for the selected period.
/api/stats/realtimeActive visitors and what they are looking at now.
/api/stats/sessionsIndividual visitor sessions.
/api/eventsCustom event names with counts, visitors and revenue.
/api/events/recentIndividual occurrences of one event, with properties and session IDs. Takes event_name and an optional limit (max 100).
/api/events/summaryThe 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/logEvery 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-keysWhich property keys an event carries. Takes event_name.
/api/events/propsCounts grouped by the value of one property. Takes event_name and prop_key.
/api/goals/statsEvery goal with conversions and conversion rate.
/api/funnelsSaved 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:

Examples
# 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

StatusMeaning
202Accepted. The event is queued for storage.
400The payload failed validation. The error field says which field and why.
401Missing, malformed, revoked or expired API key.
403The url hostname does not belong to the key's site, or the key is not allowed on that endpoint.
404Unknown site_id, or a site the key cannot reach.
429Rate limited.

Errors return JSON: {"error": "timestamp must be within the last 2 years..."}

Back to the docs index →