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
| Endpoint | Limit | Window | Key type |
|---|---|---|---|
POST /api/v1/ocr/ | 60 requests | per minute | API key |
POST /api/v1/ocr/ | 30 requests | per minute | IP (anonymous) |
GET /api/v1/balance/ | 60 requests | per minute | API key |
POST /demo/ | 10 requests | per day | IP (anonymous) |
Rate-limit response headers
Every API response includes these headers so you can track usage in real time:
| Header | Meaning |
|---|---|
X-RateLimit-Limit | Maximum requests allowed in the window |
X-RateLimit-Remaining | Requests left in the current window |
X-RateLimit-Reset | Unix timestamp when the window resets |
Retry-After | Seconds 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;
});
}