Data API
Read event analytics (summary, daily, and trend) and check BigQuery export status for a location or agency through the headless Data API.
Overview
The Data API is a read-only surface for agencies that want to pull Mythic Analytics event data into their own systems. It serves aggregated event analytics — summaries, daily counts, and volume trends — plus the provisioning status of a BigQuery export.
Every endpoint is a GET except POST /query, whose body is a query spec. CORS is open for GET, POST and OPTIONS, which is what lets the Builder embed's own pickers read events and property values — but keys remain server-side credentials: treat one that reaches a browser you don't control as compromised.
Resolved person profiles and the identity graph are documented in the People & Identity section of this tab, which shares this base URL and authentication.
Base URL
All endpoints share the same base URL.
https://mythic-analytics.gulp.workers.dev/client/v1/data
Location scoping
Event data is isolated per location. How the location is determined depends on the key type:
| Key type | Prefix | How the location is set |
|---|---|---|
| Agency key | ak_ | You bind a location per request via location_id query or X-Location-Id header |
| Location secret key | sk_ | Resolved automatically from the key itself |
The event routes are location-scoped. The /export route is agency-wide and requires an agency key. See Authentication for details.
Endpoints
Explore queries
Trends and funnels over any event × property in one call — a typed spec, compiled server-side, sub-second. See the Explore page.
Event summary
Aggregated event counts by type over an optional date range.
Daily events
Per-day event counts, optionally filtered to a single event type.
Event trend
Event volume over an explicit range or a rolling window of days.
Export
BigQuery export dataset status for the agency.
Session replays
Recorded browser sessions (rrweb) with activity counters, playable in your own player.
Error tracking
Uncaught JS errors grouped into issues by a stable fingerprint, with volume, occurrences, and parsed stack traces.
Property discovery
Every event and person property key observed for the location, with value types and masked sample values — so destination field mapping is a pick-list, not a guess.
Explainability coverage
Whether a page's drop-off can be explained from the data at all — call it before you analyse, especially from an AI agent.
Before you explain a drop-off
GET /explain/coverage?page=/pricing answers one question: is there enough data
here to support a conclusion? It returns no analysis. Call it first — and if you
are driving this API from an AI agent, hand the response through verbatim rather
than summarising it.
The reason is that the dangerous output is not a bad number, it is a number that should never have been computed. On a real paid landing page with 4,225 non-bot sessions over 14 days, zero of them fired the conversion event — because the page carries no form. Its step is a click-through to the next page. A conversion rate there reads 0.00%, and the obvious recommendation ("rewrite this page") is wrong.
outcome_event_never_fires is a separate verdict from a low conversion rate.
Missing instrumentation and a failing page demand opposite responses, so the
gate never collapses them into one number. Omit outcome_event and "advanced"
means the session saw a second page — the right model for a bridge page.
Three evidence arms are reported, always all three, so you can distinguish
"checked and unavailable" from "not checked". An arm is available only when both
sides of its contrast hold at least min_arm_n sessions.
available means computable, not powered. Every available arm reports
mde_pp: the smallest difference in outcome rate it could actually detect, in
percentage points. At the bare min_arm_n threshold that exceeds 30pp — larger
than almost any real conversion effect. On the reference page the error arm
reports ~6.7pp while the effect present was ~8pp, which is genuinely marginal.
Compare mde_pp to the effect you care about before you believe a contrast.
| Arm | What it contrasts | Confound |
|---|---|---|
error_outcome | Sessions that threw a JS error on the page vs those that did not | exposure |
field_abandon | Which form field the visitor last touched before leaving | none |
scroll_contrast | How deep advancers read vs non-advancers | reverse_causality |
The confound field is load-bearing, not a footnote. On that same page, the 190
sessions that threw a JavaScript error bounced less than clean ones (80.1%
vs 88.1%) and reached the next step three times more often. You have to engage
with a page to trigger an error, so the arm measures exposure, not harm. Read
causally it claims that JavaScript errors improve conversion. Hold engagement
depth fixed before you interpret it — or do not report it.
Counts are page-scoped and order-scoped. An exception thrown two pages later is evidence about that step, not this one — and "advanced" requires a page after this one, not just a second page somewhere in the session. On the reference page 17 of 521 sessions saw their other page only before it: a 3% error on a landing page, approaching 100% on a mid-funnel page where every visitor arrives from somewhere else. Bots are excluded, which matters more than it sounds — unfiltered, that page reads 4,834 sessions instead of 4,225, and the whole 12.6% inflation lands in the denominator.
When answerable is true but every arm with coverage carries a confound, a
top-level caveat of only_confounded_arms_available says so. Branch on that
field rather than inferring it from the arm list.
curl -s -H "Authorization: Bearer $MYTHIC_KEY" \
-H "X-Location-Id: $LOCATION_ID" \
"https://mythic-analytics.gulp.workers.dev/client/v1/data/explain/coverage?page=/pricing&outcome_event=form_submitted"
{
"success": true,
"data": {
"page": "/pricing",
"window_days": 14,
"answerable": false,
"reason": "outcome_event_never_fires",
"hint": "No session on /pricing fired "form_submitted" in 14d. This is missing instrumentation or the wrong step definition, NOT a 0% conversion rate — omit outcome_event to measure page-transition instead.",
"outcome": {
"kind": "event",
"event": "form_submitted",
"n_positive": 0,
"n_total": 4225
},
"arms": [
{
"id": "error_outcome",
"available": true,
"n": 190,
"mde_pp": 6.7,
"confound": "exposure",
"confound_note": "Throwing a client-side error requires engaging with the page, so this arm measures exposure. Hold dwell/interaction depth fixed before reading it."
},
{ "id": "field_abandon", "available": false, "n": 0, "reason": "no_form_on_page" },
{
"id": "scroll_contrast",
"available": false,
"n": 7,
"coverage": 0.0017,
"reason": "too_few_scroll_sessions",
"hint": "Scroll capture is opt-in per location — enable capture_heatmaps for this client and wait for traffic."
}
],
"thresholds": { "min_arm_n": 30, "min_scroll_coverage": 0.05 }
}
}
A false here is a useful answer, not an error. It tells you which pages to turn
heatmap capture on for, and which conversion events are
missing from a funnel you thought you were measuring.
Response envelope
Every response is a JSON envelope with a success flag.
Whether the request succeeded.
Resource payload. Event endpoints return an array of rows; /export returns an object.
Number of rows returned. Present on the event endpoints.
Present only on failed requests. Contains a machine-readable code and a human-readable message.
Example error response:
{
"success": false,
"error": {
"code": "unauthorized",
"message": "Missing authorization"
}
}
Rate limiting
Requests are limited to 120 per 10 seconds per location. When you exceed the limit you receive a 429 with a Retry-After header. Successful responses also carry X-RateLimit-Limit and X-RateLimit-Remaining headers.
Error handling
| Status | Code | Meaning |
|---|---|---|
400 | location_required | An agency key called an event route without binding a location |
401 | unauthorized | Missing, malformed, or invalid key |
402 | feature_not_in_plan | the Data API is not included in this account's plan |
403 | location_not_linked | The requested location is not linked to the agency |
403 | hipaa_restricted | The location has HIPAA mode enabled; the Data API is unavailable |
403 | agency_required | /export was called without an agency (ak_) key |
404 | not_provisioned | No BigQuery export provisioned for the agency |
405 | method_not_allowed | Only GET is supported |
429 | rate_limited | Too many requests |
502 | upstream_error | An upstream data service failed |
503 | not_configured | The analytics service is not configured |
Check both the HTTP status code and the error.code field for programmatic error handling.
Locations with HIPAA mode enabled are blocked from the Data API entirely and return 403 hipaa_restricted.