Skip to content

Core concepts

Requests and responses

What is true of every endpoint: one base URL, one version, one date format, one response envelope.

Base URL and version

Everything lives under one origin and one version segment. There is no regional endpoint to choose and no beta host.

Base URL
https://api.cresva.ai/v1

The version is in the path rather than a header, so a URL in a log or a bug report says which contract it was speaking without anybody having to ask.

Methods

8 endpoints are GET and 2 are POST. Neither POST writes anything: they take a body because their input does not fit comfortably in a query string, not because they change state.

  • /v1/query, ask in plain language.
  • /v1/recommendations, budget reallocation under constraints.
POST
curl -X POST "https://api.cresva.ai/v1/query" \  -H "Authorization: Bearer $CRESVA_API_KEY" \  -H "Content-Type: application/json" \  -d '{"question":"What was my Meta ROAS last week?","brand_id":"brd_8Kq2mR4xVn"}'

Illustrative values.

Nothing here changes a campaign

This API reads. Budget recommendations are returned as recommendations; applying one is an action you take in the product or on the platform. There is no endpoint that can pause an ad or move a budget.

Formats

Dates
YYYY-MM-DD, and both ends of a range are inclusive. A request for 2026-07-01 to 2026-07-31 covers all 31 days.
Timestamps
ISO 8601 in UTC, for example 2026-07-28T04:00:12Z. Reporting dates follow the ad account’s own timezone, which /v1/brands returns.
Numbers
JSON numbers, not strings. Money is in the brand’s currency, which /v1/brands returns; the API does not convert between currencies.
Query values
Coerced. A query string is always text, so limit=50 arrives as a string and is parsed into a number by the schema. Sending it as a number in a body is equally fine.
Nulls
Meaningful. A null metric is a metric that could not be computed, which is different from zero. Fields that can be null are marked in each endpoint’s response table.

The meta object

Every successful response carries one. Two fields, both worth reading.

200
{  "data": [ ... ],  "meta": {    "request_id": "req_01JQ8Z3M6WT4",    "simulated": false  }}
request_id
Identifies this call in our logs. Quote it when reporting a problem.
simulated
True when the request used a test key and the rows are fabricated. Worth asserting in your own tests: it is how you catch a staging deploy that is quietly pointed at a live key.

Result size

Endpoints that return lists take a limit with a documented maximum, shown in each endpoint’s parameter table. There is no cursor and no page token: these are analytical reads over a date range you choose, so the way to get less is to ask for a narrower window or a higher level of aggregation.

Aggregate before you paginate

Asking for level=ad across a quarter and then paging is usually the wrong shape. Ask at level=campaign first, find the campaign you care about, and go down a level only there.