Bulks

This page covers what a Bulk is, the lifecycle, how to inspect items, and the full status_code catalog.

Overview

A bulk is a batch job that processes many identifiers at once. Instead of making one API call per identifier, you submit them all together, get a bulk_id back, and poll for status until the batch completes.

  • Up to 100,000 items per bulk.
  • Asynchronous: bulk creation returns 202 Accepted immediately. Items process in the background.
  • Each item has its own status and status_code, so you can see exactly which identifiers succeeded, which failed, and why.

Use a bulk when you need to process more than ~50 identifiers at once and don't need synchronous results per identifier. For real-time results, use POST /verifications or POST /consents individually.


Bulk types

There are two kinds of bulks. Both are inspected through the same endpoints.

TypeCreated byWhat it does
consentPOST /consents/bulkCreates a consent record per identifier.
verificationPOST /verifications/bulkTriggers a verification per identifier.

Both types are managed through:

The response from GET /bulks/{bulk_id} includes a type field (consent or verification) so you always know which kind of bulk you're looking at.


Lifecycle

A bulk moves through this flow:

  1. Create. Call POST /consents/bulk or POST /verifications/bulk with your list of identifiers. The API returns 202 Accepted with a bulk_id.
  2. In progress. Items are processed asynchronously. The metrics object on GET /bulks/{bulk_id} tracks how many are done, failed, or still running.
  3. Completed. When metrics.in_progress hits zero, the bulk is finished. completed + failed will equal total.
  4. Inspect. If metrics.failed > 0, use GET /bulks/{bulk_id}/items?status=error to see each failed item with its status_code.

A metrics object looks like this:

{
  "total": 1000,
  "completed": 850,
  "failed": 50,
  "in_progress": 100
}

Recommended polling interval: every 30 seconds for bulks under 10k items, every 2 minutes for larger bulks. Don't poll more than once per 10 seconds.

while true; do
  response=$(curl -s -H "X-API-Key: $API_KEY" \
    https://api.burodeingresos.com/bulks/$BULK_ID)
  in_progress=$(echo $response | jq '.metrics.in_progress')
  if [ "$in_progress" = "0" ]; then
    echo "Done"
    break
  fi
  sleep 30
done

Inspecting items

When metrics.failed > 0, list the failed items to see which ones errored and why:

curl -H "X-API-Key: $API_KEY" \
  "https://api.burodeingresos.com/bulks/$BULK_ID/items?status=error&page=1&items_per_page=100"

Each item has:

FieldTypeDescription
identifierstringThe input identifier (CURP or 12-char RFC).
verification_idstring (UUID) or nullResulting verification ID. null if the item errored before a verification was created, or for consent bulks.
statusstringsuccess or error.
status_codestringA value from the catalog below.
created_atstring (ISO 8601)When this item was processed.

Filters available on GET /bulks/{bulk_id}/items:

  • status, success or error.
  • status_code, any value from the catalog.
  • identifier, exact match.

Pagination: default items_per_page is 100, max is 500. Walk pages until page > total_pages.


status_code catalog

Every item has a status_code. It's a closed enum with 13 values.

status_codeMeaningClient action
successItem completed successfully.None.
invalid_identifierIdentifier malformed (must be 18-char CURP or 12-char RFC).Fix the identifier format and retry.
invalid_rfcrfc field malformed (must be 12-13 chars).Fix the RFC format and retry.
invalid_ip_addressip_address is not a valid IPv4.Fix the IP address and retry.
invalid_emailemail is not a valid email address.Fix the email and retry.
missing_ip_addressip_address is required and was not provided.Include ip_address in the request.
consent_not_foundNo consent exists for this identifier.Create a consent via POST /consents before retrying.
consent_expiredConsent exists but is past its 365-day TTL.Create a new consent and retry.
consent_mismatchConsent exists but doesn't match the identifier (wrong type).Verify the identifier type matches the consent type.
verification_in_progressA verification is already in progress for this identifier.Wait for it to complete or query the existing one.
rate_limit_exceededDaily verification limit reached for this identifier (max 2/day per identifier).Retry the next day. See Rate Limits.
extraction_errorInternal error during data extraction from a source.Safe to retry.
internal_errorUncategorized internal failure.Safe to retry. Contact support if persistent.

Code examples

Get bulk status

curl -H "X-API-Key: $API_KEY" \
  https://api.burodeingresos.com/bulks/4214c040-26c5-40c0-9526-afaff68eb97d

Response:

{
  "bulk_id": "4214c040-26c5-40c0-9526-afaff68eb97d",
  "external_id": null,
  "type": "verification",
  "created_at": "2026-05-20T17:37:14.133658Z",
  "metrics": {
    "total": 5,
    "completed": 0,
    "failed": 5,
    "in_progress": 0
  }
}

List failed items with a specific status code

curl -H "X-API-Key: $API_KEY" \
  "https://api.burodeingresos.com/bulks/$BULK_ID/items?status=error&status_code=rate_limit_exceeded"

Response:

{
  "items": [
    {
      "identifier": "FUVF580402HDFRLR05",
      "verification_id": null,
      "status": "error",
      "status_code": "rate_limit_exceeded",
      "created_at": "2026-05-20T17:37:14.133658Z"
    }
  ],
  "pagination": {
    "page": 1,
    "items_per_page": 100,
    "total_items": 5,
    "total_pages": 1
  }
}

Polling loop with failure inspection

#!/bin/bash
BULK_ID="4214c040-26c5-40c0-9526-afaff68eb97d"

while true; do
  response=$(curl -s -H "X-API-Key: $API_KEY" \
    "https://api.burodeingresos.com/bulks/$BULK_ID")

  in_progress=$(echo $response | jq '.metrics.in_progress')
  failed=$(echo $response | jq '.metrics.failed')

  if [ "$in_progress" = "0" ]; then
    break
  fi
  sleep 30
done

if [ "$failed" -gt "0" ]; then
  curl -H "X-API-Key: $API_KEY" \
    "https://api.burodeingresos.com/bulks/$BULK_ID/items?status=error"
fi

Migration from /verifications/bulk/{bulk_id}

If you're using the old endpoints, they still work as aliases but are now deprecated:

Old endpointNew endpoint
GET /verifications/bulk/{bulk_id}GET /bulks/{bulk_id}
DELETE /verifications/bulk/{bulk_id}DELETE /bulks/{bulk_id}

The new endpoints work for both consent and verification bulks. Migrate when you can, the old endpoints will be removed in a future release.