Skip to main content
Errors from /api/preview/* use one JSON shape, whatever the endpoint:
Handle errors by code, and treat a code you don’t recognize by its HTTP status and retryable flag: new codes may be added during the preview. A few responses carry no JSON body: a path that doesn’t exist (404), a method a route doesn’t support (405, for example DELETE on an Instagram rule), and, rarely, a 5xx when authentication or rate limiting fails unexpectedly. Treat any 5xx whose body you can’t parse as retryable.

Retrying

  • retryable: true: wait and send the same request again. Use retryAfterSeconds (or the Retry-After header on a 429) when present; otherwise back off exponentially with jitter, starting at a second or two. Cap it at about five attempts, never retry in a tight loop, and wait at least a second even when retryAfterSeconds is 0.
  • retryable: false: the same request will fail the same way. Fix the input, re-read the resource, or ask a person to act in the dashboard, as the tables below say.
  • Creates with an Idempotency-Key (POST /api/preview/documents, POST /api/preview/instagram/rules, POST /api/preview/segments): a retryable failure the create reports itself (for example a retryable NOTION_FETCH_FAILED or INSTAGRAM_UNAVAILABLE) releases the key, so retry with the same key and body. A non-retryable failure is stored against the key: resending the same key and body returns the same error again. After fixing the request, send it with a new key (reusing the old key with a different body is 409 IDEMPOTENCY_KEY_CONFLICT).
  • A create that fails with 500 INTERNAL_ERROR may keep holding its key for up to a minute. Retry with the same key and body. While the earlier attempt still holds the key you get 409 IDEMPOTENCY_KEY_IN_PROGRESS: wait retryAfterSeconds (3) between tries. It clears within about a minute, after which the retry runs as a fresh request; stop and investigate if it hasn’t cleared after two minutes.

Rate limits

Each account can make 60 requests per minute across all /api/preview/* routes, however many keys it has. Over the limit, the API answers 429 RATE_LIMITED with these headers: These headers are sent only on the 429. On the idempotent creates, a replay of a key that was already used, a conflict and an in-progress answer don’t count toward the limit.

Requests running at once

An account can also have at most 10 requests running at the same moment across all /api/preview/* routes, however many keys it has. The eleventh is refused with 429 TOO_MANY_CONCURRENT_REQUESTS and Retry-After: 1, and none of the X-RateLimit-* headers. It counts requests that have not finished, so it can apply while you are well under 60 requests per minute: send requests one after another, or a few at a time, instead of all at once. A refused request did nothing, so it is safe to retry as is, but wait the Retry-After pause first: the refused request still counts toward the 60 requests per minute, except on the idempotent creates, which spend that budget only once a request is accepted.

Error codes

Authentication and access

These can come from any endpoint.

Rate limits and availability

The API can also answer 502 with BATCH_SCHEDULE_FAILED or INSTAGRAM_UNAVAILABLE (see Instagram); both are retryable.

Request format

Idempotency

POST /api/preview/documents, POST /api/preview/instagram/rules and POST /api/preview/segments require an Idempotency-Key header. POST /api/preview/segments/preview does not take one: it saves nothing, so repeating it is always safe.

Documents

Contacts

Groups

A group rule is checked the same way by POST /segments and POST /segments/preview. A refusal here never created or changed anything. The rule format is on Group conditions.

Email

Instagram

Instagram write errors that fall into none of these codes are 500 INTERNAL_ERROR (retryable).