PaddlePaddle/PaddleOCR · warning · RateLimitError
Rate limit exceeded: {msg}
Error message
Rate limit exceeded: {msg} What it means
Raised as RateLimitError by raise_for_status() when the HTTP response status is 429. The service is throttling requests for your API key or account; the embedded message usually states the limit and reset window. Unlike 400, retrying after a delay can succeed.
Source
Thrown at paddleocr/_api_client/_core.py:156
)
return JobStatus(
job_id=job_id,
state=state,
progress=progress,
result=data.get("resultUrl"),
error_msg=data.get("errorMsg"),
)
def raise_for_status(status_code: int, msg: str) -> None:
if 200 <= status_code < 300:
return
if status_code in (401, 403):
raise AuthError(f"Authentication failed: {msg}")
if status_code == 400:
raise InvalidRequestError(f"Bad request: {msg}")
if status_code == 429:
raise RateLimitError(f"Rate limit exceeded: {msg}")
if status_code in (503, 504):
raise ServiceUnavailableError(status_code, f"Service unavailable: {msg}")
raise APIError(status_code, msg)
def unwrap_api_response(payload: dict, status_code: int) -> dict:
if not isinstance(payload, dict):
raise ResponseFormatError("Response body must be a JSON object.")
code = payload.get("code", 0)
if code not in (0, None):
raise APIError(status_code, extract_api_message_from_payload(payload) or "")
data = payload.get("data")
if not isinstance(data, dict):
raise ResponseFormatError("Response JSON must contain object field 'data'.")
return data
def extract_job_id(data: dict) -> str:View on GitHub (pinned to 2661c7c0ef)
Solutions
- Honor Retry-After / the message's reset timing and retry after the window.
- Increase the poll interval (and keep exponential backoff) in polling calls.
- Throttle or batch job creation; add a client-side rate limiter.
- Request a quota increase if sustained throughput is needed.
Example fix
# before
result = await client.ocr(file_path=p, timeout=600) # tight internal polling
# after
import asyncio
try:
result = await client.ocr(file_path=p, timeout=600)
except RateLimitError:
await asyncio.sleep(60)
result = await client.ocr(file_path=p, timeout=600) Defensive patterns
Strategy: retry
Validate before calling
import time
MIN_POLL_INTERVAL = 2.0
_last_call = 0.0
def throttle():
global _last_call
wait = MIN_POLL_INTERVAL - (time.monotonic() - _last_call)
if wait > 0:
time.sleep(wait)
_last_call = time.monotonic() Try / catch
from paddleocr._api_client.errors import RateLimitError
import asyncio
async def with_backoff(fn, *a, attempts=5, **kw):
for i in range(attempts):
try:
return await fn(*a, **kw)
except RateLimitError:
if i == attempts - 1:
raise
await asyncio.sleep(2 ** i * 5) Prevention
- Keep poll intervals at or above the documented rate limit.
- Wrap all calls in exponential backoff for RateLimitError.
- Cap concurrent workers per API key.
When it happens
Trigger: Burst job creation, tight polling loops on get_job_status, or concurrent workers sharing one key, exceeding the account's requests-per-second or jobs-per-day quota.
Common situations: Reduced the poll interval below the allowed rate, scaled out workers without raising quotas, or free-tier daily quota exhausted.
Related errors
- Rate limit exceeded: ${text}
- Service unavailable: {msg}
- Either fileUrl or filePath is required.
- OCR result item is missing result.ocrResults.
- OCR result page is missing prunedResult.
AI-assisted analysis of PaddlePaddle/PaddleOCR@2661c7c0ef (2026-08-14).
Data as JSON: /api/errors/8b3b7b572cbebec3.
Report an issue: GitHub.