Migrate from ZeroBounce
This guide maps the ZeroBounce API v2 (single validation, batch validation, the bulk file API, and credit balance) to the Tomba Email Verifier and bulk verifier jobs. ZeroBounce API behavior checked on 2026-09-30 against the ZeroBounce 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
ZeroBounce uses the hosts api.zerobounce.net (or api-us, api-eu) and bulkapi.zerobounce.net. Tomba serves everything from api.tomba.io.
| ZeroBounce | Tomba |
|---|---|
GET /v2/validate (or POST) | GET /v1/email-verifier |
POST /v2/validatebatch | POST /v1/bulk/verifier, then PUT /v1/bulk/verifier/{id} to launch |
POST https://bulkapi.zerobounce.net/v2/sendfile | POST /v1/bulk/verifier, then PUT /v1/bulk/verifier/{id} to launch |
GET /v2/filestatus | GET /v1/bulk/verifier/{id}/progress |
GET /v2/getfile | GET /v1/bulk/verifier/{id}/download |
GET /v2/deletefile | DELETE /v1/bulk/verifier/{id}/delete |
GET /v2/getcredits | GET /v1/me |
Authentication changes
ZeroBounce reads the key from the api_key parameter, in the query string or the request body. Tomba needs two headers, X-Tomba-Key and X-Tomba-Secret, and doesn't accept credentials in the query string or body:
Code
Tomba API keys expire; plan their rotation with Key expiry.
Parameter mapping
| ZeroBounce | 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. |
ip_address | No equivalent | |
timeout | No equivalent | Set the timeout in your HTTP client. |
activity_data, verify_plus | No equivalent | |
Batch email_batch[].email_address | list | A newline-separated string of addresses. |
Send file file | file | Must be sent with the content type text/csv. Also send name. |
email_address_column | email_field_index | Zero-based in Tomba: subtract 1. |
has_header_row | No equivalent | Tomba always reads the first row as a header. |
return_url | webhook_url | Receives a signed event when the job ends. See Bulk job events. |
first_name_column, last_name_column, gender_column | No equivalent | |
ip_address_column, remove_duplicate, allow_phase_2 | No equivalent | |
file_id | {id} in the path | |
Get file download_type, activity_data | No equivalent | Tomba's download type selects full or valid rows; see Lifecycle. |
Response field mapping
ZeroBounce 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.
| ZeroBounce | Tomba | Notes |
|---|---|---|
address | data.email.email | An empty string when the domain is disposable. |
status | data.email.status, data.email.result | See Status mapping. |
sub_status | No equivalent | Tomba reports greylisted, block, disposable, and mx_check as fields. |
free_email | data.email.webmail | |
catchall_domain | data.email.accept_all | |
mx_found | data.email.mx_check | |
mx_record | data.email.mx.records | An array of MX host names instead of one string. |
smtp_provider | data.email.smtp_provider | |
domain_age_days | No equivalent | data.email.whois.created_date gives the domain's registration date. |
account, domain | No equivalent | Split the address at @. |
did_you_mean | No equivalent | |
active_in_days, active_first_seen | No equivalent | |
firstname, lastname, gender | No equivalent | |
city, region, zipcode, country | No equivalent | |
processed_at | No equivalent | |
error | errors.message | Returned with an HTTP error status; see Error mapping. |
Tomba also returns score, block, greylisted, gibberish, regex, and whois, which have no ZeroBounce counterpart. Read your remaining balance from the X-Verify-Remaining header or GET /v1/me; see Check usage.
Status mapping
ZeroBounce status | sub_status | Tomba status | Tomba result |
|---|---|---|---|
valid | Any | valid | deliverable |
invalid | Any | invalid | undeliverable |
invalid | failed_syntax_check | No status. The request fails with 422 params_invalid. | |
catch-all | Any | accept_all | risky |
unknown | Any | unknown | risky |
do_not_mail | disposable | disposable | Empty string |
do_not_mail | Other sub-statuses | No equivalent. Tomba returns the mailbox result. | |
spamtrap, abuse | No equivalent. Tomba returns the mailbox result. |
ZeroBounce returns some catch-all domains as valid with the accept_all sub-status. Tomba sets status to accept_all whenever the domain's mail server accepts mail for any address. Compare status case-insensitively. Each value is defined in Status values.
Error mapping
ZeroBounce reports failures in the body: an error string on single validation and credits, an errors array on batch validation, and success: false with error_message on the file endpoints. Tomba returns an HTTP error status with an errors object, so check the status code instead:
| ZeroBounce | Tomba | What to do |
|---|---|---|
Invalid API Key or your account ran out of credits | 400 authentication_failed (missing or malformed), 400 api_key_expired, 401 authentication_failed (wrong key or secret) | Check both headers; rotate an expired key. |
Invalid API Key or your account ran out of credits | 402 quota_exceeded | Tomba separates the two cases. Wait for your usage window to renew or add credits. |
| Temporary block after exceeding the rate limit | 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
ZeroBounce's validatebatch returns results in the response. Tomba runs every list as an asynchronous verifier bulk job, so move batch calls to the same flow as your file uploads, or send one GET /v1/email-verifier request per address.
| ZeroBounce | Tomba |
|---|---|
validatebatch with email_batch | POST /v1/bulk/verifier with name and a newline-separated list of addresses |
sendfile with file and email_address_column | POST /v1/bulk/verifier with name, a CSV file, and email_field_index |
| Validation starts on upload | Launch the job: PUT /v1/bulk/verifier/{id} |
Poll filestatus for file_status and complete_percentage | Poll GET /v1/bulk/verifier/{id}/progress for status and progress until status is completed |
The return_url callback | webhook_url. See Bulk job events. |
getfile | GET /v1/bulk/verifier/{id}/download?file=full |
Code
Tomba treats the first row of the file as a header, so add one if your ZeroBounce files have none. 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 /v2/validate and returns ZeroBounce's field names for the values Tomba supplies. 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 removeapi_keyfrom your URLs, request bodies, and logs. - Point single validations at
GET https://api.tomba.io/v1/email-verifier, droptimeout, and set a client timeout of at least 180 seconds. - Replace checks of the
errorfield with HTTP status handling:402,422,429withRetry-After,408, and451. - Translate
statuswith the status mapping, comparing it case-insensitively, and stop branching onsub_status. - Replace
validatebatchandsendfilewithverifierbulk jobs: create, launch, receive thewebhook_urlevent instead ofreturn_url, and download the CSV. - Verify the same sample list with both services and compare the mapped results before you switch production traffic.