{"record":{"id":"b3d7c8834dc20163","repo":"PaddlePaddle/PaddleOCR","slug":"rate-limit-exceeded-text","errorCode":null,"errorMessage":"Rate limit exceeded: ${text}","messagePattern":"Rate limit exceeded: (.+?)","errorType":"exception","errorClass":"RateLimitError","httpStatus":429,"severity":"warning","filePath":"api_sdk/typescript/src/internal/http.ts","lineNumber":241,"sourceCode":"      clearTimeout(timeoutID);\n      signal?.removeEventListener(\"abort\", abort);\n    }\n\n    if (resp.ok) return resp;\n\n    let text = await resp.text();\n    try {\n      const payload = JSON.parse(text) as { msg?: string; message?: string; errorMsg?: string };\n      text = payload.msg || payload.message || payload.errorMsg || text;\n    } catch {\n      // Keep raw response text.\n    }\n    if (resp.status === 401 || resp.status === 403) {\n      throw new AuthError(`Authentication failed: ${text}`);\n    } else if (resp.status === 400) {\n      throw new InvalidRequestError(`Bad request: ${text}`);\n    } else if (resp.status === 429) {\n      throw new RateLimitError(`Rate limit exceeded: ${text}`);\n    } else if (resp.status === 503 || resp.status === 504) {\n      throw new ServiceUnavailableError(resp.status, `Service unavailable: ${text}`);\n    } else {\n      throw new APIError(resp.status, text);\n    }\n  }\n}\n\nfunction requireJobId(data: SubmitResponse): string {\n  if (!data || typeof data.jobId !== \"string\" || data.jobId.length === 0) {\n    throw new ResponseFormatError(\"Submit response is missing jobId.\");\n  }\n  return data.jobId;\n}\n","sourceCodeStart":223,"sourceCodeEnd":256,"githubUrl":"https://github.com/PaddlePaddle/PaddleOCR/blob/2661c7c0ef5c613e8f93c6e93b2e052399f0f854/api_sdk/typescript/src/internal/http.ts#L223-L256","documentation":"RateLimitError is thrown when the API answers HTTP 429 — you exceeded the allowed request rate or quota for your key/tier. It extends APIError with statusCode fixed at 429, and the message embeds any server explanation. This error is by design retryable after waiting.","triggerScenarios":"Tight poller loops hitting getJobStatus too frequently; submitting many files in a parallel loop; bursty batch status checks; shared keys used by multiple services; free-tier keys with low QPS ceilings.","commonSituations":"Fan-out uploads without concurrency limits; polling intervals shorter than the API allows; retries after errors amplifying request volume; month/quota exhaustion near billing boundaries; multiple team members sharing one key.","solutions":["Retry with backoff honoring the server's Retry-After guidance — start the next attempt after at least 1-2 seconds and double on repeat 429s","Cap concurrency of parallel submissions (e.g. p-limit with 2-4) and add spacing between polls","Increase the poll interval / maxWaitTime budget so status checks are less frequent","If sustained, request a quota increase or distribute across keys/environments as allowed by your plan"],"exampleFix":"// before\nconst results = await Promise.all(files.map(f => client.extractFile(model, f, {})));\n\n// after\nimport pLimit from \"p-limit\";\nconst limit = pLimit(3);\nconst results = await Promise.all(files.map(f =>\n  limit(() => retryOn429(() => client.extractFile(model, f, {})))\n));","handlingStrategy":"retry","validationCode":null,"typeGuard":"function isRateLimitError(e: unknown): e is RateLimitError {\n  return e instanceof RateLimitError;\n}","tryCatchPattern":"async function withRateLimit<T>(fn: () => Promise<T>, maxRetries = 5): Promise<T> {\n  for (let attempt = 0; ; attempt++) {\n    try { return await fn(); }\n    catch (e) {\n      if (e instanceof RateLimitError && attempt < maxRetries) {\n        await sleep(Math.min(2 ** attempt * 1000, 30_000) + Math.random() * 500);\n        continue;\n      }\n      throw e;\n    }\n  }\n}","preventionTips":["Cap submission concurrency (p-limit 2-4) instead of unbounded Promise.all fan-out","Space out polls; prefer the SDK poller's built-in backoff over hand-rolled tight loops","Respect Retry-After guidance embedded in the message and shed load upstream"],"tags":["rate-limit","retry","http","typescript"],"backgroundTag":null,"analyzedSha":"2661c7c0ef5c613e8f93c6e93b2e052399f0f854","analyzedAt":"2026-08-14T20:17:30.180Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}