# Migrate from Clearout

This guide maps the Clearout API v2 email verification endpoints (instant verify, bulk verify, and available credits) to the Tomba [Email Verifier](/api/verifier#email-verifier) and [bulk verifier jobs](/bulks#email-verifier). Clearout API behavior checked on 2026-09-30 against the [Clearout API reference](https://docs.clearout.io/developers/api/email-verify).

## Before you begin

- Get your API key (`ta_…`) and secret (`ts_…`); see [Authentication](/authentication).
- Check what a verification costs in [Credit costs](/usage-and-quotas#credit-costs) and your plan's throttling in [Limits by plan](/rate-limits#limits-by-plan).
- Set your HTTP client timeout to at least 180 seconds. Tomba returns the finished result in the response.

## Endpoint mapping

Replace the base URL `https://api.clearout.io/v2` with `https://api.tomba.io/v1`. Clearout shows the base URL for your account's region under Developer, Reference in its app; replace that one if it differs.

| Clearout                                 | Tomba                                                                 |
| ---------------------------------------- | --------------------------------------------------------------------- |
| `POST /email_verify/instant`             | [`GET /v1/email-verifier`](/api/verifier#email-verifier)              |
| `POST /email_verify/bulk`                | `POST /v1/bulk/verifier`, then `PUT /v1/bulk/verifier/{id}` to launch |
| `GET /email_verify/bulk/progress_status` | `GET /v1/bulk/verifier/{id}/progress`                                 |
| `POST /download/result`                  | `GET /v1/bulk/verifier/{id}/download`                                 |
| `POST /email_verify/list/remove`         | `DELETE /v1/bulk/verifier/{id}/delete`                                |
| `POST /email_verify/list/cancel`         | No equivalent                                                         |
| `POST /email_verify/list`                | `GET /v1/bulk/verifier`                                               |
| `GET /email_verify/getcredits`           | [`GET /v1/me`](/api/account#get-account)                              |

Clearout's single-check endpoints (`/email/verify/catchall`, `/disposable`, `/business`, `/free`, `/role`, `/gibberish`) have no separate Tomba endpoints. The Email Verifier response carries `accept_all`, `disposable`, `webmail`, and `gibberish` for every address.

## Authentication changes

Clearout reads an API token from the `Authorization` header. Tomba needs two headers, `X-Tomba-Key` and `X-Tomba-Secret`:

```bash
# Clearout
curl -X POST "https://api.clearout.io/v2/email_verify/instant" \
  -H "Authorization: Bearer $CLEAROUT_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"email": "jane.doe@stripe.com"}'

# Tomba
curl "https://api.tomba.io/v1/email-verifier?email=jane.doe@stripe.com" \
  -H "X-Tomba-Key: $TOMBA_API_KEY" \
  -H "X-Tomba-Secret: $TOMBA_SECRET_KEY"
```

Tomba takes the address as a query parameter on a `GET` request, not in a JSON body. Tomba API keys expire; plan their rotation with [Key expiry](/authentication#key-expiry).

## Parameter mapping

| Clearout                         | Tomba                                      | Notes                                                                                        |
| -------------------------------- | ------------------------------------------ | -------------------------------------------------------------------------------------------- |
| `Authorization` header           | `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.                                                         |
| `response=flat`                  | No equivalent                              | Read the checks from `data.email`.                                                           |
| Bulk `file`                      | `file`                                     | CSV only, sent with the content type `text/csv`. Convert XLSX files first. Also send `name`. |
| Email column found by its header | `email_field_index`                        | Zero-based, and optional when the header names the column, such as `email`.                  |
| Bulk `optimize`                  | No equivalent                              |                                                                                              |
| Bulk `ignore_duplicate_file`     | No equivalent                              |                                                                                              |
| `list_id`                        | `{id}` in the path                         |                                                                                              |

## Response field mapping

Clearout returns the result in `data`. 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](/attributes/verifier).

| Clearout                                       | Tomba                                    | Notes                                                                       |
| ---------------------------------------------- | ---------------------------------------- | --------------------------------------------------------------------------- |
| `status` (top level)                           | HTTP status code                         | See [Error mapping](#error-mapping).                                        |
| `data.email_address`                           | `data.email.email`                       | An empty string when the domain is disposable.                              |
| `data.status`                                  | `data.email.status`, `data.email.result` | See [Status mapping](#status-mapping).                                      |
| `data.safe_to_send`                            | `data.email.result`                      | The closest field; see [Result values](/attributes/verifier#result-values). |
| `data.sub_status.code`, `data.sub_status.desc` | No equivalent                            | Tomba reports `greylisted`, `block`, and `mx_check` as separate fields.     |
| `data.disposable`                              | `data.email.disposable`                  | A boolean instead of `"yes"` or `"no"`.                                     |
| `data.free`                                    | `data.email.webmail`                     | A boolean instead of `"yes"` or `"no"`.                                     |
| `data.gibberish`                               | `data.email.gibberish`                   | A boolean instead of `"yes"` or `"no"`.                                     |
| `data.role`                                    | No equivalent                            |                                                                             |
| `data.detail_info.mx_record`                   | `data.email.mx.records`                  | An array of MX host names instead of one string.                            |
| `data.detail_info.smtp_provider`               | `data.email.smtp_provider`               |                                                                             |
| `data.detail_info.account`, `.domain`          | No equivalent                            | Split the address at `@`.                                                   |
| `data.suggested_email_address`                 | No equivalent                            |                                                                             |
| `data.bounce_type`                             | No equivalent                            |                                                                             |
| `data.verified_on`, `data.time_taken`          | No equivalent                            |                                                                             |
| `data.profile`                                 | No equivalent                            |                                                                             |
| `error.code`, `error.message`                  | `errors.type`, `errors.message`          | See [Error mapping](#error-mapping).                                        |

Tomba also returns `score`, `accept_all`, `block`, `greylisted`, `regex`, and `whois`, which have no field in Clearout's instant verify response. Read your remaining balance from the `X-Verify-Remaining` header or `GET /v1/me`; see [Check usage](/usage-and-quotas#check-usage).

## Status mapping

Clearout documents four primary statuses. Its API returns them in `data.status`; the example response shows `valid` in lower case, so compare case-insensitively.

| Clearout status                          | Tomba `status`                                          | Tomba `result`  |
| ---------------------------------------- | ------------------------------------------------------- | --------------- |
| Valid                                    | `valid`                                                 | `deliverable`   |
| Invalid                                  | `invalid`                                               | `undeliverable` |
| Invalid, sub-status `400` (syntax error) | No status. The request fails with `422 params_invalid`. |                 |
| Catch All                                | `accept_all`                                            | `risky`         |
| Unknown                                  | `unknown`                                               | `risky`         |
| Any status with `disposable: "yes"`      | `disposable`                                            | Empty string    |

Tomba reports a disposable domain as its own `status` rather than as a flag next to a mailbox result. Compare `status` case-insensitively. Each value is defined in [Status values](/attributes/verifier#status-values).

## Error mapping

Both APIs signal failures with HTTP status codes. Clearout's error body is `{"status": …, "error": {"code", "message"}}`; Tomba's is an `errors` object with `type`, `message`, and `code`.

| Clearout                                  | Tomba                                                                                                                        | What to do                                                                                                                   |
| ----------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
| `400` Bad Request                         | `422 params_invalid`                                                                                                         | Fix the parameter named in the message.                                                                                      |
| `401` Unauthorized                        | `400 authentication_failed` (missing or malformed), `400 api_key_expired`, `401 authentication_failed` (wrong key or secret) | Check both headers; [rotate](/authentication#rotate-keys) an expired key.                                                    |
| `402` with code `1002`, `1028`, or `1031` | `402 quota_exceeded` on single requests                                                                                      | For bulk jobs, the balance is checked at the first download instead; see [Billing](/bulks#billing).                          |
| `429` with code `1030`                    | `429 rate_limit`                                                                                                             | Retry after the `Retry-After` delay, not `x-ratelimit-reset`; see [Handle 429 responses](/rate-limits#handle-429-responses). |
| Code `1001` (too many bulk requests)      | `429 rate_limit` (running job limit)                                                                                         | Wait for a running job to finish; see [Limits](/bulks#limits).                                                               |
| Code `1004` (no email column found)       | `422 params_invalid`                                                                                                         | Name the header `email`, or send `email_field_index`.                                                                        |
| `415` Invalid Content Type                | No equivalent                                                                                                                | Tomba's single check is a `GET` request with no body.                                                                        |
| `500`, `503`                              | `500 api_error`                                                                                                              | Retry with backoff.                                                                                                          |
| `524` Request Timeout                     | No equivalent                                                                                                                | Tomba returns the final result; keep the client timeout at 180 seconds or more.                                              |
| 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](/error-handling#error-types).

## Bulk and async jobs

| Clearout                                                      | Tomba                                                                                                           |
| ------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- |
| `POST /email_verify/bulk` with a CSV or XLSX `file`           | `POST /v1/bulk/verifier` with `name` and a CSV `file`, or a newline-separated `list` of addresses               |
| Verification is queued on upload                              | Launch the job: `PUT /v1/bulk/verifier/{id}`                                                                    |
| Poll `progress_status` for `progress_status` and `percentile` | Poll `GET /v1/bulk/verifier/{id}/progress` for `status` and `progress` until `status` is `completed`            |
| The `email_verifier.bulk.completed` webhook                   | `webhook_url`, which receives a signed `bulk.completed` event. See [Bulk job events](/webhook#bulk-job-events). |
| `POST /download/result` returns a file URL in `data.url`      | `GET /v1/bulk/verifier/{id}/download?file=full` returns the CSV itself                                          |

```bash
curl -X POST "https://api.tomba.io/v1/bulk/verifier" \
  -H "X-Tomba-Key: $TOMBA_API_KEY" \
  -H "X-Tomba-Secret: $TOMBA_SECRET_KEY" \
  -F "name=Newsletter list" \
  -F "file=@emails.csv;type=text/csv" \
  -F "email_field_index=0"
```

The launch, progress, and download requests are shown in [Lifecycle](/bulks#lifecycle). Row limits and daily job limits are in [Limits](/bulks#limits), and a job is charged when you first download its results; see [Billing](/bulks#billing).

## Code example

This function replaces a call to `/email_verify/instant` and returns Clearout's field names for the values Tomba supplies. It needs Node.js 18 or later.

```js title="verify.mjs"
const TOMBA_HEADERS = {
    "X-Tomba-Key": process.env.TOMBA_API_KEY,
    "X-Tomba-Secret": process.env.TOMBA_SECRET_KEY,
};

// Tomba status -> Clearout primary status, as Clearout's docs name them.
// Its API example returns "valid" in lower case; compare case-insensitively.
const STATUSES = {
    valid: "Valid",
    invalid: "Invalid",
    accept_all: "Catch All",
    unknown: "Unknown",
};

const yesNo = (value) => (value ? "yes" : "no");

async function instantVerify(email) {
    const url = new URL("https://api.tomba.io/v1/email-verifier");
    url.searchParams.set("email", email);
    const res = await fetch(url, { headers: TOMBA_HEADERS });
    const body = await res.json();
    if (body.errors) {
        throw new Error(
            `${res.status} ${body.errors.type}: ${body.errors.message}`,
        );
    }

    const check = body.data.email;
    const status = check.status.toLowerCase();
    return {
        email_address: check.email || email,
        // A disposable address has no mailbox status in Tomba.
        status: STATUSES[status] ?? "Unknown",
        disposable: yesNo(status === "disposable" || check.disposable),
        free: yesNo(check.webmail),
        gibberish: yesNo(check.gibberish),
        detail_info: {
            mx_record: check.mx?.records?.[0] ?? null,
            smtp_provider: check.smtp_provider,
        },
    };
}

console.log(await instantVerify("jane.doe@stripe.com"));
```

## Cutover checklist

1. Store the Tomba key and secret as `TOMBA_API_KEY` and `TOMBA_SECRET_KEY`, and send them as `X-Tomba-Key` and `X-Tomba-Secret` instead of the `Authorization` header.
2. Change instant verification from a `POST` with a JSON body to `GET https://api.tomba.io/v1/email-verifier?email=`, drop `timeout`, and set a client timeout of at least 180 seconds.
3. Swap error handling: `422` for bad parameters, `402` for credits, `429` with `Retry-After`, and `408` and `451` as new cases.
4. Translate `status` with the [status mapping](#status-mapping), comparing it case-insensitively, and read `disposable`, `webmail`, and `gibberish` as booleans.
5. Replace bulk uploads with `verifier` bulk jobs: convert XLSX to CSV, launch, receive the `webhook_url` event instead of the webhook, and download the CSV.
6. Verify the same sample list with both services and compare the mapped results before you switch production traffic.
