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 Acceptedimmediately. Items process in the background. - Each item has its own
statusandstatus_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.
| Type | Created by | What it does |
|---|---|---|
consent | POST /consents/bulk | Creates a consent record per identifier. |
verification | POST /verifications/bulk | Triggers a verification per identifier. |
Both types are managed through:
GET /bulks/{bulk_id}, status and metrics.GET /bulks/{bulk_id}/items, paginated list of items.DELETE /bulks/{bulk_id}, cancel and delete the bulk.
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:
- Create. Call
POST /consents/bulkorPOST /verifications/bulkwith your list of identifiers. The API returns202 Acceptedwith abulk_id. - In progress. Items are processed asynchronously. The
metricsobject onGET /bulks/{bulk_id}tracks how many are done, failed, or still running. - Completed. When
metrics.in_progresshits zero, the bulk is finished.completed + failedwill equaltotal. - Inspect. If
metrics.failed > 0, useGET /bulks/{bulk_id}/items?status=errorto see each failed item with itsstatus_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
doneInspecting 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:
| Field | Type | Description |
|---|---|---|
identifier | string | The input identifier (CURP or 12-char RFC). |
verification_id | string (UUID) or null | Resulting verification ID. null if the item errored before a verification was created, or for consent bulks. |
status | string | success or error. |
status_code | string | A value from the catalog below. |
created_at | string (ISO 8601) | When this item was processed. |
Filters available on GET /bulks/{bulk_id}/items:
status,successorerror.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
status_code catalogEvery item has a status_code. It's a closed enum with 13 values.
status_code | Meaning | Client action |
|---|---|---|
success | Item completed successfully. | None. |
invalid_identifier | Identifier malformed (must be 18-char CURP or 12-char RFC). | Fix the identifier format and retry. |
invalid_rfc | rfc field malformed (must be 12-13 chars). | Fix the RFC format and retry. |
invalid_ip_address | ip_address is not a valid IPv4. | Fix the IP address and retry. |
invalid_email | email is not a valid email address. | Fix the email and retry. |
missing_ip_address | ip_address is required and was not provided. | Include ip_address in the request. |
consent_not_found | No consent exists for this identifier. | Create a consent via POST /consents before retrying. |
consent_expired | Consent exists but is past its 365-day TTL. | Create a new consent and retry. |
consent_mismatch | Consent exists but doesn't match the identifier (wrong type). | Verify the identifier type matches the consent type. |
verification_in_progress | A verification is already in progress for this identifier. | Wait for it to complete or query the existing one. |
rate_limit_exceeded | Daily verification limit reached for this identifier (max 2/day per identifier). | Retry the next day. See Rate Limits. |
extraction_error | Internal error during data extraction from a source. | Safe to retry. |
internal_error | Uncategorized 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-afaff68eb97dResponse:
{
"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"
fiMigration from /verifications/bulk/{bulk_id}
/verifications/bulk/{bulk_id}If you're using the old endpoints, they still work as aliases but are now deprecated:
| Old endpoint | New 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.
Updated 3 days ago
