Lookup

A lookup tells you which carrier a US or Canada number is on and whether it is a mobile, a landline or a VoIP line. Run one before you text a list to skip landlines, or at signup to reject VoIP numbers.

Look up a number

GET /v1/lookup/{phone}

bash
curl https://api.jointextkit.com/v1/lookup/+14155550132 \
  -H "Authorization: Bearer ssms_sk_test_YOUR_KEY"
json
{
  "phone_number": "+14155550132",
  "valid": true,
  "line_type": "mobile",
  "carrier": { "name": "Sandbox Wireless", "type": "mobile" },
  "caller_name": null
}

The number can be written any way you like: +14155550132, 4155550132 and (415) 555-0132 are the same lookup. It must have 10 digits, or 11 starting with 1. The response always carries the E.164 form. A restricted key needs the lookup scope.

Response fields

FieldNotes
phone_numberThe number you asked about, normalized to E.164.
validfalse only when the carrier network reports that the number does not exist (unassigned or vacant). Everything else is true, including a number whose line type is unknown.
line_typemobile, landline, voip, or null. null means we could not classify the carrier; it is never a guess.
carrier.nameThe carrier the number is on today. It follows ports, so a number moved to another carrier shows the new one. null when the network returned no name.
carrier.typeThe same value as line_type.
caller_nameAlways null today. The field is reserved, so check for null before using it.

A successful response also carries an X-Quota-Remaining header: the lookups left in your account's day.

With the SDKs

js
import { TextKit } from 'jointextkit';
const sms = new TextKit(process.env.TEXTKIT_API_KEY);

const result = await sms.lookup.phone('+14155550132');
if (result.line_type === 'landline') {
  // skip: this number cannot receive texts
}
python
from jointextkit import TextKit

client = TextKit()  # reads TEXTKIT_API_KEY

result = client.lookup.phone("+14155550132")
print(result["line_type"], result["carrier"]["name"])

Test keys

A test key never contacts a carrier and is never billed. The answer is decided by the number alone, so your tests get the same result every time:

Numberline_typecarrier.name
Last digit evenmobileSandbox Wireless
Last digit oddlandlineSandbox Telecom
+15005550010landlineSandbox Telecom

+15005550010 is the sandbox landline number, so it answers landline although it ends in 0. valid is always true and caller_name is always null with a test key.

Live keys

A live key returns real carrier data. Each answer is cached for 24 hours, so a second lookup of the same number inside a day returns the stored answer, and a port can take up to 24 hours to show. A cached answer still counts as a lookup.

On an account with a payment method, each successful live lookup costs $0.008. A lookup that ends in an error is not billed.

Sending a message never runs a lookup. The destination_carrier field on a message is filled from the same 24-hour cache, so it is null unless you looked the number up recently.

Limits

Lookups are counted per account, per UTC day. Test and live keys draw from the same counter.

AccountLookups per day
With a payment method5,000
New account, no payment method100
No payment method, after live access is approved250

A lookup that fails with carrier_error still uses one of the day's lookups; a request rejected with invalid_request does not. The usual limit of 60 requests a minute per key applies too.

Errors

StatusCodeWhen
400invalid_requestThe number is not a valid US or Canada number. param is phone.
401invalid_api_keyThe key is missing, malformed or revoked.
403insufficient_scopeA restricted key without the lookup scope. required_scope names it.
403forbiddenA live key on an account with no lookup allowance. Add a payment method in the console.
429quota_exceededThe day's lookups are used up. The message states the limit.
429rate_limitedToo many requests in a minute. Wait for the Retry-After seconds.
502carrier_errorThe carrier network did not answer. Try again shortly.
json
{
  "error": {
    "code": "quota_exceeded",
    "message": "Daily lookup quota reached (5000/day).",
    "request_id": "req_a1B2c3D4e5F6g7H8i9J0"
  }
}

Every code is explained in Errors.

Retired: spam scores

GET /v1/lookup/{phone}/spam was retired on 2026-09-30 and answers 410 endpoint_retired. The Node SDK still has sms.lookup.spam(), which now always throws that error.