API reference
Read published daily sessions
POST /v1/query
Returns published daily sessions for one connection and a window of at most 31 days, with full provenance.
The window is not gated on UTC: a “through yesterday” query from a
UTC+2 property just after local midnight is answered rather than refused.
What gates the answer is publication — every date in the window must have a
published head. If some do, the response is 409 data_incomplete and names
the missing dates; if none do, 409 data_not_ready.
Authorization is checked before the read and again before the response is
released, so a key revoked while ClickHouse is in flight yields 401 and no
data. The workspaceId in the body must equal the key’s own workspace;
a mismatch is 403, never a cross-tenant read. Workspace headers are ignored.
Authorizations
Section titled “Authorizations ”Request Body required
Section titled “Request Body required ”The query request body. metric and dimensions are fixed in this version.
There is no filter, no segment and no free-form SQL: the query surface cannot
express anything the published layer cannot answer.
object
Must equal the API key’s own workspace.
GA4 numeric property id, as a string.
Calendar date in the property’s reporting timezone.
Inclusive. The closed interval must contain 1–31 days. Publication gates query availability; there is no independent UTC-today check at the query boundary.
Example
{ "workspaceId": "your_workspace", "connectionId": "your_connection", "propertyId": "123456789", "startDate": "2026-08-31", "endDate": "2026-09-04", "metric": "ga4.sessions", "dimensions": [ "date" ]}Responses
Section titled “ Responses ”Every requested day has a complete published snapshot.
object
One row per requested date, in ascending date order, with no gaps.
object
zero when the window totals zero, observed otherwise. It is never how
missing or unavailable data is represented — that is what the 409 codes
are for.
object
IANA timezone of the property, e.g. Europe/Oslo.
The canonical definition, embedded in every result.
object
Dates are the property’s calendar dates, never UTC-shifted.
object
object
Always complete in a 200 — an incomplete window is a 409, so this
field can be relied on rather than checked.
The product does not re-derive these numbers from a second source. Stated on every result rather than assumed.
object
True when the oldest snapshot is older than thresholdHours.
Always includes provider_may_revise_recent_data and
not_independently_reconciled; adds stale_source_snapshot when
freshness.stale is true. Surface these wherever the number is shown.
The exact snapshot each day came from. Two results citing the same
runId, extractionSeq and extractedAt for a date are reading the
same publication — which is how a spreadsheet, a REST caller and an
MCP client are shown to agree on more than a matching total. The
source timestamp is selected from the head’s matching immutable sync
run and must agree with every returned ClickHouse row; disagreement
fails closed rather than returning provenance that cannot be vouched
for.
object
Immutable extraction time from the matching source sync run, validated against analytics rows.
invalid_json — the body is absent, not valid JSON, or not valid UTF-8.
invalid_query — the body is not a supported metric query (unknown field,
malformed date, window outside 1–31 days, a metric or dimension other than
ga4.sessions by date), or the URL carried a query delimiter,
including a bare ?.
object
object
object
unauthorized — missing, unknown, expired or revoked key.
object
object
object
Headers
Section titled “Headers ”forbidden — the body’s workspaceId is not the key’s workspace, or the
key lacks query:read.
object
object
object
connection_unavailable — no active connection matches this
workspace, connection and property, or it was disconnected mid-read.
object
object
object
method_not_allowed — Allow names the method this route accepts.
object
object
object
Headers
Section titled “Headers ”data_not_ready when nothing in the
window is published, data_incomplete when some days are; both name the
missing dates so a caller can narrow the window or wait. timezone_conflict
means published dates use different property timezones: re-sync every
requested date using the property’s current timezone. No analytics read
occurs for that conflict, and it does not include missingDates.
The two missing-publication 409 forms; timezone_conflict has no missingDates. This is a separate closed object because composing Error with allOf would reject missingDates under Error.properties.error.additionalProperties.
object
object
object
object
Examples
{ "error": { "code": "data_incomplete", "message": "Complete data is not available for every requested day.", "missingDates": [ "2026-09-03", "2026-09-04" ] }}{ "error": { "code": "timezone_conflict", "message": "Published data uses different property timezones. Re-sync every requested date using the property's current timezone." }}request_too_large — REST bodies are capped at 4 KiB.
object
object
object
unsupported_media_type unless the body is application/json, or
unsupported_encoding for any Content-Encoding other than identity.
object
object
object
query_unavailable — a backend is unreachable, or a stored row failed an
integrity check. The boundary fails closed rather than returning a result
it cannot vouch for, and never passes a database or provider message
through.
auth_unavailable — the authorization store could not be reached. Pilot
operations report this condition as automation_unavailable.