Migrate from MillionVerifier
This guide maps the MillionVerifier API (single verification v3, the bulk file API v2, and credits) to the Tomba Email Verifier and bulk verifier jobs. MillionVerifier API behavior checked on 2026-09-29 against the MillionVerifier API reference.
Before you begin
- Get your API key (
ta_…) and secret (ts_…); see Authentication. - Check what a verification costs in Credit costs and your plan's throttling in Limits by plan.
- Set your HTTP client timeout to at least 180 seconds. Tomba returns the finished result in the response.
Endpoint mapping
MillionVerifier uses two hosts, api.millionverifier.com and bulkapi.millionverifier.com. Tomba serves everything from api.tomba.io.
| MillionVerifier | Tomba |
|---|---|
GET https://api.millionverifier.com/api/v3/ | GET /v1/email-verifier |
POST https://bulkapi.millionverifier.com/bulkapi/v2/upload | POST /v1/bulk/verifier, then PUT /v1/bulk/verifier/{id} to launch |
GET /bulkapi/v2/fileinfo | GET /v1/bulk/verifier/{id}/progress |
GET /bulkapi/v2/filelist | GET /v1/bulk/verifier |
GET /bulkapi/v2/download | GET /v1/bulk/verifier/{id}/download |
GET /bulkapi/stop | No equivalent |
GET /bulkapi/v2/delete | DELETE /v1/bulk/verifier/{id}/delete |
GET https://api.millionverifier.com/api/v3/credits | GET /v1/me |
Authentication changes
MillionVerifier reads the key from the query string: api on the single and credits endpoints, key on the bulk endpoints. Tomba needs two headers, X-Tomba-Key and X-Tomba-Secret, and doesn't accept credentials in the query string:
Code
Tomba API keys expire; plan their rotation with Key expiry.
Parameter mapping
| MillionVerifier | Tomba | Notes |
|---|---|---|
api, key | X-Tomba-Key and X-Tomba-Secret headers | |
email | email | Required. A malformed address returns 422 params_invalid instead of a result. |
timeout | No equivalent | Set the timeout in your HTTP client. |
Upload file_contents | file | Also send name, and email_field_index (zero-based) if the header doesn't name the address column. |
Download filter=all | file=full | |
Download filter=ok | file=valid | |
Download filter ok_and_catch_all, unknown, invalid, custom | No equivalent | Download file=full and filter the rows. |
file_id | {id} in the path |
Response field mapping
MillionVerifier returns a flat object. Tomba nests the checks in data.email and lists the public pages where the address appears in data.sources. Field definitions are in Email verifier response.
| MillionVerifier | Tomba | Notes |
|---|---|---|
email | data.email.email | An empty string when the domain is disposable. |
result, resultcode | data.email.status, data.email.result | See Status mapping. |
quality | data.email.result | The closest field; see Result values. |
subresult | No equivalent | Tomba reports greylisted, block, and mx_check as separate fields. |
free | data.email.webmail | |
role | No equivalent | |
didyoumean | No equivalent | |
credits | X-Verify-Remaining header, GET /v1/me | See Check usage. |
executiontime | No equivalent | |
error | errors.message | Returned with an HTTP error status; see Error mapping. |
livemode | No equivalent |
Tomba also returns accept_all, disposable, gibberish, regex, smtp_provider, mx.records, and whois, which have no MillionVerifier counterpart.
Status mapping
MillionVerifier result | resultcode | Tomba status | Tomba result |
|---|---|---|---|
ok | 1 | valid | deliverable |
catch_all | 2 | accept_all | risky |
unknown | 3 | unknown | risky |
error | 4 | No status. The request fails with an HTTP error. | |
disposable | 5 | disposable | Empty string |
invalid | 6 | invalid | undeliverable |
Compare status case-insensitively. Each value is defined in Status values.
Error mapping
MillionVerifier returns errors with HTTP 200, as result: "error" and an error string on the single endpoint, or an error field on the bulk endpoints. Tomba returns an HTTP error status with an errors object, so check the status code instead:
| MillionVerifier | Tomba | What to do |
|---|---|---|
apikey_not_found (single), invalid_api_key (bulk) | 400 authentication_failed (missing or malformed), 400 api_key_expired, 401 authentication_failed (wrong key or secret) | Check both headers; rotate an expired key. |
No email specified | 422 params_invalid | Fix the parameter named in the message. |
insufficient_credits (bulk upload) | 402 quota_exceeded on single requests | For bulk jobs, the balance is checked at the first download instead; see Billing. |
| No equivalent | 429 rate_limit | Retry after the Retry-After delay; see Handle 429 responses. |
| No equivalent | 408 proxy_error | The mailbox check failed. Retry later. |
| No equivalent | 451 claimed_email | The owner of the address asked Tomba to stop processing it. Remove it from your list. |
Error responses don't consume credits. Every type is listed in Errors.
Bulk and async jobs
| MillionVerifier | Tomba |
|---|---|
Upload a file with file_contents | POST /v1/bulk/verifier with name and a CSV file, or a newline-separated list of addresses |
| No start call | Launch the job: PUT /v1/bulk/verifier/{id} |
Poll fileinfo for status and percent | Poll GET /v1/bulk/verifier/{id}/progress for status and progress until status is completed |
The file_ready webhook, set in account settings | webhook_url on the job, which receives a signed bulk.completed event. See Bulk job events. |
download with a filter | GET /v1/bulk/verifier/{id}/download?file=full |
Code
Tomba treats the first row of the file as a header. The launch, progress, and download requests are shown in Lifecycle. Row limits and daily job limits are in Limits, and a job is charged when you first download its results; see Billing.
Code example
This function replaces a call to /api/v3/ and returns MillionVerifier's result and resultcode for the address. It needs Node.js 18 or later.
Code
Cutover checklist
- Store the Tomba key and secret as
TOMBA_API_KEYandTOMBA_SECRET_KEY, send them asX-Tomba-KeyandX-Tomba-Secret, and remove theapiandkeyparameters from your URLs and logs. - Point single verifications at
GET https://api.tomba.io/v1/email-verifier, droptimeout, and set a client timeout of at least 180 seconds. - Replace checks of
result: "error"with HTTP status handling:402,422,429withRetry-After,408, and451. - Translate
resultandresultcodewith the status mapping, comparingstatuscase-insensitively. - Replace file uploads with
verifierbulk jobs: create, launch, receive thewebhook_urlevent instead of thefile_readywebhook, and download the CSV. - Verify the same sample list with both services and compare the mapped results before you switch production traffic.