Docs

Rate limits

Partners endpoints rate-limit at 100 requests per minute per API key. Every response carries headers describing the current quota state; 429 responses include Retry-After.

Limits

Endpoint groupDefault
/v1/partners/*100 requests / minute / API key
/v1/surveys60 requests / minute (when shipped)
/v1/uploads/presign60 requests / minute (when shipped)
/v1/health, /v1/openapiNot rate-limited

Implemented as a Postgres-backed sliding window with 1-second resolution. Rate is per API key — multiple tokens for the same organization have independent quotas.

Response headers

Every Partners-tagged response (success and error) carries:

HeaderMeaning
X-RateLimit-LimitRequests allowed per window (e.g. 100).
X-RateLimit-RemainingRequests remaining in the current window.
X-RateLimit-ResetUnix-seconds when the current window resets.
X-Request-IdCorrelation id for support; matches error.request_id.
Retry-AfterSeconds to wait before retrying. 429 only.

Handling 429

When you exceed the limit, you get:

HTTP/1.1 429 Too Many Requests
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1715040060
Retry-After: 42

{
  "error": {
    "type": "rate_limit_error",
    "code": "RATE_LIMITED",
    "message": "Rate limit exceeded for this API key.",
    "doc_url": "https://developer.tendralhealth.com/docs/errors#RATE_LIMITED",
    "request_id": "req_018f5a2e7c127c8da123cafed010001"
  }
}

Honor Retry-After. Don't retry tighter — that just keeps the limit pegged. A simple, correct client recipe:

async function callWithRateLimit(url, headers) {
  for (let attempt = 0; attempt < 5; attempt++) {
    const r = await fetch(url, { headers })
    if (r.status !== 429) return r
    const retryAfter = parseInt(r.headers.get('retry-after') ?? '60', 10)
    await sleep(retryAfter * 1000 + Math.random() * 1000)
  }
  throw new Error('Exceeded retry budget')
}

Need a higher limit?

100/min is the default. If a legitimate use case needs more, talk to your Tendral contact — limits are per-key and we can raise them on request.