Guides
Run a bulk enrichment workflow
Prepare, submit, monitor, reconcile, and recover bulk email, mobile, and validation jobs.
Use bulk enrichment when you need to process a list without holding one synchronous API request open for every row. LeadX supports file-based workflows for operators and a JSON email-job workflow for applications.
Choose the right bulk workflow
| Goal | Endpoint | Input | Monitor and collect |
|---|---|---|---|
| Find emails from a file | POST /v1/emails/find/bulk | CSV or XLSX, up to 25,000 rows | LeadX platform bulk-enrichment activity |
| Find mobile numbers from a file | POST /v1/mobile-numbers/find/bulk | CSV or XLSX, up to 25,000 rows | LeadX platform bulk-enrichment activity |
| Validate emails from a file | POST /v1/emails/validation/bulk | CSV or XLSX, up to 250,000 rows | LeadX platform bulk-enrichment activity |
| Integrate bulk email into an application | POST /v1/emails/find/bulk/jobs | JSON, up to 100,000 records | Public status and results endpoints |
File-upload job IDs identify activity in the LeadX platform. The public status and results contract is supported for jobs created through the JSON email-job endpoint. Do not depend on it for file-upload jobs.
Plan a recoverable batch
Before submission:
- Split files larger than the endpoint limit into deterministic batches.
- Add a stable CRM, warehouse, or source-system identifier to every row.
- Store the source batch ID, row count, and input checksum in your system.
- Choose one identity mode for each email row.
- Decide where completed output will be archived.
For JSON email jobs, use the stable source batch ID as the basis of your Idempotency-Key. Every JSON record also needs a unique external_id within that job.
Prepare file inputs
Email discovery
An email row needs one of these identity modes:
| Mode | Required columns |
|---|---|
| Name and domain | first_name, last_name, and base_url or url |
| LinkedIn profile | linkedin_url |
You can mix modes across rows in the same file. Use one complete mode per row.
crm_id,first_name,last_name,base_url,linkedin_url
crm-1001,Jane,Smith,acmeplumbing.com,
crm-1002,,,,https://www.linkedin.com/in/john-doeMobile discovery
Every mobile row needs a public LinkedIn profile URL.
crm_id,linkedin_url
crm-1001,https://www.linkedin.com/in/jane-smith
crm-1002,https://www.linkedin.com/in/john-doeEmail validation
Every validation row needs an email value.
crm_id,email
crm-1001,jane.smith@acmeplumbing.com
crm-1002,john.doe@example.orgLeadX accepts UTF-8 CSV files and XLSX workbooks. Column matching is case-insensitive for the required API-style headers. Empty files, unsupported extensions, and files above the row limit return 400 Bad Request.
Email validation also accepts a gzip-compressed CSV. Name the file with a .csv.gz suffix, send upload_compression=gzip, and keep the decompressed CSV at or below 50 MB.
Submit a file job
Send the file as multipart form data.
curl --fail-with-body "https://api.leadx.com/v1/emails/find/bulk" \
-H "X-API-KEY: $LEADX_API_KEY" \
-F "upload=@email-contacts.csv;type=text/csv"A successful submission returns the job ID:
{
"success": true,
"id": "f7c45f23-12a4-4db3-9f2c-c3bc46f80bb1"
}Persist the job ID with your source batch metadata. Monitor the job and download its completed workbook from the LeadX platform.
Preserve source columns
LeadX keeps uploaded columns and appends or updates enrichment fields. If a mapping workflow adds temporary lookup columns, send source_columns as a JSON array to control which original columns appear in the completed workbook.
curl --fail-with-body "https://api.leadx.com/v1/emails/find/bulk" \
-H "X-API-KEY: $LEADX_API_KEY" \
-F "upload=@mapped-contacts.csv;type=text/csv" \
-F 'source_columns=["crm_id","first_name","last_name","company"]'Every source_columns entry must match an uploaded column. The array cannot be empty and cannot contain duplicates.
Submit a JSON email job
Use the JSON workflow when an application needs idempotent submission, programmatic progress, partial results, and row-level reconciliation.
curl --fail-with-body "https://api.leadx.com/v1/emails/find/bulk/jobs" \
-H "X-API-KEY: $LEADX_API_KEY" \
-H "Idempotency-Key: crm-import-2026-08-23-001" \
-H "Content-Type: application/json" \
--data '{
"records": [
{
"external_id": "crm-1001",
"first_name": "Jane",
"last_name": "Smith",
"url": "acmeplumbing.com"
},
{
"external_id": "crm-1002",
"linkedin_url": "https://www.linkedin.com/in/john-doe"
}
],
"notify_email": false
}'The API returns 202 Accepted with status_url and results_url. Persist the idempotency key before sending the request and persist the returned job ID immediately.
Monitor a JSON job
Poll status_url until the job enters a terminal state.
| Status | Terminal? | Meaning |
|---|---|---|
queued | No | LeadX accepted the job and is waiting to start it |
running | No | At least part of the job is processing |
done | Yes | Processing completed |
failed | Yes | The job could not complete |
halted | Yes | Processing stopped before completion |
Start with a five-second polling interval. Increase it toward 30 seconds for long jobs and add jitter. Stop after an application-defined deadline instead of polling forever.
The status counters have different meanings:
processedcounts terminal rows.foundcounts rows with an email result.credits_debitedcounts credits charged across processed rows.progress_pctisprocessed / total * 100.phaseidentifies the current processing phase.status_detailcan provide additional status context.progress_modeiscontinuousorbatch.
Collect and reconcile results
Page results_url in input order. Start with cursor=-1, use a limit from 1 to 1,000, and pass each next_cursor into the following request until has_more is false.
Merge by external_id, not by name or array position. The row-level result is one of pending, found, not_found, or error.
LeadX does not publish a retention duration for JSON job results. Retrieve completed output promptly and store the records required by your own retention and audit policies.
See Sync enrichment into a CRM for a complete polling and pagination example.
Interpret results and credits
- A completed row can be a successful no-result row.
foundandcredits_debitedare independent counters.- A repeat email or mobile lookup can return data without another charge.
- A catch-all email can be returned with zero credits debited.
- A validation status describes data quality, not the credit decision by itself.
See Results and billing for exact fields, reason codes, and validation statuses.
Recover without duplicating work
| Failure point | Recovery action |
|---|---|
| JSON submission response is lost | Replay the identical body with the same Idempotency-Key |
| JSON key is reused with a different body | Treat 409 IDEMPOTENCY_KEY_REUSED as a configuration error and choose the correct batch |
Status request returns 429 or a transient 5xx | Back off and retry the status GET |
| A file-upload response is ambiguous | Check platform activity before uploading the file again |
A JSON row returns error | Submit only the failed source rows as a new logical batch |
A job becomes failed or halted | Preserve partial results and the job ID, then investigate before resubmission |
See Reliability and retries before automating recovery.
Upload a compressed email validation file
Email validation accepts gzip-compressed CSV files. Use a .csv.gz file and set upload_compression to gzip. The decompressed CSV cannot exceed 50 MB.
curl --fail-with-body "https://api.leadx.com/v1/emails/validation/bulk" \
-H "X-API-KEY: $LEADX_API_KEY" \
-F "upload=@emails.csv.gz;type=application/gzip" \
-F "upload_compression=gzip"Avoid common upload errors
- Use
.csvor.xlsx; other extensions return400. - For compressed email validation, use
.csv.gzwithupload_compression=gzipand a decompressed size of 50 MB or less. - Keep email and mobile discovery files at or below 25,000 rows.
- Keep email validation files at or below 250,000 rows.
- Preserve exact semantic headers such as
linkedin_url,first_name,last_name,base_url, andemail. - Save CSV files as UTF-8.
- Do not put multiple contacts into one row.
- Keep a stable source identifier in every row.
- Do not automatically replay an ambiguous file upload.