Skip to content

Rate limits & idempotency

LimitApplies to
60 requests per minuteEach API key, across all endpoints.
10 report starts per hourEach API key, on POST /v1/reports, on top of the per-minute limit.
Plan limitsStarts on your own provider keys are also bounded by the Agent plan: brands, reports per billing period, reports in any 24 hours, and reports at once.

Rate-limit windows are fixed: a per-minute budget resets at the start of each minute. Requests without a valid key (public rankings, a missing or mistyped key) are limited per client address instead.

There are two kinds of 429. Tell them apart by the limit field.

Over the per-minute or per-hour budget, you get 429 with a JSON error, retryAfterSeconds in the body, and these headers:

HeaderMeaning
Retry-AfterSeconds to wait before retrying.
RateLimit-LimitRequests allowed in the window.
RateLimit-RemainingRequests left in the window (0 on a 429).
RateLimit-ResetSeconds until the window resets.

Wait Retry-After seconds, then retry.

A start refused by an Agent plan limit is a 429 whose body names the limit in limit. It never carries RateLimit-* headers. Whether it says when to retry depends on the limit:

limitRetry-After and retryAfterSecondsWhen it lifts
perDayYesWhen the oldest start in the last 24 hours turns 24 hours old.
reportsPerMonth, projectsYes, when the billing period’s end is knownAt the next billing period.
concurrentNeverWhen one of your running reports finishes. Poll its status, then start again.

Without an active Agent plan, a start is refused with 402 and limit: "plan", also with no retry time. Don’t retry plan limits in a loop. Nothing was queued and nothing was charged.

A report takes several minutes. Poll GET /v1/reports/{slug}/status about once a minute; faster polling only spends your budget. Honour Retry-After and back off on repeated 429s.

Starting a report spends money, and networks fail. Send an idempotency key with every start so a retry can never spend twice:

Terminal window
curl -X POST https://api.recomention.com/v1/reports \
-H "Authorization: Bearer $RECOMENTION_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 9f1c2e7a4b6d8e0f1a2b3c4d" \
-d '{"domain":"example.com","category":"meeting scheduling","description":"Booking links for sales teams","plan":"FULL","funding":"credits"}'
  • The key is 16–128 characters of letters, digits, - and _. Generate a random one per start (for example openssl rand -hex 16).
  • Send it as the Idempotency-Key header or as idempotencyKey in the body. If you send both, they must match.
  • Same key, same body → the first start. The response is 200 with reused: true and the original slug. Nothing new is spent.
  • A key identifies one start together with its body. The same key with a different body is a different start. Reuse a key only to retry the same request; a new report needs a new key.
  • providerKeys are not part of that fingerprint. Retrying a BYOK start with the same idempotency key and corrected provider keys returns the first report, which ran (or failed) with the old keys. To run again with different keys, use a new idempotency key.
  • If the first start’s credit was returned — it could not be queued, or it ran, failed and was refunded — a retry with the same key gets 409 instead of a replay. Send a new start with a new key.
  • Keys are scoped to your account. The same string sent from another account is that account’s own, separate request.
  • Without a key, every request is a new start.

After a 503, a timeout or a dropped connection, the outcome of a start is unknown. Retry with the same key, or check GET /v1/reports. Never retry with a new key, which could start a second report. See Errors.