Bulk verification endpoint: submit a list, poll for results

Last updated May 19, 2026API

Bulk verification handles hundreds, thousands, or up to a million addresses per task. It's a two-step asynchronous flow: create a task, then poll for results when the task completes.

The shape of the flow

┌─────────────────┐     ┌─────────────────┐     ┌─────────────────┐
│  Step 1: POST   │────▶│  Processing...  │────▶│  Step 2: GET    │
│  Create task    │     │  (background)   │     │  Get results    │
└─────────────────┘     └─────────────────┘     └─────────────────┘
        │                       │                       │
   Returns task_id          Takes time             Returns results
  1. POST your list → receive a task_id.
  2. Poll the get-results endpoint → receive status and (when complete) full results.

Step 1 — create the task

Endpoint details

PropertyValue
URLhttps://app.validemailchecker.com/api/verify-bulk
MethodPOST
Content-Typeapplication/json
AuthenticationBearer token (API key)
Max emails per task1,000,000

Request body

json
{
  "emails": ["user1@example.com", "user2@example.com", "user3@example.com"],
  "name": "Q2 newsletter cleanup"
}
FieldTypeRequiredDescription
emailsarray<string>YesArray of addresses to verify
namestringNoFriendly task name — helps you find it later in the dashboard

cURL example

bash
curl -X POST https://app.validemailchecker.com/api/verify-bulk \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "emails": ["user1@example.com", "user2@example.com"],
    "name": "Q2 newsletter cleanup"
  }'
API documentation example for the bulk-verify endpoint, showing a POST request with an emails array and the task_id response
Step 1 — POST the list, capture the `task_id` from the response.

Response

json
{
  "task_id": "VECGE7KL3DQ",
  "status": "processing",
  "total_emails": 2,
  "is_chunked": false,
  "total_chunks": 1,
  "partial_failure": false,
  "failed_emails_count": 0,
  "credits_refunded": 0,
  "message": "Bulk verification task created successfully"
}
Save the task_id
It is the only handle on the running job. Without it you cannot retrieve results — and there's no list endpoint to look it up retroactively (use the dashboard's Uploads & Results page if you lose it).

Step 2 — get the results

Endpoint details

PropertyValue
URLhttps://app.validemailchecker.com/api/get-results/{task_id}
MethodGET
AuthenticationBearer token (API key)

cURL example

bash
curl https://app.validemailchecker.com/api/get-results/VECGE7KL3DQ \
  -H "Authorization: Bearer YOUR_API_KEY"
API documentation example for the get-results endpoint, showing a GET request with the task_id in the URL path and the full results payload returned
Step 2 — GET with the `task_id` in the path, results come back when status is `completed`.

Response (completed task)

json
{
  "task_id": "VECGE7KL3DQ",
  "name": "Q2 newsletter cleanup",
  "status": "completed",
  "progress_percentage": 100,
  "count_checked": 2,
  "count_total": 2,
  "results": {
    "support@validemailchecker.com": {
      "email": "support@validemailchecker.com",
      "status": "role_account",
      "is_valid": true,
      "is_disposable": false,
      "is_role_account": true,
      "is_catch_all": false,
      "is_free_email": false,
      "mx_found": true,
      "domain": "validemailchecker.com",
      "risk_score": 7,
      "deliverability": "high",
      "credits_used": 1,
      "verified_at": "2026-05-19T21:17:06.460Z",
      "reason": "Role account detected"
    },
    "username@gmail.com": {
      "email": "username@gmail.com",
      "status": "inbox_full",
      "is_valid": false,
      "is_disposable": false,
      "is_role_account": false,
      "is_catch_all": false,
      "is_free_email": true,
      "mx_found": true,
      "domain": "gmail.com",
      "risk_score": 80,
      "deliverability": "low",
      "credits_used": 1,
      "verified_at": "2026-05-19T21:17:06.460Z",
      "reason": "Inbox full"
    }
  }
}
Watch progress_percentage
Check progress_percentage for in-progress tasks. Full results only appear when status is completed.

Task statuses

StatusMeaning
pendingTask created, waiting for processing to start
processingVerification is running
completedAll emails verified, results ready
failedTask failed (rare, usually a system issue)
15-day retention
Tasks and their results are stored for 15 days, then automatically deleted. Download results before then if you need them long-term. After deletion, the get-results endpoint returns 404 Task not found.

Polling strategy

Bulk verification is asynchronous. Poll the get-results endpoint at sensible intervals — don't hammer it.

Simple polling loop (Node.js)

javascript
async function waitForResults(taskId, maxWaitSeconds = 600) {
  const startTime = Date.now();
  const pollInterval = 5000; // 5 seconds

  while (Date.now() - startTime < maxWaitSeconds * 1000) {
    const response = await fetch(
      `https://app.validemailchecker.com/api/get-results/${taskId}`,
      { headers: { Authorization: `Bearer ${process.env.VEC_API_KEY}` } }
    );

    const data = await response.json();

    if (data.status === 'completed') return data.results;
    if (data.status === 'failed')    throw new Error('Task failed');

    console.log(`Progress: ${data.progress_percentage}%`);
    await new Promise(r => setTimeout(r, pollInterval));
  }

  throw new Error('Timeout waiting for results');
}
List sizeSuggested interval
1–100 emailsEvery 3 seconds
100–1,000 emailsEvery 5 seconds
1,000–10,000 emailsEvery 10 seconds
10,000+ emailsEvery 30 seconds
Do not poll too aggressively
The get-results endpoint has a 120/minute rate limit — generous, but a tight while-loop with no sleep will burn through it fast. Keep at least a 5-second floor.

How long does the task take?

List sizeTypical time
100 emailsSeconds
1,000 emails1–2 minutes
10,000 emails5–10 minutes
100,000 emails15–30 minutes
1,000,000 emails1–2 hours

Times vary based on the mix of email domains and how quickly each mail server responds. Some domains use greylisting that intentionally delays verification.

Error responses

Task creation errors

json
{
  "error": "Insufficient credits",
  "required": 1000,
  "current_balance": 500,
  "message": "You need 1000 credits but only have 500 available"
}

Result-retrieval errors

json
{
  "error": "Task not found"
}

See rate limits and error handling for the full error matrix.

Defensive get-results wrapper

javascript
async function safeGetResults(taskId) {
  const response = await fetch(
    `https://app.validemailchecker.com/api/get-results/${taskId}`,
    { headers: { Authorization: `Bearer ${process.env.VEC_API_KEY}` } }
  );

  if (response.status === 404) throw new Error('Task not found — may have expired');
  if (response.status === 401) throw new Error('Invalid API key');
  if (response.status === 403) throw new Error('Unauthorized — task belongs to a different account');

  return await response.json();
}

Common questions

How are credits charged?

Credits are reserved when the task is created (so the count is locked in upfront). Any address that comes back unknown is auto-refunded after the task completes.

Can I cancel a running task?

Tasks cannot be cancelled once started. Credits are reserved at creation, so submit the right list the first time.

What if my process crashes mid-poll?

The task keeps running on our side. Call get-results again with the same task_id to resume checking status. The task itself is independent of your polling process.

Do API-created tasks show in the dashboard?

Yes — they appear on the Uploads & Results page like dashboard-created tasks. Useful if you want to download results as CSV/XLSX from the dashboard instead of consuming them through the API.

Next steps