Getting Started
Reliability and retries
Use bounded timeouts, safe retries, idempotency keys, and resilient bulk-job polling.
Production integrations should bound every request, retry only recoverable failures, and prevent a replay from creating duplicate work.
Choose synchronous or asynchronous processing
| Need | Recommended workflow |
|---|---|
| One result during an interactive workflow | Use the corresponding synchronous endpoint |
| Up to 25,000 email records from an application | Create a JSON bulk email job |
| A CSV or XLSX workflow operated by a person | Use a file-upload endpoint and monitor it in the LeadX platform |
| More records than one request accepts | Split the source into deterministic batches |
Prefer a bulk job when the caller can tolerate asynchronous completion. It isolates long-running enrichment from the client request and gives you durable job and row identifiers.
Set bounded client timeouts
Use separate connection and response timeouts. The following values are starting points, not service-level objectives:
| Request type | Connect timeout | Response timeout |
|---|---|---|
| Synchronous email discovery or validation | 10 seconds | 65 seconds |
| Other synchronous enrichment requests | 10 seconds | 45 seconds |
| Bulk submission, job status, and result pages | 10 seconds | 30 seconds |
Tune these values to your network and contractual requirements. Do not use an unbounded timeout.
response = requests.post(
"https://api.leadx.com/v1/emails/find",
headers={"X-API-KEY": api_key},
json=payload,
timeout=(10, 65),
)Classify failures before retrying
| Outcome | Automatic retry? | Action |
|---|---|---|
429 Too Many Requests | Yes | Wait for Retry-After, then retry with jitter |
502, 503, or 504 | Yes, when the operation is safe to replay | Back off and retry a bounded number of times |
| Connection failure before a request is sent | Yes | Retry with backoff |
| Connection loss or timeout after a request may have been sent | Only after reconciliation | Check logs or reuse an idempotency key before replaying |
400 or 422 | No | Correct the request |
401 or 403 | No | Correct credentials or entitlements |
402 | No | Resolve the credit balance or billing configuration |
404 | No, unless eventual resource creation is expected | Verify the path, identifier, and access scope |
409 IDEMPOTENCY_KEY_REUSED | No | Reuse the original body or submit a new logical batch with a new key |
Use the response body's retryability indicator when one is present. A successful no-result response is terminal and should not enter the transient-error retry path.
Apply exponential backoff with jitter
Keep retries bounded. A practical schedule is approximately 1, 2, 4, and 8 seconds plus random jitter, capped at 30 seconds. Honor a longer Retry-After value when the API supplies one.
import random
import time
def retry_delay(attempt, retry_after=None):
if retry_after is not None:
return max(0, float(retry_after))
return min(30, 2 ** attempt) + random.uniform(0, 0.5)
for attempt in range(4):
response = requests.get(status_url, headers=headers, timeout=(10, 30))
if response.status_code not in {429, 502, 503, 504}:
response.raise_for_status()
break
if attempt == 3:
response.raise_for_status()
time.sleep(retry_delay(attempt, response.headers.get("Retry-After")))The example retries a GET, which is safe to replay. Do not apply the same loop to every POST request.
Make write-like requests replay-safe
JSON bulk email jobs
Send an Idempotency-Key with POST /v1/emails/find/bulk/jobs.
- Derive the key from a stable source batch identifier.
- Store the key before sending the request.
- Reuse the same key only with the identical request body.
- Persist the returned job ID immediately.
- If the first response is lost, replay the same body with the same key.
The API returns the original job ID and sets idempotent_replay: true. Reusing the key with a different body returns 409 with IDEMPOTENCY_KEY_REUSED.
File uploads
File-upload endpoints do not expose an idempotency key. Treat a timeout after submission as an ambiguous file upload. Do not replay it automatically. Check the bulk-enrichment activity in the LeadX platform first. If you must resubmit, use a deterministic source batch identifier so you can detect duplicate output.
Synchronous enrichment
Retry a synchronous enrichment POST automatically only after you receive a retryable response such as 429, 502, or 504. If the connection closes after the server may have completed the lookup, reconcile the result through available logs before replaying it.
Poll bulk jobs responsibly
For JSON bulk email jobs:
- Start with a five-second interval.
- Increase the interval toward 30 seconds for longer jobs.
- Add jitter so multiple workers do not poll simultaneously.
- Treat
done,failed, andhaltedas terminal states. - Fetch partial result pages only when your application can merge the same
external_idmore than once. - Stop polling after an application-defined deadline and alert an operator instead of waiting forever.
The file-upload endpoints are monitored through 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.
Preserve diagnostic context
For every failed request, log the endpoint, HTTP status, attempt number, job ID or external_id, and the LeadX request_id when returned. Do not log API keys, uploaded files, or full contact records.
When you contact LeadX support, include the request_id, UTC timestamp, endpoint, and job ID. See Response codes for error formats.