Rate Limits

FastCaptcha uses per-IP and per-API-key rate limiting to ensure fair access for all users. This guide covers the limits, the response headers that tell you how close you are, and how to handle a 429.

Current limits

EndpointLimitWindowKey type
POST /api/v1/ocr/60 requestsper minuteAPI key
POST /api/v1/ocr/30 requestsper minuteIP (anonymous)
GET /api/v1/balance/60 requestsper minuteAPI key
POST /demo/10 requestsper dayIP (anonymous)
Need higher limits? Enterprise plans with unlimited throughput are available. Contact us.

Rate-limit response headers

Every API response includes these headers so you can track usage in real time:

HeaderMeaning
X-RateLimit-LimitMaximum requests allowed in the window
X-RateLimit-RemainingRequests left in the current window
X-RateLimit-ResetUnix timestamp when the window resets
Retry-AfterSeconds to wait before retrying (only on 429)
HTTP/1.1 200 OK
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 47
X-RateLimit-Reset: 1718000060
Content-Type: application/json

{"success": true, "text": "XK92B", ...}

Handling a 429 Too Many Requests

When you hit the limit, the API returns HTTP 429 with a JSON body and a Retry-After header:

HTTP/1.1 429 Too Many Requests
Retry-After: 8
Content-Type: application/json

{"success": false, "error": "Rate limit exceeded. Please slow down.", "code": "RATE_LIMIT_EXCEEDED"}

Always read the Retry-After header and wait that many seconds before retrying. Do not poll in a tight loop — this will only extend your backoff period.

Best practices for high-volume usage

Throttle at the client

Use a request queue with a configurable rate (e.g., ratelimiter in Python, bottleneck in Node.js) to stay safely under 60 RPM.

Monitor X-RateLimit-Remaining

If remaining drops below 10, add a small sleep between requests rather than waiting for a 429.

Exponential backoff on 429

If you miss the limit, wait Retry-After seconds, then use exponential backoff for subsequent retries.

Batch requests sensibly

Process captchas in batches of 40–50/min to leave headroom for spikes without hitting the cap.

Code examples

Python — rate-aware client with Bottleneck-style throttling

import time, requests

API_KEY = "YOUR_API_KEY"
RATE_LIMIT = 55  # stay 5 under the 60 RPM cap

class FastCaptchaClient:
    def __init__(self):
        self._last_reset = time.time()
        self._sent = 0

    def _throttle(self):
        now = time.time()
        if now - self._last_reset >= 60:
            self._sent = 0
            self._last_reset = now
        if self._sent >= RATE_LIMIT:
            sleep_for = 60 - (now - self._last_reset)
            print(f"Throttling: sleeping {sleep_for:.1f}s")
            time.sleep(sleep_for)
            self._sent = 0
            self._last_reset = time.time()
        self._sent += 1

    def solve(self, image_path: str) -> str:
        self._throttle()
        resp = requests.post(
            "https://fastcaptcha.org/api/v1/ocr/",
            headers={"X-API-Key": API_KEY},
            files={"image": open(image_path, "rb")},
            timeout=15,
        )
        remaining = int(resp.headers.get("X-RateLimit-Remaining", 99))
        if remaining < 5:
            time.sleep(1)  # slow down proactively
        data = resp.json()
        if not data.get("success"):
            raise ValueError(data.get("error", "Unknown error"))
        return data["text"]

Node.js — using Bottleneck

const Bottleneck = require("bottleneck");
const axios = require("axios");
const FormData = require("form-data");
const fs = require("fs");

const limiter = new Bottleneck({ maxConcurrent: 5, minTime: 1100 }); // ~55 RPM

async function solve(imagePath) {
  return limiter.schedule(async () => {
    const form = new FormData();
    form.append("image", fs.createReadStream(imagePath));
    const { data } = await axios.post(
      "https://fastcaptcha.org/api/v1/ocr/",
      form,
      { headers: { "X-API-Key": "YOUR_API_KEY", ...form.getHeaders() } }
    );
    return data.text;
  });
}