Skip to content

Core concepts

Data freshness

Every number this API returns comes from ingested platform data, and ingestion can fail. /v1/sync is how you find out before your users do.

The failure this prevents

A connection breaks on a Tuesday. Nothing errors: the API keeps answering, the rows keep coming back, and every one of them is Monday. A dashboard reads it as spend falling off a cliff, somebody investigates a campaign that is fine, and the actual problem is an expired token.

The data to catch that exists and is one call away. An integration that reads it turns a silent wrong answer into a labelled partial one.

Reading sync state

Request
curl "https://api.cresva.ai/v1/sync?brand_id=brd_8Kq2mR4xVn" \  -H "Authorization: Bearer $CRESVA_API_KEY"
200
{  "brand_id": "brd_8Kq2mR4xVn",  "data": [    {      "platform": "meta",      "last_attempt_at": "2026-07-31T06:00:00Z",      "last_successful_sync_at": "2026-07-31T06:00:00Z",      "data_fresh_as_of": "2026-07-30",      "consecutive_failures": 0,      "status": "ok",      "last_error_code": null    },    {      "platform": "google",      "last_attempt_at": "2026-07-31T06:00:00Z",      "last_successful_sync_at": "2026-07-28T06:00:00Z",      "data_fresh_as_of": "2026-07-27",      "consecutive_failures": 3,      "status": "failing",      "last_error_code": "invalid_grant"    }  ],  "meta": { "request_id": "req_01JQ8Z3M6WT4", "simulated": false }}

Illustrative values. Google is shown failing on purpose: this is what a broken connection looks like.

What each field tells you

status
ok, failing or never_synced. The last one means the platform is connected but has never delivered, which is a different problem from one that broke.
data_fresh_as_of
The most recent DATE the data covers, which is the field to compare against the end of the window you are querying. It lags the sync time because platforms finalise a day after it closes.
last_successful_sync_at
When we last got data. Compare with last_attempt_at: a recent attempt and an old success is a connection that is trying and failing.
consecutive_failures
How long it has been failing. One is a blip, and a run of them is a credential or permission problem that needs a person.
last_error_code
The platform’s own error, passed through unrenamed. invalid_grant means the OAuth token needs reconnecting.

Guarding a read

Compare the freshness date against the end of the range you asked for. If it is behind, say so beside the number rather than suppressing the number: partial data is still useful once it is labelled.

TypeScript
const { data: platforms } = await cresva.v1.getSyncState({ brand_id }); const stale = platforms.filter(  (p) => p.status !== "ok" || p.data_fresh_as_of < requestedEndDate,); if (stale.length) {  // Render the number AND the caveat. Suppressing the number entirely is  // usually wrong: partial data is still data, as long as it is labelled.  banner(`${stale.map((p) => p.platform).join(", ")} last delivered ${stale[0].data_fresh_as_of}`);}

Cheap polling

  • /v1/sync is the cheapest call in the API. Poll it, and re-read metrics only when data_fresh_as_of has moved.
  • That is also the answer to most rate-limit problems: an integration that re-reads a month of metrics every five minutes is asking the same question of unchanged data.

Forecasts and recommendations have their own answer

Those are produced on a schedule from accumulated history. When there is not enough yet, they return 409 data_not_ready rather than a flat line. See Errors.