Prepare and reuse a stored GA4 report through the regular async worker
/sites/{site_id}/ga4/reports/{report_type}analytics:read, analytics:prepareRequires analytics:read and site access. Accepts the same parameters as GET in a JSON object or in the query string, exclusively. source must be stored. Existing fresh reports return immediately; missing reports reuse or enqueue collection. Repeat GET with the same dates after completion. This does not change scheduled collection or backfill.
| Request | POST https://satelliteai.io/api/v1/sites/{site_id}/ga4/reports/{report_type} |
| Permission | analytics:read, analytics:prepare |
| Response | A single object: data is Ga4Report, or null when nothing is stored. |
| Operation ID | prepareGa4Report |
More context and worked examples are in the developer guide.
Example request
curl --fail-with-body --request POST \
'https://satelliteai.io/api/v1/sites/123/ga4/reports/sessions_over_time' \
-H 'Content-Type: application/json' \
--data '{"start_date":"2026-01-01","end_date":"2026-10-25","limit":100}' \
-H "Authorization: Bearer $SATELLITE_API_KEY"Replace the example IDs with values returned by the discovery endpoints. Example values are placeholders, not real records.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
site_idrequired | path | integer | ID from GET /sites. min 1 |
report_typerequired | path | string |
Unknown or repeated query parameters return 400.
JSON request body
Send Content-Type: application/json. The request body is optional. Fields below describe the request; response fields follow separately.
| Field | Type | Description |
|---|---|---|
source | string | one of: stored |
start_date | string (date) | |
end_date | string (date) | |
days | integer | min 1 · max 365 |
limit | integer | Maximum records per page applied to this request. min 1 · max 100 |
offset | integer | min 0 · max 100000 |
report_set_id | integer (int64) | min 1 |
Data dictionary
Stored report ready, or partial daily data with coverage metadata. Stored reports come from the canonical GA4 store. Missing intervals return a pending object; available reports can have an empty rows array. Use report_set_id to pin exact-range pages. Daily history and live Google results can change during pagination.
202: Collection pending. Response uses Ga4Report and Ga4Meta, including report_metadata.collection.jobs when work is queued.
[] marks an array element. nullable means the value can be null; treat it as unknown, not zero. always present means the key is always included in the object.
| Field | Type | Description |
|---|---|---|
data.site_id | integeralways present | Satellite site ID (from GET /sites). min 1 |
data.report_type | stringalways present | |
data.source | stringalways present | one of: stored, live |
data.date_range | objectalways present | |
data.date_range.start_date | string (date)nullablealways present | |
data.date_range.end_date | string (date)nullablealways present | |
data.snapshot_date | string (date)nullablealways present | Null; canonical reports use explicit intervals and optional report_set_id. |
data.fetched_at | string (date-time)nullablealways present | UTC publication or live fetch timestamp; null when no rows have been published. |
data.stale | booleanalways present | True when the store identifies a pinned report as stale. False for live requests. |
data.rows | array of map of stringalways present | |
data.row_count | integeralways present | Total matching published rows for stored; Google's matching rowCount for live. Consult coverage for missing dates. min 0 |
data.row_count_is_total | booleanalways present | True for the stored matching row count and live Google rowCount. Incomplete daily coverage does not imply complete interval data; consult state and missing_days. |
data.report_metadata | objectnullablealways present | Allowlisted stored provenance/coverage or Google quality fields. Unrecorded metadata is unknown. |
data.report_metadata.state | string | one of: ready, pending, partial, unsupported, empty_period |
data.report_metadata.source | string | |
data.report_metadata.dataset | string | |
data.report_metadata.definition_version | integer | |
data.report_metadata.binding_id | integer | |
data.report_metadata.connection_revision | integer | |
data.report_metadata.timezone | string | |
data.report_metadata.start | string (date) | |
data.report_metadata.end | string (date) | |
data.report_metadata.currency_code | stringnullable | |
data.report_metadata.report_set_id | integernullable | |
data.report_metadata.report_state | stringnullable | |
data.report_metadata.publication_watermark | stringnullable | |
data.report_metadata.aggregation | string | |
data.report_metadata.missing_days | integer | |
data.report_metadata.reason | string | |
data.report_metadata.quality | array of object | |
data.report_metadata.collection | object | |
data.report_metadata.collection.state | string | |
data.report_metadata.collection.jobs | array of object | |
data.report_metadata.collection.jobs[].job_id | string | |
data.report_metadata.collection.jobs[].status | string | |
data.report_metadata.collection.jobs[].status_url | string | |
data.report_metadata.dataLossFromOtherRow | boolean | |
data.report_metadata.subjectToThresholding | boolean | |
data.report_metadata.emptyReason | string | |
data.report_metadata.currencyCode | string | |
data.report_metadata.timeZone | string | |
data.report_metadata.samplingMetadatas | array of object | |
data.report_metadata.samplingMetadatas[].samplesReadCount | string | |
data.report_metadata.samplingMetadatas[].samplingSpaceSize | string |
Response metadata
| Field | Type | Description |
|---|---|---|
meta.request_id | string | Request identifier; also returned in X-Request-ID. Include it in support requests. |
meta.api_version | string | API version that produced the response. one of: v1 |
meta.limit | integeralways present | Maximum records per page applied to this request. min 1 · max 100 |
meta.has_more | booleanalways present | Another page is available within the API offset bound. |
meta.next_cursor | stringnullable | Opaque cursor for the next page; send as cursor. Null on the last page. |
meta.availability | stringalways present | available when stored data exists; not_available when it does not. one of: available, not_available |
meta.billing | object | Present for paid live SERP requests; replayed receipts describe the original debit, not an additional charge. |
meta.billing.operation | stringalways present | one of: client_api_serp_refresh |
meta.billing.credits_charged | integeralways present | min 0 |
meta.billing.transaction_id | integeralways present | |
meta.billing.replayed | booleanalways present | |
meta.state | string | one of: ready, pending, partial, unsupported, empty_period |
meta.offset | integeralways present | min 0 · max 100000 |
meta.next_offset | integernullablealways present | Next allowed row offset, or null when traversal stops. min 0 · max 100000 |
meta.offset_limit_reached | booleanalways present | More rows remain beyond the API offset cap; narrow the date range. |
Error responses
Errors return {"error": {"code", "message", "request_id"}}. See error handling for retry guidance.
| HTTP | When |
|---|---|
400 | Invalid parameters, incompatible date selectors, or unavailable report identity. |
403 | Missing analytics:read or disabled integration. |
404 | Missing or inaccessible site. |
429 | Request or collection quota exceeded; honor Retry-After. |
503 | Store or queue unavailable. |