Rate Limits

Every FourA API request passes through rate limiting. You'll encounter two types of limits: concurrency (simultaneous requests) and RPM (requests per minute). Both are enforced per service.

How Rate Limits Work

The API enforces limits at two levels:

  1. Global API limit: applies to all requests regardless of endpoint. This check runs first.
  2. Per-service limit: applies separately to each endpoint (single, proxy, browser). This check only runs if the global limit passes.

If the global limit is hit, the per-service check doesn't run. A request must pass both checks before it reaches the backend.

Each level tracks two metrics:

  • Concurrency: how many requests are running at the same time.
  • RPM: how many requests you've sent in the last 60 seconds.

Rate Limit Responses

When you hit a limit, the API returns a JSON response with your current usage and the limits in effect.

429: RPM Exceeded

{
  "error": "Rate limit exceeded",
  "status": 429,
  "service": "single",
  "retryAfter": 5,
  "current": {
    "concurrency": 12,
    "rpm": 3000
  },
  "limits": {
    "maxConcurrency": 500,
    "maxRpm": 3000
  }
}

You've sent too many requests in the last minute. Wait for the retryAfter period before sending more.

503: Concurrency Exceeded

{
  "error": "Service at capacity",
  "status": 503,
  "service": "proxy",
  "retryAfter": 2,
  "current": {
    "concurrency": 500,
    "rpm": 1200
  },
  "limits": {
    "maxConcurrency": 500,
    "maxRpm": 3000
  }
}

Too many requests are running at the same time. Some of your earlier requests haven't finished yet.

Service Disabled

When a service is temporarily taken offline for maintenance, the API returns 503 with a different error message:

{
  "error": "Service disabled",
  "status": 503,
  "retryAfter": 60
}

This isn't a rate limit. The service is temporarily unavailable. Check the retryAfter value and retry after that many seconds. This typically resolves within minutes.

Don't confuse this with the concurrency 503. A "Service disabled" response won't include current or limits fields.

Response Fields

Field Type Description
error string Human-readable error message
status number HTTP status code (429 or 503)
service string Which service hit the limit: single, proxy, browser, or api
retryAfter number Recommended wait time in seconds before retrying
current.concurrency number Your current number of active requests
current.rpm number Your requests in the last 60 seconds
limits.maxConcurrency number Maximum simultaneous requests allowed
limits.maxRpm number Maximum requests per minute allowed

Handling Rate Limits

Use the retryAfter field to implement backoff:

import requests
import time

def fetch(url, max_retries=5):
    for attempt in range(max_retries):
        resp = requests.post(
            "https://eu.api.foura.ai/api/single/",
            headers={
                "X-API-Key": "YOUR_API_KEY",
                "Content-Type": "application/json"
            },
            json={"method": "GET", "url": url}
        )

        if resp.status_code in (429, 503):
            data = resp.json()
            wait = data.get("retryAfter", 5)
            time.sleep(wait)
            continue

        return resp

    raise Exception("Max retries exceeded")

Tips

  • Check the current fields in rate limit responses to understand your usage patterns.
  • If you're consistently hitting concurrency limits, reduce the number of parallel requests.
  • If you're hitting RPM limits, add a small delay between requests or batch them over a longer window.
  • The retryAfter value varies by limit type: 2 seconds for concurrency, 5 seconds for RPM.
  • A "Service disabled" response means maintenance is in progress. Don't keep hammering the endpoint.
Last updated: April 15, 2026