# Migrate from RocketReach

This guide maps RocketReach's People Lookup, People Lookup Status, Bulk People Lookup, and Account endpoints to Tomba; the Universal API endpoints (`/universal/*`) aren't covered. RocketReach API behavior checked on 2026-09-30 against the [RocketReach API reference](https://docs.rocketreach.co/reference/people-lookup-api).

## Before you begin

- Get your API key (`ta_…`) and secret (`ts_…`); see [Authentication](/authentication).
- Check what each endpoint 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.rocketreach.co/api/v2` with `https://api.tomba.io/v1`. RocketReach's person lookup takes any identifier; Tomba has one endpoint per identifier:

| RocketReach                                           | Tomba                                                                                                                                         | Notes                                                                                                                           |
| ----------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
| `GET /person/lookup` with `linkedin_url`              | [`GET /v1/linkedin`](/api/finder#linkedin-finder) with `enrich_mobile=true`                                                                   | Returns the email address and the person's numbers in `data.phone_data`.                                                        |
| `GET /person/lookup` with `linkedin_url`, phones only | [`GET /v1/phone-finder`](/api/phone#phone-finder) with `linkedin`                                                                             | Set `full=true` to get every number.                                                                                            |
| `GET /person/lookup` with `name`, `current_employer`  | [`GET /v1/email-finder`](/api/finder#email-finder) with `enrich_mobile=true`                                                                  | Returns the email address and the person's numbers in `data.phone_data`.                                                        |
| `GET /person/lookup` with `email`                     | [`GET /v1/enrich`](/api/finder#email-enrichment) with `enrich_mobile=true`, or [`GET /v1/phone-finder`](/api/phone#phone-finder) with `email` | Email Enrichment returns the person; Phone Finder returns only numbers.                                                         |
| `GET /person/lookup` with `phone`                     | No equivalent                                                                                                                                 | Tomba doesn't look up a person from a number. [`GET /v1/phone-validator`](/api/phone#phone-validator) checks the number itself. |
| `GET /person/lookup` with `id` or `npi_number`        | No equivalent                                                                                                                                 | Store the LinkedIn URL, email address, or name and company instead.                                                             |
| `GET /person/checkStatus`                             | No equivalent                                                                                                                                 | Tomba returns the final result in the lookup response. Remove the polling.                                                      |
| `POST /bulkLookup`                                    | Bulk jobs                                                                                                                                     | See [Bulk and async jobs](#bulk-and-async-jobs).                                                                                |
| `GET /account/`                                       | [`GET /v1/me`](/api/account#get-account)                                                                                                      | See [Check usage](/usage-and-quotas#check-usage).                                                                               |

## Authentication changes

RocketReach takes the key in the `Api-Key` header, or in the deprecated `api_key` query parameter. Tomba needs two headers, `X-Tomba-Key` and `X-Tomba-Secret`, and doesn't accept credentials in the query string:

```bash
# RocketReach
curl "https://api.rocketreach.co/api/v2/person/lookup?linkedin_url=https://www.linkedin.com/in/jane-doe" \
  -H "Api-Key: $ROCKETREACH_API_KEY"

# Tomba
curl "https://api.tomba.io/v1/linkedin?url=https://www.linkedin.com/in/jane-doe&enrich_mobile=true" \
  -H "X-Tomba-Key: $TOMBA_API_KEY" \
  -H "X-Tomba-Secret: $TOMBA_SECRET_KEY"
```

Tomba API keys expire; plan their rotation with [Key expiry](/authentication#key-expiry).

## Parameter mapping

| RocketReach                        | Tomba                                                | Notes                                                                                                                                                                |
| ---------------------------------- | ---------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `linkedin_url`, `linkedin_ext_url` | `url` on LinkedIn Finder, `linkedin` on Phone Finder | Send a full profile URL, such as `https://www.linkedin.com/in/jane-doe`.                                                                                             |
| `name`                             | `full_name` on Email Finder                          | Or send `first_name` and `last_name`.                                                                                                                                |
| `current_employer`                 | `company` on Email Finder                            | Send `domain` instead when you have the company's domain.                                                                                                            |
| `email`                            | `email` on Email Enrichment or Phone Finder          |                                                                                                                                                                      |
| `title`                            | No equivalent                                        | The response carries `position`; compare it in your code.                                                                                                            |
| `phone`, `id`, `npi_number`        | No equivalent                                        |                                                                                                                                                                      |
| `lookup_type`                      | `enrich_mobile`, or the choice of endpoint           | Set `enrich_mobile=true` on Email Finder, LinkedIn Finder, or Email Enrichment to get numbers, at an extra cost; see [Credit costs](/usage-and-quotas#credit-costs). |
| `return_cached_emails`             | No equivalent                                        | Tomba doesn't return partial results.                                                                                                                                |
| `webhook_id`                       | `webhook_url`                                        | Pass the callback URL on each request instead of an ID registered in the dashboard; see [Webhooks](/webhook#per-request-callbacks).                                  |
| `metadata`                         | No equivalent                                        | Keep your own IDs and tags alongside the request.                                                                                                                    |

## Response field mapping

RocketReach returns the profile at the top level. Tomba returns the person under `data`, and phone numbers under `data.phone_data[]` on the finders or `data` on Phone Finder.

### Person and email fields

| RocketReach                                                                                                                | Tomba                                                   | Notes                                                                                                |
| -------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
| `name`                                                                                                                     | `data.full_name`                                        | Also `first_name` and `last_name`.                                                                   |
| `current_title`                                                                                                            | `data.position`                                         |                                                                                                      |
| `current_employer`                                                                                                         | `data.company`                                          |                                                                                                      |
| `current_employer_domain`                                                                                                  | `data.website_url`                                      |                                                                                                      |
| `linkedin_url`                                                                                                             | `data.linkedin`                                         |                                                                                                      |
| `country_code`                                                                                                             | `data.country`                                          | Tomba uses ISO 3166-1 alpha-2 codes.                                                                 |
| `recommended_email`, `recommended_professional_email`, `current_work_email`                                                | `data.email`                                            | `null` when no address is found; the status is still `200`.                                          |
| `emails[].email`                                                                                                           | `data.email`                                            | Tomba returns one address. LinkedIn Finder returns up to two with `full=true`, without `phone_data`. |
| `emails[].smtp_valid`                                                                                                      | `data.verification.status`                              | See [Status mapping](#status-mapping).                                                               |
| `emails[].last_validation_check`                                                                                           | `data.verification.date`                                |                                                                                                      |
| `emails[].type` `role-based`                                                                                               | `data.type` `generic`                                   | Tomba's `personal` means an address that belongs to one person, not a personal mailbox.              |
| `emails[].grade`                                                                                                           | `data.score`                                            | A 0 to 100 confidence, computed differently. Recalibrate any threshold you apply to it.              |
| `recommended_personal_email`, `current_personal_email`                                                                     | No equivalent                                           |                                                                                                      |
| `current_employer_website`, `current_employer_linkedin_url`, `current_employer_industry`                                   | [`GET /v1/companies/find`](/api/enrichment#company-api) | Call it with `data.website_url`.                                                                     |
| `city`, `region`, `location`, `region_latitude`, `region_longitude`                                                        | No equivalent                                           |                                                                                                      |
| `job_history`, `education`, `skills`, `birth_year`, `npi_data`, `connections`, `links`, `tags`, `profile_list`, `metadata` | No equivalent                                           |                                                                                                      |
| `id`, `status`                                                                                                             | No equivalent                                           | Tomba has no lookup IDs or lookup states.                                                            |

Tomba also returns `department`, `gender`, `twitter`, `accept_all`, and `sources`; see [Person response](/attributes/person).

### Phone fields

| RocketReach                  | Tomba                            | Notes                                                                                                                      |
| ---------------------------- | -------------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
| `phones[].number`            | `intl_format`                    | Tomba also returns `local_format` and `rfc3966_format`.                                                                    |
| `phones[].e164`              | `e164_format`                    |                                                                                                                            |
| `phones[].country_code`      | No equivalent                    | RocketReach returns the calling code, such as `1`. Tomba's `country_code` is the ISO 3166-1 alpha-2 country, such as `US`. |
| `phones[].type`              | `line_type`                      | See [Status mapping](#status-mapping).                                                                                     |
| `phones[].grade`, `validity` | `valid`                          | `valid` only says whether the number fits its country's numbering plan; there's no identity-match grade.                   |
| `phones[].recommended`       | Phone Finder without `full`      | The single number Phone Finder returns prefers a valid one.                                                                |
| `phones[].extension`         | No equivalent                    |                                                                                                                            |
| No equivalent                | `carrier`, `region`, `timezones` | See [Phone response](/attributes/phone#fields).                                                                            |

On the finders, `data.phone_number` is `true` when Tomba has a number for the person; `data.phone_data` is filled only with `enrich_mobile=true`. When Phone Finder finds nothing, the status is `200` and `data` is an empty array.

## Status mapping

RocketReach's lookup `status` (`complete`, `failed`, `waiting`, `searching`, `progress`) has no equivalent: every Tomba response is final. Email and phone statuses map as follows:

| RocketReach `emails[].smtp_valid` | Tomba `data.verification.status` |
| --------------------------------- | -------------------------------- |
| `valid`                           | `valid`                          |
| `invalid`                         | `invalid`                        |
| `accept-all`                      | `accept_all`                     |
| `unknown`                         | `unknown`                        |

Tomba can also return `disposable`, or `null` when the address hasn't been verified. Compare `status` case-insensitively. Each value is defined in [Status values](/attributes/verifier#status-values).

| RocketReach `phones[].type` | Tomba `line_type`                                                                                   |
| --------------------------- | --------------------------------------------------------------------------------------------------- |
| `mobile`                    | `MOBILE`                                                                                            |
| `direct dial`, `other`      | No direct equivalent. Tomba reports the line itself: `FIXED_LINE`, `VOIP`, `TOLL_FREE`, and others. |

Every value is listed in [Line types](/attributes/phone#line-types).

## Error mapping

RocketReach returns an error body with `detail` or `message`. Tomba returns an `errors` object with `type`, `message`, and `code`:

| RocketReach                           | Tomba                                                                                                                        | What to do                                                                            |
| ------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------- |
| `400` malformed or missing parameters | `422 params_invalid`                                                                                                         | Fix the parameter named in the message.                                               |
| `401` missing or invalid API key      | `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.             |
| `403` out of lookup credits           | `402 quota_exceeded`                                                                                                         | Wait for your usage window to renew or add credits.                                   |
| `403` key lacks permission            | `403`                                                                                                                        | Your plan doesn't include the feature; see [Errors](/error-handling#error-types).     |
| `404` profile doesn't exist           | `200` with `data.email` `null`, or an empty `data` array on Phone Finder                                                     | Check the body, not the status.                                                       |
| `429` with `Retry-After`              | `429 rate_limit` with `Retry-After`                                                                                          | Retry after the delay; see [Handle 429 responses](/rate-limits#handle-429-responses). |
| `500`                                 | `500 api_error`                                                                                                              | Retry with backoff.                                                                   |

Also handle `451 claimed_email` or `451 claimed_linkedin` when the person asked Tomba to stop processing their data. Error responses don't consume credits; every type is listed in [Errors](/error-handling#error-types). RocketReach's `RR-Request-ID` header corresponds to Tomba's request ID; see [Request IDs](/going-to-production#request-ids).

## Bulk and async jobs

RocketReach's `POST /bulkLookup` takes 1 to 100 queries and delivers results to a webhook. Tomba runs lists as bulk jobs, which can send an event to a `webhook_url` when they end:

| RocketReach query             | Tomba bulk type                                                                                                                  |
| ----------------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| `linkedin_url`, for phones    | `phone-finder`: a JSON `list` of LinkedIn URLs, or a CSV file with a `linkedin` column. See [Phone finder](/bulks#phone-finder). |
| `email`, for phones           | `phone-finder`: a JSON `list` of email addresses, or a CSV file with an `email` column. See [Phone finder](/bulks#phone-finder). |
| `linkedin_url`, for emails    | `linkedin`: a JSON `list` of profile URLs. See [LinkedIn finder](/bulks#linkedin-finder).                                        |
| `name` and `current_employer` | `finder`: a CSV file with name and company columns. See [Email finder](/bulks#email-finder).                                     |

Each job is created, launched, polled, and downloaded as CSV; see [Lifecycle](/bulks#lifecycle). A job is charged when you first download its results; see [Billing](/bulks#billing). Limits are in [Limits](/bulks#limits).

## Code example

This function replaces a call to RocketReach's `GET /person/lookup` and returns RocketReach's field names, or `null` when no person is found. It needs Node.js 18 or later.

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

const SMTP_VALID = {
    valid: "valid",
    invalid: "invalid",
    accept_all: "accept-all",
    unknown: "unknown",
};

// Accepts RocketReach's linkedin_url, email, or name + current_employer.
async function personLookup(params) {
    let path;
    const query = { enrich_mobile: "true" };
    if (params.linkedin_url) {
        path = "linkedin";
        query.url = params.linkedin_url;
    } else if (params.email) {
        path = "enrich";
        query.email = params.email;
    } else if (params.name && params.current_employer) {
        path = "email-finder";
        query.full_name = params.name;
        query.company = params.current_employer;
    } else {
        throw new Error(
            "Send linkedin_url, email, or name and current_employer",
        );
    }

    const url = new URL(`https://api.tomba.io/v1/${path}`);
    for (const [key, value] of Object.entries(query)) {
        url.searchParams.set(key, value);
    }

    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 person = body.data;
    const phones = person.phone_data ?? [];
    if (!person.email && phones.length === 0) return null;

    const status = person.verification?.status?.toLowerCase();
    return {
        status: "complete",
        name: person.full_name,
        current_title: person.position,
        current_employer: person.company,
        current_employer_domain: person.website_url,
        linkedin_url: person.linkedin,
        country_code: person.country,
        recommended_professional_email: person.email,
        emails: person.email
            ? [
                  {
                      email: person.email,
                      smtp_valid: SMTP_VALID[status] ?? null,
                      type:
                          person.type === "generic"
                              ? "role-based"
                              : "professional",
                      last_validation_check: person.verification?.date ?? null,
                  },
              ]
            : [],
        phones: phones.map((phone) => ({
            number: phone.intl_format,
            e164: phone.e164_format,
            type: phone.line_type === "MOBILE" ? "mobile" : "other",
        })),
    };
}

console.log(
    await personLookup({
        linkedin_url: "https://www.linkedin.com/in/jane-doe",
    }),
);
```

## 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 `Api-Key`.
2. Route each lookup by identifier: LinkedIn URLs to `/v1/linkedin`, names and companies to `/v1/email-finder`, email addresses to `/v1/enrich`, and phone-only lookups to `/v1/phone-finder`. Add `enrich_mobile=true` where you need numbers.
3. Remove the `/person/checkStatus` polling and the lookup webhook handler, or pass `webhook_url` per request.
4. Update response parsing: read fields under `data`, numbers from `data.phone_data`, and treat `data.email` `null` or an empty `data` array as not found instead of `404`.
5. Swap error handling: `402` replaces RocketReach's out-of-credits `403`, `422` replaces `400`, and `errors` is an object.
6. Move `/bulkLookup` batches to `phone-finder`, `linkedin`, and `finder` bulk jobs.
7. Run the same sample of people through both services and compare the results before you switch production traffic.
