Signup protection Pricing Documentation Sign up Log in

API reference

Bulk verification endpoint

Note

Bulk verification is available on all paid plans.

The bulk verification endpoints check a list of up to 25,000 email addresses and domains in one request. UserCheck verifies the list in the background, and each item gets the response body the email or domain endpoint returns, without blocklisted (see the result item object).

These are the same verifications the dashboard's bulk tool creates. Only production keys list the ones started from the dashboard and read their results.

How it works

  1. Submit the list with POST /bulk-verifications. The response is 202 Accepted with the verification's id.
  2. Fetch the verification with GET /bulk-verifications/{id} until its status is completed or cancelled. Its items counts show progress.
  3. Page through the results with GET /bulk-verifications/{id}/results.

There is no webhook for a finished verification. Poll the verification instead.

Your account runs one verification at a time: a verification submitted while another is running waits with the status queued and starts when the earlier one finishes. An account can have at most 10 verifications queued or running at once, counting the ones started from the dashboard.

A verification belongs to the environment of the key that submitted it, like blocklist entries and Gates. Verifications started from the dashboard belong to production. A key only lists and reads the verifications of its own environment: an ID from another environment returns 404, the same as an ID from another account. The queue and the limit of 10 are shared across environments, like credits.

Each item that is verified costs 1 credit, the same as a single lookup. Items that are rejected as invalid, cancelled, or not verified cost nothing. Submitting, polling, and reading results are free, and submitting does not reserve credits: if your account runs out of monthly credits partway through, the remaining items are reported as rate_limited.

Endpoints

Method and path Purpose
POST /bulk-verifications Create a bulk verification
GET /bulk-verifications List bulk verifications
GET /bulk-verifications/{id} Get a bulk verification
GET /bulk-verifications/{id}/results List results
POST /bulk-verifications/{id}/cancel Cancel a bulk verification

All endpoints live under https://api.usercheck.com and authenticate with your API key, like the rest of the API. See Authentication.

The bulk verification object

{
  "id": "bv_x7Kq2m",
  "status": "in_progress",
  "items": {
    "total": 3,
    "pending": 1,
    "success": 1,
    "invalid": 1,
    "rate_limited": 0,
    "error": 0,
    "cancelled": 0
  },
  "created_at": "2026-11-03T10:15:42.000000Z",
  "updated_at": "2026-11-03T10:15:43.000000Z"
}
Field Type Description
id string The verification's ID, starting with bv_. Treat it as an opaque string.
status string queued, in_progress, completed, or cancelled. See statuses.
items object How many items are in each item status. Every key is always present, and total is their sum.
created_at string ISO 8601 timestamp of the submission.
updated_at string ISO 8601 timestamp of the last change. It moves while the verification runs.

Endpoints that return one verification wrap it in a top-level data key. The list endpoint returns the same object without items.

Verification statuses

Status Meaning
queued Waiting for another verification on your account, in any environment, to finish.
in_progress Being verified.
completed Every item has reached a final status.
cancelled Stopped by a cancel before every item was verified.

completed and cancelled are final. A verification in either status never changes again.

The result item object

{
  "value": "[email protected]",
  "type": "email",
  "status": "success",
  "result": {
    "status": 200,
    "email": "[email protected]",
    "normalized_email": "[email protected]",
    "domain": "github.com",
    "domain_authority": 96,
    "tld_trust": 1,
    "domain_age_in_days": 6871,
    "mx": true,
    "mx_records": [
      { "hostname": "github-com.mail.protection.outlook.com", "priority": 0 }
    ],
    "mx_providers": [
      { "slug": "microsoft", "type": "mailbox", "grade": "professional" }
    ],
    "spf": "relaxed",
    "dmarc": "quarantine",
    "disposable": false,
    "public_domain": false,
    "relay_domain": false,
    "free_subdomain": false,
    "alias": false,
    "role_account": false,
    "spam": false,
    "did_you_mean": null
  }
}
Field Type Description
value string The item as it was verified: lowercased and trimmed. See request body.
type string email when the value contains @, otherwise domain.
status string The item's status. See item statuses.
result object or null The response body the email or domain endpoint returned for the item, including the fields paid plans add, except blocklisted. Present for success and invalid items, null for every other status.

Results never include blocklisted. To block the domains on your blocklist, use the domain.blocklisted condition in Gates.

Item statuses

Status result Credits Meaning
pending null 0 Not verified yet.
success Response body 1 Verified. result is the 200 body.
invalid Error body 0 Rejected as an invalid email address or domain. result is the 400 body, for example {"status": 400, "error": "The domain is invalid."}.
rate_limited null 0 Not verified because the lookup was refused with 429, which happens when your account has used its monthly credits.
error null 0 Not verified because of a failure on UserCheck's side.
cancelled null 0 Not verified because the verification was cancelled first.

Items are not retried once they reach rate_limited or error. To verify them, submit them again in a new verification.

Create a bulk verification

Submit a list of email addresses and domains to verify.

Endpoint

POST /bulk-verifications

Request body

Field Type Required Description
items array Yes A JSON array of 1 to 25,000 strings. Each string is an email address or a domain, and the two can be mixed.

Each item is lowercased, and surrounding whitespace, commas, and semicolons are removed. After that:

  • An item must be 255 characters or fewer.
  • An item that is blank, or not a string, fails the whole request rather than being skipped, so results always line up one to one with the array you sent.
  • Duplicates are kept. Each copy is verified and billed.
  • Values are not checked for format at submission. An item that is not a valid email address or domain is reported as invalid in the results and costs nothing.

A request with invalid items lists up to 10 of them in its 422 response, keyed by their position in the array (items.0 is the first item).

Headers

Header Required Description
Idempotency-Key No A key of your choosing, 1 to 255 printable ASCII characters without spaces. See idempotent submissions.

Request example

curl -X POST "https://api.usercheck.com/bulk-verifications" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "items": [
      "[email protected]",
      "github.com",
      "not-a-domain"
    ]
  }'

Success response (202)

The Location header holds the verification's URL.

{
  "data": {
    "id": "bv_x7Kq2m",
    "status": "in_progress",
    "items": {
      "total": 3,
      "pending": 3,
      "success": 0,
      "invalid": 0,
      "rate_limited": 0,
      "error": 0,
      "cancelled": 0
    },
    "created_at": "2026-11-03T10:15:42.000000Z",
    "updated_at": "2026-11-03T10:15:42.000000Z"
  }
}

status is queued instead when another verification on your account is running, in any environment.

Idempotent submissions

Send an Idempotency-Key header to make a submission safe to retry. If a verification with the same Idempotency-Key was already submitted in the key's environment, the API does not create a second one. It answers 200 with the existing verification as it is now, a Location header, and Idempotent-Replayed: true.

The key is matched before anything else is checked: a retry is answered with the original verification even if its body differs, and is never refused by the limit on queued verifications. An Idempotency-Key is unique within one environment of your account and does not expire. Sent from another environment, the same Idempotency-Key creates a separate verification.

List bulk verifications

Get a paginated list of the verifications in the key's environment, newest first. For a production key, that includes the ones started from the dashboard. The list returns each verification without its items counts; get a single verification for those.

Endpoint

GET /bulk-verifications

Query parameters

Parameter Type Required Description
page integer No Page to return (default: 1)
per_page integer No Verifications per page, 1 to 100 (default: 25). A value outside that range returns 422.

Request example

curl -X GET "https://api.usercheck.com/bulk-verifications" \
  -H "Authorization: Bearer YOUR_API_KEY"

Success response (200)

{
  "data": [
    {
      "id": "bv_x7Kq2m",
      "status": "completed",
      "created_at": "2026-11-03T10:15:42.000000Z",
      "updated_at": "2026-11-03T10:15:44.000000Z"
    }
  ],
  "links": {
    "first": "https://api.usercheck.com/bulk-verifications?page=1",
    "last": "https://api.usercheck.com/bulk-verifications?page=1",
    "prev": null,
    "next": null
  },
  "meta": {
    "current_page": 1,
    "from": 1,
    "last_page": 1,
    "links": [
      {
        "url": null,
        "label": "« Previous",
        "active": false
      },
      {
        "url": "https://api.usercheck.com/bulk-verifications?page=1",
        "label": "1",
        "active": true
      },
      {
        "url": null,
        "label": "Next »",
        "active": false
      }
    ],
    "path": "https://api.usercheck.com/bulk-verifications",
    "per_page": 25,
    "to": 1,
    "total": 1
  }
}

Get a bulk verification

Get one verification with its status and item counts. Poll this endpoint to follow a verification's progress.

Endpoint

GET /bulk-verifications/{id}

Path parameters

Parameter Type Required Description
id string Yes The verification's ID, for example bv_x7Kq2m

Request example

curl -X GET "https://api.usercheck.com/bulk-verifications/bv_x7Kq2m" \
  -H "Authorization: Bearer YOUR_API_KEY"

Success response (200)

{
  "data": {
    "id": "bv_x7Kq2m",
    "status": "completed",
    "items": {
      "total": 3,
      "pending": 0,
      "success": 2,
      "invalid": 1,
      "rate_limited": 0,
      "error": 0,
      "cancelled": 0
    },
    "created_at": "2026-11-03T10:15:42.000000Z",
    "updated_at": "2026-11-03T10:15:44.000000Z"
  }
}

List results

Get one page of a verification's items with their results, in the order they were submitted.

Endpoint

GET /bulk-verifications/{id}/results

Path parameters

Parameter Type Required Description
id string Yes The verification's ID

Query parameters

Parameter Type Required Description
per_page integer No Items per page, 1 to 1,000 (default: 1,000)
cursor string No Where the page starts. Pass the meta.next_cursor of the previous page; omit it for the first page.

Request example

curl -X GET "https://api.usercheck.com/bulk-verifications/bv_x7Kq2m/results?per_page=2" \
  -H "Authorization: Bearer YOUR_API_KEY"

Success response (200)

{
  "data": [
    {
      "value": "[email protected]",
      "type": "email",
      "status": "success",
      "result": {
        "status": 200,
        "email": "[email protected]",
        "normalized_email": "[email protected]",
        "domain": "github.com",
        "domain_authority": 96,
        "tld_trust": 1,
        "domain_age_in_days": 6871,
        "mx": true,
        "mx_records": [
          { "hostname": "github-com.mail.protection.outlook.com", "priority": 0 }
        ],
        "mx_providers": [
          { "slug": "microsoft", "type": "mailbox", "grade": "professional" }
        ],
        "spf": "relaxed",
        "dmarc": "quarantine",
        "disposable": false,
        "public_domain": false,
        "relay_domain": false,
        "free_subdomain": false,
        "alias": false,
        "role_account": false,
        "spam": false,
        "did_you_mean": null
      }
    },
    {
      "value": "not-a-domain",
      "type": "domain",
      "status": "invalid",
      "result": {
        "status": 400,
        "error": "The domain is invalid."
      }
    }
  ],
  "meta": {
    "per_page": 2,
    "next_cursor": "2"
  },
  "links": {
    "next": "https://api.usercheck.com/bulk-verifications/bv_x7Kq2m/results?per_page=2&cursor=2"
  }
}

links.next is the URL of the next page, with your per_page carried over. On the last page, meta.next_cursor and links.next are both null.

The cursor only moves forward. An item still pending when its page was read is not shown again on a later page, so the results are complete once the verification is completed or cancelled. Read them after that.

Cancel a bulk verification

Stop a queued or running verification. Its items that have not been verified yet become cancelled and cost nothing. Items verified before the cancel keep their results and their credits.

Endpoint

POST /bulk-verifications/{id}/cancel

Path parameters

Parameter Type Required Description
id string Yes The verification's ID

Request example

curl -X POST "https://api.usercheck.com/bulk-verifications/bv_x7Kq2m/cancel" \
  -H "Authorization: Bearer YOUR_API_KEY"

Success response (200)

{
  "data": {
    "id": "bv_x7Kq2m",
    "status": "cancelled",
    "items": {
      "total": 3,
      "pending": 0,
      "success": 1,
      "invalid": 0,
      "rate_limited": 0,
      "error": 0,
      "cancelled": 2
    },
    "created_at": "2026-11-03T10:15:42.000000Z",
    "updated_at": "2026-11-03T10:15:43.000000Z"
  }
}

Cancelling a verification that is already cancelled answers 200 again, so a retried cancel is harmless. A verification that has already completed cannot be cancelled and returns 409.

Rate limits

The bulk verification endpoints have their own rate limit: 120 requests per minute per account, shared by all its keys. Polling and paging never use up the per-second rate limit your lookups and Gates decisions rely on, and the items a verification checks don't count against it either.

Exceeding the limit returns 429 with a Retry-After header:

{
  "error": "rate_limit_exceeded",
  "message": "Too many requests"
}

Requests with a missing, mistyped, or revoked key are limited to 5 per hour per IP address. Past that, they return 429 instead of 401.

Error responses

Unauthorized (401)

{
  "message": "API key is required"
}

message is "API key is required" when the request carried no API key, and "Invalid API key" when the key is not recognized.

{
  "message": "This feature requires a Pro account"
}

Verification not found (404)

{
  "message": "Bulk verification not found."
}

Returned for an ID that does not exist, and for one that belongs to another account or to another environment than the key's.

Already completed (409)

Returned when cancelling a verification that has already completed.

{
  "message": "This bulk verification has already completed."
}

Misdirected request (421)

Rare, and only on submissions and cancellations. Nothing was created or cancelled, and the request can be retried as is.

{
  "message": "Bulk verification submissions and cancellations are processed in eu-west-1, and this request reached another region. Retry it."
}

Validation failed (422)

{
  "message": "You can submit up to 25,000 items per bulk verification. This request contains 25001.",
  "errors": {
    "items": ["You can submit up to 25,000 items per bulk verification. This request contains 25001."]
  }
}

message carries the first error from errors, with (and N more errors) appended when there is more than one. Errors on single items are keyed by position:

{
  "message": "Each item must be 255 characters or fewer.",
  "errors": {
    "items.2": ["Each item must be 255 characters or fewer."]
  }
}

An invalid Idempotency-Key header is reported under the Idempotency-Key key.

Too many verifications queued or running (429)

Returned when your account already has 10 verifications queued or running, counting every environment and the ones started from the dashboard. Your key may not be able to list or cancel all of them: it only sees its own environment's. It carries no Retry-After header: a new submission is accepted as soon as one of them finishes.

{
  "message": "Your account has 10 bulk verifications queued or running, across all its environments and the dashboard. Submit more once one finishes."
}

A 429 from the rate limit has a different body, shown above.

Previous
Blocklist endpoint