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

NeedRecommended workflow
One result during an interactive workflowUse the corresponding synchronous endpoint
Up to 25,000 email records from an applicationCreate a JSON bulk email job
A CSV or XLSX workflow operated by a personUse a file-upload endpoint and monitor it in the LeadX platform
More records than one request acceptsSplit 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 typeConnect timeoutResponse timeout
Synchronous email discovery or validation10 seconds65 seconds
Other synchronous enrichment requests10 seconds45 seconds
Bulk submission, job status, and result pages10 seconds30 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

OutcomeAutomatic retry?Action
429 Too Many RequestsYesWait for Retry-After, then retry with jitter
502, 503, or 504Yes, when the operation is safe to replayBack off and retry a bounded number of times
Connection failure before a request is sentYesRetry with backoff
Connection loss or timeout after a request may have been sentOnly after reconciliationCheck logs or reuse an idempotency key before replaying
400 or 422NoCorrect the request
401 or 403NoCorrect credentials or entitlements
402NoResolve the credit balance or billing configuration
404No, unless eventual resource creation is expectedVerify the path, identifier, and access scope
409 IDEMPOTENCY_KEY_REUSEDNoReuse 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, and halted as terminal states.
  • Fetch partial result pages only when your application can merge the same external_id more 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.