Migrate from Voila Norbert
This guide maps the Voila Norbert API (/search/name, /search/domain, /contacts, /massives, /verifier, /enrich, and /account) to Tomba. Voila Norbert API behavior checked on 2026-09-30 against the Voila Norbert API reference.
Before you begin
- Get your API key (
ta_…) and secret (ts_…); see Authentication. - Check what each endpoint costs in Credit costs and your plan's throttling in Limits by plan.
- Voila Norbert can return a name search while it's still running, with
searching: true, and you poll the contact until it finishes. Tomba returns the finished result in the response. Set your HTTP client timeout to at least 180 seconds.
Endpoint mapping
Replace the base URL https://api.voilanorbert.com/2018-01-08 with https://api.tomba.io/v1. Voila Norbert searches are POST requests; Tomba lookups are GET requests with query parameters.
| Voila Norbert | Tomba | Notes |
|---|---|---|
POST /search/name | GET /v1/email-finder | |
GET /contacts/{id} (polling a search) | No equivalent | Remove the polling loop. |
POST /search/domain | GET /v1/domain-search | |
POST /verifier/upload for one address | GET /v1/email-verifier | One address per request. |
POST /verifier/upload for a list | POST /v1/bulk/verifier | See Email verifier. |
GET /verifier/{token}, .../download | GET /v1/bulk/verifier/{id}/progress, .../download | See Lifecycle. |
POST /enrich/upload for one address | GET /v1/enrich | |
POST /enrich/upload for a list | POST /v1/bulk/enrich | See Email enrichment. |
GET /enrich/{token}, .../download | GET /v1/bulk/enrich/{id}/progress, .../download | See Lifecycle. |
POST /massives/ | POST /v1/bulk/finder | See Bulk and async jobs. |
GET /massives/{id} | GET /v1/bulk/finder/{id}/progress | |
DELETE /massives/{id} | DELETE /v1/bulk/finder/{id}/delete | |
.../pause, .../resume, .../rerun | No equivalent | |
GET /lists/, POST /lists/ | GET /v1/leads_lists, POST /v1/leads_lists | Tomba doesn't add search results to a list; create leads with POST /v1/leads. |
GET /account/, GET /organization/credits/ | GET /v1/me | See Check usage. |
Authentication changes
Voila Norbert uses HTTP Basic authentication with any user name and the API token as the password. Tomba needs two headers, X-Tomba-Key and X-Tomba-Secret:
Code
Tomba API keys expire; plan their rotation with Key expiry.
Parameter mapping
| Voila Norbert | Tomba | Notes |
|---|---|---|
Name search name | full_name | Or send first_name and last_name. |
Name and domain search domain | domain | The email finder returns 400 unknown_record for a disposable or webmail domain. |
Name and domain search company | company | A domain gives better results. |
Domain search page | page | Tomba also takes limit; the cost depends on it. See Credit costs. |
webhook | webhook_url | Optional in Tomba. The result is still returned in the response, and also posted to your URL when it qualifies. See Per-request callbacks. Bulk jobs send a signed event to their own webhook_url; see Bulk job events. |
list_id | No equivalent | |
Verifier and enrich data, file | list, file | Bulk jobs. A JSON list has one address per line, like data. |
Response field mapping
Tomba wraps every result in data.
| Voila Norbert | Tomba | Notes |
|---|---|---|
id | No equivalent | There's no contact record to fetch later. |
searching | No equivalent | Every Tomba response is final. |
name | data.full_name | |
email.email | data.email | null when no address is found; the status is still 200. |
email.score | data.score, data.verification.status | See Status mapping. |
company.name | data.company | |
company.url | data.website_url | A domain in Tomba, not a URL. |
Domain search result[].email.email | data.emails[].email | |
Domain search result[].name | data.emails[].full_name | |
Domain search result[].is_new | No equivalent | |
Domain search total | meta.total | |
Domain search has_next | meta.current < meta.total_pages | |
Verifier email | data.email.email | An empty string when the domain is disposable. |
Verifier is_deliverable, is_risky, is_bounce | data.email.result | See Status mapping. |
Verifier error_msg | No equivalent |
Tomba also returns position, linkedin, country, and the sources where the address was found; see Person. The verifier fields are in Email verifier response.
Status mapping
Voila Norbert scores a found address 100 when it was verified against the mail server, 80 when it wasn't, and 5 when it comes only from external sources. Tomba's data.score is computed differently, so read the mail server check from data.verification.status instead:
Voila Norbert email.score | Tomba verification.status |
|---|---|
100 | valid |
Tomba can also return accept_all, invalid, or unknown, or null when the address hasn't been verified.
For the verifier, map the webhook result flags to Tomba's result:
| Voila Norbert verifier flag | Tomba result |
|---|---|
is_deliverable | deliverable |
is_risky | risky |
is_bounce | undeliverable |
Compare status case-insensitively. Each value is defined in Status values and Result values.
Error mapping
Tomba returns an errors object with type, message, and code:
| Voila Norbert | Tomba | What to do |
|---|---|---|
400 bad request | 422 params_invalid | Fix the parameter named in the message. |
401 authentication failed | 400 authentication_failed (missing or malformed), 400 api_key_expired, 401 authentication_failed (wrong key or secret) | Check both headers; rotate an expired key. |
402 insufficient credits | 402 quota_exceeded | Wait for your usage window to renew or add credits. |
402 missing webhook | Not returned | Tomba doesn't need a webhook. |
429 rate limit exceeded | 429 rate_limit | Retry after the Retry-After delay instead of X-RateLimit-Reset; see Handle 429 responses. |
5xx | 500 api_error | Retry with backoff. |
Also handle 451 claimed_email when the owner of an address asked Tomba to stop processing it. Error responses don't consume credits; every type is listed in Errors.
Bulk and async jobs
Voila Norbert's /massives/ endpoint takes CSV content or a file URL, and sends the result file to a required webhook. Tomba's finder bulk job takes an uploaded CSV file and sends no webhook: you poll its progress and download the results.
Voila Norbert /massives/ field | Tomba finder field | Notes |
|---|---|---|
content, file_url | file | Upload the CSV as multipart/form-data. |
col_name | first_name_field_index and last_name_field_index, or full_name_field_index | Tomba's indexes start at 0. Without them, the columns are found by their header. |
col_domain, col_company | domain_field_index, company_name_field_index | |
webhook | No equivalent | Poll the job's progress. |
rerun_frequency | No equivalent | Create a new job; running the same list again is charged again. |
webhook_file_retention_time | No equivalent | Results can be downloaded for 180 days. |
Verification and enrichment lists become verifier and enrich jobs; see Email verifier and Email enrichment. Each job is created, launched, polled, and downloaded as CSV; see Lifecycle. A job is charged when you first download its results; see Billing.
Code example
This function replaces a POST /search/name call and its polling. It returns Voila Norbert's contact field names, with email set to null when Tomba finds no address. It needs Node.js 18 or later.
Code
Cutover checklist
- Store the Tomba key and secret as
TOMBA_API_KEYandTOMBA_SECRET_KEY, and send them asX-Tomba-KeyandX-Tomba-Secretinstead of Basic authentication. - Change the base URL, send lookups as
GETrequests, and sendnameasfull_name. - Remove the
searchingcheck and the/contacts/{id}polling loop. - Replace score thresholds with
data.verification.status, comparing it case-insensitively. - Move
/massives/,/verifier/upload, and/enrich/uploadlists tofinder,verifier, andenrichbulk jobs, setting zero-based field indexes where the headers don't name the columns, and replace their webhooks withwebhook_url. - Handle
402for exhausted credits, retry429afterRetry-After, and drop addresses that return451. - Run the same sample of people and addresses through both services and compare the results before you switch production traffic.