Migrate from Kaspr
This guide maps Kaspr's Get LinkedIn Profile endpoint and its key endpoints to Tomba: phone numbers to Phone Finder, work emails to LinkedIn Finder. Kaspr API behavior checked on 2026-09-30 against the Kaspr 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.
- 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.developers.kaspr.io with https://api.tomba.io/v1. Kaspr returns phones and emails from one POST request; Tomba splits them across two GET endpoints that both take the LinkedIn profile URL.
| Kaspr | Tomba | Notes |
|---|---|---|
POST /profile/linkedin with dataToGet phone | GET /v1/phone-finder | Send the profile URL as linkedin. Set full=true to get every number, as in Kaspr's phones. |
POST /profile/linkedin with dataToGet workEmail | GET /v1/linkedin | Send the profile URL as url. |
POST /profile/linkedin for phones and a work email | GET /v1/linkedin | Add enrich_mobile=true to get the person's numbers in data.phone_data in the same response. |
| Numbers you already stored from Kaspr | GET /v1/phone-validator | Checks validity and returns line type, carrier, and region for a number you have. |
GET /keys/verifyKey | GET /v1/me | Wrong credentials return an authentication error. |
GET /keys/remainingCredits | GET /v1/me | See Check usage. |
GET /keys/rateLimits | GET /v1/rate-limits | See Check your limits. |
Authentication changes
Kaspr takes the key in an authorization: Bearer header and needs accept-version: v2.0 on the profile endpoint. Tomba needs two headers, X-Tomba-Key and X-Tomba-Secret, and has no version header:
Code
Tomba API keys expire; plan their rotation with Key expiry.
Parameter mapping
Kaspr's body fields become query parameters:
| Kaspr | Tomba | Notes |
|---|---|---|
id | linkedin on Phone Finder, url on LinkedIn Finder | Send a full profile URL, such as https://www.linkedin.com/in/jane-doe. When you stored only Kaspr's profile ID, build the URL from it. |
name | No equivalent | Tomba doesn't need the person's name for a LinkedIn lookup. |
dataToGet | Choice of endpoint | Call Phone Finder for phone and LinkedIn Finder for workEmail, or LinkedIn Finder with enrich_mobile=true for both. |
requiredData | No equivalent | Both endpoints return what they find. Check the response in your code and discard results that lack the data you need. |
| No equivalent | full on Phone Finder | true returns every number found as an array. Without it, data is one number, preferring a valid one. |
| No equivalent | email, domain on Phone Finder | Send them with linkedin to also look up numbers by email address or company domain; the results are merged without duplicates. domain returns company numbers. |
| No equivalent | webhook_url | Also sends the result to your URL; see Webhooks. Phone Finder sends no callback with full=true. |
Response field mapping
Kaspr returns the person under profile. Tomba returns data: a phone number object on Phone Finder, and a person object on LinkedIn Finder.
| Kaspr | Tomba | Notes |
|---|---|---|
profile.phones[] | Phone Finder with full=true: data[].intl_format, data[].e164_format | Kaspr returns strings such as +1 415 555 0100. Tomba returns each number in several formats; see Phone response. |
profile.starryPhone | Phone Finder without full: data.intl_format | Tomba's single number prefers a valid one. |
profile.phones[] alongside a work email | LinkedIn Finder with enrich_mobile=true: data.phone_data[] | data.phone_number is true when Tomba has a number for the person. phone_data isn't returned with full=true. |
profile.professionalEmails[] | LinkedIn Finder with full=true: data[].email | Up to two stored addresses. |
profile.starryProfessionalEmail | LinkedIn Finder: data.email | The single most likely address. null when none is found. |
profile.personalEmails[], starryPersonalEmail | No equivalent | Tomba's type field is personal for an address that belongs to one person and generic for a role address; it doesn't mark personal mailboxes. |
profile.name, firstName, lastName | LinkedIn Finder: data.full_name, first_name, last_name | |
profile.id | LinkedIn Finder: data.linkedin | Tomba returns the full profile URL. |
profile.title | LinkedIn Finder: data.position | |
profile.location | LinkedIn Finder: data.country | A two-letter country code only. |
profile.company.name, company.domains[] | LinkedIn Finder: data.company, data.website_url | For the other company fields, call GET /v1/companies/find with the domain. |
fetchedAt, processingTime | No equivalent | |
| No equivalent | Phone Finder: valid, line_type, carrier, country_code, region, timezones | See Phone response. |
| No equivalent | LinkedIn Finder: score, verification, sources | See Person response. |
When Phone Finder finds nothing, the status is 200, data is an empty array, and the response repeats the linkedin you sent, reduced to its permalink such as jane-doe.
Status mapping
Kaspr returns phones and emails without a status. Tomba adds one to each result:
| Result | Tomba field | Values |
|---|---|---|
| Phone number | valid | true when the number is valid in its country's numbering plan. |
| Phone number | line_type | MOBILE, FIXED_LINE, VOIP, and others; see Line types. |
| Work email | data.verification.status | valid, accept_all, invalid, unknown, or disposable, or null when the address hasn't been verified; see Status values. |
To keep only dialable numbers, filter Phone Finder results on valid: true, and on line_type MOBILE if you call mobiles only.
Error mapping
Kaspr returns {message, reason}. Tomba returns an errors object with type, message, and code:
| Kaspr | Tomba | What to do |
|---|---|---|
402 No credits left | 402 quota_exceeded | Wait for your usage window to renew or add credits. |
429 Too many requests, key blocked | 429 rate_limit | Retry after the Retry-After delay; see Handle 429 responses. |
Also handle Tomba's 400 and 401 authentication_failed for missing or wrong credentials, 400 api_key_expired for an expired key, 422 params_invalid for a malformed LinkedIn URL or a Phone Finder request without email, domain, or linkedin, and 451 claimed_linkedin or 451 claimed_email when the person asked Tomba to stop processing their data. Error responses don't consume credits; every type is listed in Errors.
Kaspr's rate-limit headers (X-Daily-RateLimit-*, X-Hourly-RateLimit-*, X-Minutely-*) have Tomba counterparts listed in Rate limit headers.
Bulk and async jobs
Kaspr's API takes one profile per request. Tomba also runs lists of profiles as bulk jobs:
| Task | Tomba bulk type |
|---|---|
| Phone numbers for many profiles | phone-finder: a JSON list of LinkedIn URLs, or a CSV file with a linkedin column. See Phone finder. |
| Work emails for many profiles | linkedin: a JSON list of profile URLs. See LinkedIn finder. |
| Checking numbers you already have | phone-validator: a JSON list of numbers. See Phone validator. |
Each job is created, launched, polled, and downloaded as CSV; see Lifecycle. To be notified when a job ends, set webhook_url; see Bulk job events. A job is charged when you first download its results; see Billing.
Code example
This function replaces a call to Kaspr's Get LinkedIn Profile endpoint and returns Kaspr's profile field names, or null when Tomba finds neither a number nor an 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 theauthorization: Bearerandaccept-versionheaders. - Replace each
POST /profile/linkedinwithGETrequests: Phone Finder for phones, LinkedIn Finder for work emails, and send full profile URLs instead of profile IDs. - Update response parsing: read numbers from
dataordata[]instead ofprofile.phones, handle an emptydataarray as no result, and drop code that reads personal emails. - Replace
requiredDatawith checks in your code, and filter numbers onvalidandline_type. - Swap error handling to read the
errorsobject, and move to Tomba's rate-limit headers andRetry-After. - Move list processing to
phone-finderandlinkedinbulk jobs. - Run the same sample of LinkedIn profiles through both services and compare the results before you switch production traffic.