Skip to content

Ask AI

Ask a question about Scribewren, such as uploading a file, editing a transcript, plans or the API.

Ctrl or Cmd+Enter to send

Answers are written by AI and can be wrong. For account or billing help, use the chat or contact us. Contact support (opens in a new tab)

Rate limits

Rate limits protect the service from bursts. They are separate from plan limits such as the number of jobs open at once, which return 403 PLAN_LIMIT_EXCEEDED. See Errors.

Each limit is a token bucket. The bucket holds up to burst requests and refills at a steady rate. You can spend a whole burst at once, then you are held to the rate. After a pause the bucket fills up again.

Every request passes through several limits in this order. The first one that is empty rejects the request.

  1. api_key_auth_ip: per IP address, before the key is checked.
  2. api_key: per API key.
  3. api_user: per account, shared by all of its keys.
  4. A limit for the route, if it has one: media_create, media_parts or export.

GET /plans and GET /languages need no key and use api_anon_ip per IP address instead.

Policy Counted per Rate Burst Applies to
api_key_auth_ip IP address 20 per second 60 Every request that carries a well-formed key
api_key API key 10 per second 40 Every keyed request
api_user Account 20 per second 100 Every keyed request
api_anon_ip IP address 5 per second 30 GET /plans, GET /languages
media_create Account 60 per minute 50 POST /media
media_parts Account 10 per second 40 POST and GET /media/{id}/parts
export Account 30 per minute 10 GET /transcriptions/{id}/export

IPv6 addresses are counted per /64 network, not per single address.

Because api_user is per account, several keys do not multiply your limit. Splitting work across keys helps you tell services apart, not go faster.

If the rate limiter itself is not available, the api_key_auth_ip check fails closed: the request gets 503 SERVICE_UNAVAILABLE instead of being let through. Retry with backoff.

Responses that pass through a limit carry two headers about the last limit they were checked against.

Header Meaning
RateLimit-Limit The size of that limit’s burst. It is not the refill rate.
RateLimit-Remaining How many requests are left in the bucket right now.

When a request is rejected, the response also has:

Header Meaning
Retry-After Whole seconds to wait before you try again. It is at least 1.
HTTP/1.1 429 Too Many Requests
Retry-After: 2
RateLimit-Limit: 40
RateLimit-Remaining: 0
Content-Type: application/json; charset=utf-8
{
"success": false,
"code": 429,
"message": "Too many requests. Please slow down.",
"data": null,
"error": {
"code": "RATE_LIMITED",
"details": { "policy": "api_key", "retryAfterSeconds": 2 }
},
"meta": {
"requestId": "019a4f30-1c22-7a10-b5d3-42f1c0a8e777",
"timestamp": "2026-12-01T09:41:07.512Z"
}
}

error.details.policy names the limit that rejected the request, and retryAfterSeconds repeats the Retry-After header.

  • Wait for Retry-After seconds after a 429. Then retry the same request.
  • Add random jitter to your waits when many workers share a key, so they do not retry at the same instant.
  • Do not poll a transcription in a tight loop. Poll every few seconds, or use webhooks and skip polling.
  • Upload parts in a small number of parallel streams. Request presigned URLs just before you use them, in batches of up to 100 part numbers, instead of one call per part.
  • Read segments with a large limit and includeWords=false when you only need text. See Segments.
  • Cache GET /plans and GET /languages. They change rarely.

Can't find what you need? Contact support, email [email protected], or open Help and then Contact in the app. scribewren.com