CPFHub.io
Start for free

CNPJ Batch Lookup

Send a list of up to 10,000 CNPJs and get, for each one, the same record as the CNPJ Lookup, processed in the background. It costs 1 credit per CNPJ found.

Using Cursor, Lovable, v0 or another AI? Copy the CNPJ batch lookup prompt and paste it into your assistant.

Open in Cursor

Without code, the dashboard accepts a list of CNPJs and shows the result for each one.

Two-step flow:

  1. POST /cnpj/bulk sends the list and responds right away with a jobId.
  2. GET /cnpj/bulk/{jobId} returns progress and results. Repeat until status is "done" or "failed".

Endpoint

POSThttps://api.cpfhub.io/cnpj/bulk

Parameters

WhereNameRequiredDescription
Headerx-api-keyYesYour API key. See Authentication.
HeaderContent-TypeYesapplication/json
BodycnpjsYesstring[] of CNPJs, with or without formatting, numeric or alphanumeric. Not empty, at most 10,000. Items that are not strings count as invalid.
BodyfileNameNoFree label. Comes back as file_name in the status.
BodyformatNoFree label for the source format (e.g. "csv"). Comes back as format.

What happens on submission

StepEffect
NormalizationRemoves dots, slash and hyphen and converts letters to uppercase. CNPJs with a wrong format or check digit go to invalid and are not charged.
DuplicatesRemoved. Each CNPJ is looked up and charged once. They count in neither totalRecords nor invalid.
BalanceWithout overage, the balance must cover all valid CNPJs (totalRecords × lookup cost, 1 credit by default). If it does not, 403 and nothing is charged.
Pending batchesThere is a limit of batches being processed at the same time per customer. Above it, 429.

More than 10,000 CNPJs: split them into chunks and send one POST for each, waiting for earlier batches if you get a 429.

Example

Check the status before reading data: on error, the response has no jobId.

curl -X POST "https://api.cpfhub.io/cnpj/bulk" \
  -H "x-api-key: $CPFHUB_API_KEY" \
  -H "content-type: application/json" \
  -d '{
    "cnpjs": ["11222333000181", "12.ABC.345/01DE-35"]
  }'

Response

HTTP 202 Accepted

JSON
{
  "success": true,
  "data": {
    "jobId": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
    "status": "processing",
    "totalRecords": 2,
    "invalid": 0
  }
}
FieldTypeDescription
data.jobIdstringBatch ID. Keep it to check the status.
data.statusstringAlways "processing"
data.totalRecordsnumberValid, unique CNPJs that will be looked up
data.invalidnumberDiscarded items (format, check digit or not a string)

The submission responds in camelCase. The status responds in snake_case.

Status and results

GEThttps://api.cpfhub.io/cnpj/bulk/{jobId}

Use the same API key as the submission. It uses no credit and works with a zero balance.

Polling:

  • Start every 5 s and increase up to 30 s for large batches. Each call returns the whole cnpjs array, and each CNPJ record is much larger than a CPF one.
  • Set a maximum wait in the client (e.g. 30 min). Batch time depends on how many CNPJs are in the submission.
  • On 5xx or a network error, try again on the next cycle. The GET is safe to repeat.
curl "https://api.cpfhub.io/cnpj/bulk/7c9e6679-7425-40de-944b-e07fc1f90ae7" \
  -H "x-api-key: $CPFHUB_API_KEY"

HTTP 200 OK

JSON
{
  "success": true,
  "data": {
    "id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
    "status": "done",
    "file_name": null,
    "format": null,
    "total_records": 2,
    "processed": 2,
    "found": 1,
    "not_found": 1,
    "skipped": 0,
    "error": null,
    "created_at": "2026-10-15T14:15:16.000Z",
    "updated_at": "2026-10-15T14:15:18.000Z",
    "cnpjs": [
      {
        "cnpj": "11222333000181",
        "found": true,
        "data": {
          "cnpj": "11222333000181",
          "razao_social": "EMPRESA EXEMPLO LTDA",
          "situacao_cadastral": "Ativa",
          "...": "other CNPJ Lookup fields"
        }
      },
      {
        "cnpj": "12ABC34501DE35",
        "found": false,
        "data": null
      }
    ]
  }
}
FieldDescription
data.idBatch ID (the jobId from the submission)
data.status"processing", "done" or "failed"
data.total_recordsValid, unique CNPJs in the batch
data.processedCNPJs already processed (includes skipped)
data.foundFound. Each uses 1 credit by default (your plan's cost is in the dashboard)
data.not_foundNot found. No credit used.
data.skippedNot looked up because the balance ran out during the batch (no overage). No credit used.
data.errorFailure reason when status is "failed"
data.file_name, data.formatLabels sent in the POST
data.created_at, data.updated_atISO 8601 dates
data.cnpjs[]Result for each processed CNPJ: cnpj (14 characters without formatting), found and data, which is the same record as the CNPJ Lookup or null when found is false

While "processing", the counters update at intervals and may lag behind cnpjs.

Errors

The API only responds in Portuguese. Error messages are shown as returned.

StatusWhen it happensWhat to do
400cnpjs missing, empty, not an array ("Campo 'cnpjs' deve ser um array não vazio.") or more than 10,000 items ("Limite de 10000 CNPJs por lote excedido.")Fix the body.
401Missing, invalid or inactive API key (error is a string)Check the x-api-key header.
403Without overage: balance lower than the batch (error.message: "Créditos insuficientes para o lote: N necessários.", insufficient credits). Also zero balance and inactive/blocked account. POST only.Add credits or shrink the batch.
404jobId does not exist or was created with another API key ("Lote não encontrado.")Check the jobId and the key.
422No valid CNPJ in the list ("Nenhum CNPJ válido na lista enviada.")Fix the list.
429Per-minute limit exceeded, or pending batch limit ("Limite de N lotes pendentes por cliente excedido."). POST only.Wait the seconds in the Retry-After header (60 s for the pending batch limit). Let earlier batches finish before sending more.
500"Falha ao criar lote." (batch creation failed). No batch was created.Try again. Report it if it persists.

Error format and retries: Error Codes. Per-minute limit and credits: Usage Limits.

Details

Batch states

SituationHow to tellWhat to do
Processingstatus: "processing"Keep polling. cnpjs already has partial results.
Done"done" and skipped: 0Use cnpjs.
Done with skipped"done" and skipped > 0The balance ran out during the batch. Add credits and resend only the missing CNPJs.
Failed"failed"Read error. The found items in cnpjs were already charged. Resend only the missing ones.

With overage, no CNPJ is skipped: all are looked up and the excess is charged.

Matching results to your list

cnpjs does not follow the submission order and does not include skipped or unprocessed items. Normalize your list (no dots, slash or hyphen, uppercase) and match on the cnpj field. The missing ones are the ones to resend (including invalid ones, which cost nothing).

const normalize = (cnpj: string) => cnpj.replace(/[./-]/g, '').toUpperCase()

const results = new Map(job.cnpjs.map((item) => [item.cnpj, item]))

for (const original of myCnpjs) {
  const result = results.get(normalize(original))
  console.log(original, result ? (result.found ? result.data.razao_social : 'not found') : 'no result')
}

// CNPJs to resend in a new batch (if the batch failed or had skipped items)
const missing = [...new Set(myCnpjs.map(normalize))].filter((cnpj) => !results.has(cnpj))

More

  • Submission is not idempotent: each accepted POST creates a new batch and charges the found items. If the connection drops without a jobId, check the dashboard or GET /quota before resending. With an error response, no batch was created.
  • Credit: only found: true is charged, at the same cost and from the same allowance as the CPF Simple Lookup. Invalid, duplicate, not found and skipped items are not.
  • Data source: the same as the CNPJ Lookup: the monthly public database of the Receita Federal, processed by OpenCNPJ. Data may be out of date until the next monthly update.
  • Sample data: the CNPJs and the data above are fictional.

Updated on October 15, 2026