Going to production
Work through each section before you send production traffic to the Tomba API. Each item links to the page that defines the behavior.
Timeouts
- Set your HTTP client timeout to 180 seconds. Searches and verifications run in real time against the target's mail servers and websites, so a single request can take that long.
- A request that runs longer than 180 seconds ends with
504from the gateway. The body isn't JSON and has noerrorsobject. - The request body can be at most 4 MB. A larger body fails with
413and{"error": "Request Entity Too Large"}. - For lists of inputs, use bulk jobs instead of long-running loops of single requests.
Retries
Retry only the statuses that Errors marks as retryable:
| Status | Retry |
|---|---|
408 | Later, with backoff. |
429 | After Retry-After, following Handle 429 responses. A 429 without Retry-After from email count, email format, location, or email sources means the balance is used up; don't retry it. |
500 | With exponential backoff. |
504 | With backoff, or move the work to a bulk job. |
Don't retry 400, 401, 402, 403, 404, 409, 413, 422, or 451: the same request fails the same way until you change it or your account. The exception is a 422 with type openai_error or search_failed from company search, which you can retry.
Use exponential backoff with a cap and random jitter, and stop after a fixed number of attempts. Failed requests aren't charged; see Credits and usage.
The API has no idempotency keys. Retrying a GET is safe, but retrying a request that creates something can create it twice. Before you retry one of these after a timeout or 5xx, list the resource to check whether the first attempt succeeded:
| Request | A retry can |
|---|---|
POST /v1/bulk/{type} | Create a second job, which counts toward the bulk limits |
POST /v1/leads | Create a second lead with the same address |
POST /v1/leads_lists | Fail with 422 params_invalid when the first attempt created the list, because list names are unique |
POST /v1/keys | Create a second API key |
PUT /v1/keys/{id} | Rotate the key again, invalidating the value from the first attempt |
POST /v1/flag | Return 409 duplicate_record when the first report was stored; treat it as stored |
Repeated requests
Some repeats of a successful request are free. The duplicate-request rule defines which ones, and for how long. Read it before you add your own cache: a cache keyed on different parameters than the rule can cost credits the rule would have waived.
Request IDs
Responses from the API carry an X-Request-ID header. Log it with each request; quote it when you contact support about a failed request.
You can also set the ID yourself. Send an X-Request-ID header of printable ASCII characters and the API returns the same value. An empty value, or one with other characters, is replaced by a generated ID.
Code
A 504 from the gateway doesn't carry the header. Sending your own ID gives you a value to quote in that case too.
Pagination
Paginated endpoints take a 1-based page. None accepts an offset. The page size and the names of the pagination fields differ between endpoints:
| Endpoint | Parameters | Pagination fields |
|---|---|---|
GET /v1/domain-search | page; limit from a fixed set of values, default 10. The API reference lists the values. | meta.total, meta.pageSize, meta.current, meta.total_pages |
GET /v1/similar | page. Pages always hold 25 companies; limit only sets the credit cost. | meta.total, meta.pageSize, meta.current, meta.total_pages |
POST /v1/reveal/search | page in the JSON body, 1 to 1,000. Pages always hold 50 companies. | meta.total, meta.page, meta.limit, meta.pages |
GET /v1/leads | page; limit default 10. Values above 200 are treated as 200. | data.meta.total, data.meta.pageSize, data.meta.current, data.meta.total_pages |
GET /v1/bulk/{type} | page; limit default 10. Values above 50 are treated as 50. | meta.total, meta.pageSize, meta.current, meta.total_pages |
GET /v1/logs | page; limit 1 to 100, default 20. Larger values fail with 422. | meta.total, meta.page, meta.limit, meta.total_pages |
- On
GET /v1/leadsandGET /v1/bulk/{type},total_pagesis rounded down, so it can miss the last, partial page. Keep requesting pages until one returns fewer items thanlimit. - On domain search and similar domains, a larger
limitcosts more credits; see Credit costs. - On the Free plan, domain search and company search return only the first page; see Free plan limits.
Keys and secrets
- Keep the key and secret on your server, loaded from environment variables or a secrets manager. See Security.
- Use one key per integration, so you can rotate or delete one without touching the others.
- Every key expires. Track
expires_atfromGET /v1/keys, and alert on theX-Tomba-Key-Expiredheader and theapi_key_expirederror. See Key expiry. - Rotate keys before they expire, following Rotate keys.
Webhooks
Callbacks sent to a webhook_url aren't signed. Put a hard-to-guess token in the URL, reject requests that don't carry it, and respond with a 2xx as soon as you've stored the payload. Webhooks describes the delivery and retry behavior.
Monitoring
- Subscribe to status.tomba.io for incidents and outages.
- Track your throttling headroom from the rate limit headers, or poll
GET /v1/rate-limits, which isn't rate-limited itself. - Track your credit balances with
GET /v1/me,GET /v1/usage, or the quota headers on each response. See Check usage. - Alert on rising
429,5xx, and504rates, and on402 quota_exceeded, which means a balance is too low for the request.