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

  1. Honor Retry-After / the message's reset timing and retry after the window.
  2. Increase the poll interval (and keep exponential backoff) in polling calls.
  3. Throttle or batch job creation; add a client-side rate limiter.
  4. 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

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


AI-assisted analysis of PaddlePaddle/PaddleOCR@2661c7c0ef (2026-08-14). Data as JSON: /api/errors/8b3b7b572cbebec3. Report an issue: GitHub.