CPFHub.io
Start for free

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 websiteName in GET /quotaCredits per periodWhen they run out
FreeGrátis50The API stops and responds 403 until renewal.
ProMicro100The API keeps working and overage is billed on the following invoice
ProBásico1,000The API keeps working and overage is billed on the following invoice
ProIntermediário5,000The API keeps working and overage is billed on the following invoice
ProAvançado10,000The API keeps working and overage is billed on the following invoice
EnterpriseCustom planCustom pricingNegotiated

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

LookupCost
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:

WhereMessageFormat of error
Simple Lookup and Real-Time LookupLimite de créditos excedido (credit limit exceeded)Object (error.message)
Batch submission, balance smaller than the listCréditos insuficientes para o lote: N necessários. (not enough credits for the batch: N needed)Object (error.message)
Batch submission, zero balanceLimite de créditos excedidoString
  • 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

LookupDefault 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} and GET /quota do 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
HTTP/1.1 429 Too Many Requests
Retry-After: 18
JSON
{
  "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:

Python
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.

Talk to the team →


Updated on October 4, 2026