Usage Limits
The CPFHub.io API has two limits:
- Credits: each successful lookup uses credits from your plan's allowance.
- Requests per minute: a cap on calls per minute for each lookup type, to protect API stability.
The costs below are the defaults. Check the costs and overage rules specific to your plan in the dashboard. The default limits are 30/min for the Simple Lookup (each batch submission counts as 1) and 5/min for the Real-Time Lookup. Custom plans may have different limits, so confirm with CPFHub.io support. Above the limit, the API responds 429 with the Retry-After header. Your balance and current plan appear in GET /quota, which does not use credits.
Credits per plan
| Plan on the website | Name in GET /quota | Credits per period | When they run out |
|---|---|---|---|
| Free | Grátis | 50 | The API stops and responds 403 until renewal. |
| Pro | Micro | 100 | The API keeps working and overage is billed on the following invoice |
| Pro | Básico | 1,000 | The API keeps working and overage is billed on the following invoice |
| Pro | Intermediário | 5,000 | The API keeps working and overage is billed on the following invoice |
| Pro | Avançado | 10,000 | The API keeps working and overage is billed on the following invoice |
| Enterprise | Custom plan | Custom pricing | Negotiated |
The Pro plan on the website is a volume selector, and each volume matches one of the plans above. The names in GET /quota are the Portuguese names returned by the API. Custom plans may have their own rules, so check the terms of your account in the dashboard. See prices at cpfhub.io/precos.
When credits renew: on paid plans, at the start of each billing cycle of the subscription. On the Free plan, on the 1st of each month (UTC). The balance belongs to the whole account, adding up all keys.
Cost per lookup
| Lookup | Cost |
|---|---|
Simple Lookup (GET /cpf/{cpf}) | 1 credit per CPF found |
Real-Time Lookup (POST /cpf/realtime) | 1.5 credits per successful lookup |
Batch lookup (POST /cpf/bulk) | 1 credit per CPF found |
Batch status (GET /cpf/bulk/{jobId}) | Free |
Credit balance (GET /quota) | Free |
These are the default costs. Check the costs specific to your plan in the dashboard.
Errors do not use credits
A CPF that is not found (404), a CPF with an incorrect format or check digit (400/422), the per-minute limit (429) and failures when querying Receita Federal (422/502/503) do not use credits.
Credits exhausted
On plans without overage billing, such as Free, the API responds 403 when the balance does not cover the cost of the lookup:
| Where | Message | Format of error |
|---|---|---|
| Simple Lookup and Real-Time Lookup | Limite de créditos excedido (credit limit exceeded) | Object (error.message) |
| Batch submission, balance smaller than the list | Créditos insuficientes para o lote: N necessários. (not enough credits for the batch: N needed) | Object (error.message) |
| Batch submission, zero balance | Limite de créditos excedido | String |
- On a batch, the balance must cover all valid CPFs in the list before submission. If it does not, nothing is charged.
- A balance of 1 credit still allows a Simple Lookup, but not a Real-Time Lookup (1.5 credits).
- Track your balance with
GET /quota, which works with zero credits and does not use credits.
Per-minute limit
| Lookup | Default limit |
|---|---|
Simple Lookup (GET /cpf/{cpf}) | 30 requests per minute |
Batch lookup (POST /cpf/bulk) | Each submission counts as 1 Simple Lookup request |
Real-Time Lookup (POST /cpf/realtime) | 5 requests per minute |
These are the default limits. Custom plans may have different limits, so confirm with CPFHub.io support. Each batch submission counts as one Simple Lookup request. Above the limit, the API responds 429 with the Retry-After header.
- The limit applies per API key and per lookup type.
- Every request counts, including those that respond
404. - The count resets at the start of each clock minute.
GET /cpf/bulk/{jobId}andGET /quotado not count toward the limit.
When you go over the limit, the API responds 429 with the Retry-After header (seconds until it frees up) and does not use credits:
HTTP/1.1 429 Too Many Requests
Retry-After: 18{
"success": false,
"data": null,
"error": {
"message": "Limite de requisições por minuto excedido. Tente novamente em 18 segundos."
}
}The message text is in Portuguese (rate limit per minute exceeded, try again in 18 seconds). Wait for the Retry-After time and repeat the same request. For many CPFs, use the batch lookup: a single submission processes up to 10,000 CPFs and counts as one request.
Batch limit
POST /cpf/bulk accepts up to 10,000 CPFs per request. For larger volumes, split them across several calls. Details in Batch Lookup.
The Real-Time Lookup is not available in batch.
High-volume usage
Use the batch endpoint for large volumes
For many CPFs at once, prefer POST /cpf/bulk: one call processes up to 10,000 CPFs in the background.
If you prefer individual calls, limit concurrency and respect the 429:
import asyncio, os
import httpx
CONCURRENCY = 5
async def process_batch(cpf_list: list[str]):
semaphore = asyncio.Semaphore(CONCURRENCY)
async with httpx.AsyncClient(
base_url='https://api.cpfhub.io',
headers={'x-api-key': os.environ['CPFHUB_API_KEY']},
timeout=10,
) as client:
async def lookup_one(cpf: str):
async with semaphore:
for _ in range(3):
res = await client.get(f'/cpf/{cpf}')
if res.status_code != 429:
return res.status_code, res.json()
await asyncio.sleep(int(res.headers.get('retry-after', '5')))
return res.status_code, res.json()
return await asyncio.gather(*[lookup_one(cpf) for cpf in cpf_list])For the Real-Time Lookup, the typical time is about 1 second (not an SLA). Use a timeout of at least 60 seconds.
Larger volumes
For volumes above 10,000 lookups per month, higher per-minute limits, negotiated terms or a dedicated SLA, talk to us about the Enterprise plan.
Updated on October 4, 2026