Skip to main content
Bulk verification is designed for processing large lists of email addresses in a single operation. Instead of making one request per address, you submit an array of emails and OrbiSearch handles the verification asynchronously — including automatic retries for addresses that return transient errors or are greylisted. You can then poll for status and retrieve the results when the job completes.

When to use bulk verification

  • List cleaning — verify an existing database of contacts before a campaign or migration.
  • Batch imports — validate addresses in bulk when importing data from a CSV, CRM export, or third-party source.
  • Scheduled enrichment — run periodic verification jobs to keep your data fresh without blocking real-time workflows.
For checking a single address on demand (e.g., at point of entry) or in tools like Clay, use single verification instead.

How bulk verification works

1

Submit a job

Send a POST request to /v1/bulk with a JSON body containing an emails array. OrbiSearch returns a job_id immediately and begins processing in the background.
2

Poll for status

Use GET /v1/bulk/{job_id} to check the job’s progress. Poll every 10–30 seconds for large jobs until the status is complete or failed.
3

Retrieve results

Once the job is complete, fetch all results with GET /v1/bulk/{job_id}/results. Each result contains the same fields as a single verification response.

Submitting a job

Send your list of addresses as a JSON array in the request body.
cURL
curl --request POST \
  --url "https://api.orbisearch.com/v1/bulk" \
  --header "X-API-Key: YOUR_API_KEY" \
  --header "Content-Type: application/json" \
  --data '["[email protected]", "[email protected]", "[email protected]"]'
OrbiSearch returns a confirmation object with the job ID and an estimated cost:
{
  "job_id": "job_abc123",
  "status": "submitted",
  "total_emails": 500,
  "estimated_cost": 100.0
}
OrbiSearch deduplicates the emails array automatically. You are only charged for unique addresses. If your list contains duplicates, they are removed before processing and the cost reflects the unique count.

Checking job status

Poll GET /v1/bulk/{job_id} to track progress.
cURL
curl --request GET \
  --url "https://api.orbisearch.com/v1/bulk/job_abc123" \
  --header "X-API-Key: YOUR_API_KEY"
Example response while the job is running:
{
  "job_id": "job_abc123",
  "status": "in_progress",
  "total_emails": 500,
  "emails_processed": 243,
  "retry_status": "pending",
  "submitted_at": "2024-01-15T10:00:00Z",
  "completed_at": null
}

Job statuses

StatusDescription
pendingThe job has been received but has not started processing yet.
in_progressOrbiSearch is actively verifying addresses.
partial_complete_retryingThe main pass is done; OrbiSearch is retrying greylisted addresses.
completeAll verifications are finished. Results are ready to retrieve.
failedThe job failed. Contact support if this is unexpected.

Retry statuses

Some addresses return a greylisted substatus on the first attempt. OrbiSearch automatically retries these. The retry_status field tracks that process:
retry_statusDescription
noneNo addresses required retrying.
pendingRetry pass has not started yet.
partialRetry pass is in progress.
completeAll retries are finished.
For jobs with thousands of addresses, poll every 10–30 seconds. Polling more frequently than necessary won’t speed up processing and wastes API calls.

Retrieving results

Once status is complete, fetch the full results:
cURL
curl --request GET \
  --url "https://api.orbisearch.com/v1/bulk/job_abc123/results" \
  --header "X-API-Key: YOUR_API_KEY"
Response:
{
  "job_id": "job_abc123",
  "status": "complete",
  "total_results": 500,
  "results": [
    {
      "email": "[email protected]",
      "status": "safe",
      "substatus": "deliverable",
      "explanation": "The mailbox exists and is accepting mail.",
      "email_provider": "Google Workspace",
      "is_disposable": false,
      "is_role_account": false,
      "is_free": false
    }
  ],
  "pending_retries": [],
  "retry_count": 0
}
Each object in the results array follows the same schema as a single verification response. See Handling Results for guidance on acting on each status.

Complete example

The following example submits a bulk job, polls until it completes, and prints the results.
import time
import requests

API_KEY = "YOUR_API_KEY"
BASE_URL = "https://api.orbisearch.com"
HEADERS = {"X-API-Key": API_KEY, "Content-Type": "application/json"}

emails = [
    "[email protected]",
    "[email protected]",
    "[email protected]",
    # ... more addresses
]

# Step 1: Submit the job
submit_response = requests.post(
    f"{BASE_URL}/v1/bulk",
    headers=HEADERS,
    json=emails,
)
submit_response.raise_for_status()
job = submit_response.json()
job_id = job["job_id"]
print(f"Job submitted: {job_id} ({job['total_emails']} emails, estimated cost: {job['estimated_cost']} credits)")

# Step 2: Poll until complete
while True:
    status_response = requests.get(
        f"{BASE_URL}/v1/bulk/{job_id}",
        headers=HEADERS,
    )
    status_response.raise_for_status()
    status = status_response.json()

    print(f"Status: {status['status']}{status['emails_processed']}/{status['total_emails']} processed")

    if status["status"] == "complete":
        break
    elif status["status"] == "failed":
        raise RuntimeError(f"Job {job_id} failed.")

    time.sleep(15)  # poll every 15 seconds

# Step 3: Retrieve results
results_response = requests.get(
    f"{BASE_URL}/v1/bulk/{job_id}/results",
    headers=HEADERS,
)
results_response.raise_for_status()
data = results_response.json()

for result in data["results"]:
    print(f"{result['email']}: {result['status']} ({result['substatus']})")

Deduplication and credits

OrbiSearch deduplicates your submitted list before processing. If the same address appears more than once, it is verified once and the result is applied to all occurrences. You are charged 0.2 credits per unique email. The estimated_cost in the submit response reflects the deduplicated count. Results cached from a previous job (within 24 hours) are also free — OrbiSearch will not re-verify an address it already has a fresh result for.
The estimated_cost is an estimate based on the unique email count at submission time. Cached hits reduce the actual cost, but the final charge is determined at job completion.