Documentation
Sign inGet started

Look up a phone number

One call answers what a number is. Everything here needs an API key with the lookup scope.

Run a base lookup

Send the number and nothing else. The base lookup is always billed once, and it always answers the country, the serving network, the issuing network, and a coarse line type.
const answer = await bird.lookup.phoneNumber({
  phone_number: "+31612345678",
  type: ["classification", "score"],
});
console.log(answer.country_code, answer.line_type);
// Only a block whose status is ok carries a value, and only that one is billed.
if (answer.score?.status === "ok") console.log(answer.score.value);

How to write the number

Send the country calling code, then the national number. The leading + is optional and 00 works in its place, so +31612345678, 31612345678 and 0031612345678 are all the same number.
A number written for dialling inside one country, with no country code, is rejected rather than guessed at. 0612345678 returns E22000, because prefixing a country code would name a real number somewhere else and bill you for looking it up.

What the base lookup answers

country_code is the number's country, absent when the number belongs to no single country, as a non-geographic range does.
network_info is the network serving the number today, and original_network_info is the network that issued its range. The two differ when the number has been ported, and in that case flags contains ported.
line_type is what kind of line the number is: mobile, fixed_line, voip, toll_free, premium_rate, satellite, pager, payphone, m2m, service, other, or unknown. unknown means the carrier platform holds no classification for the range; other means it holds one with no equivalent here. For the allocated service at finer precision, request the classification property. It answers from a different source with a wider vocabulary, and is reported separately so you can always tell the two apart.

Add properties

Name the properties you want in type. Each is billed separately and only when delivered.
PropertyWhat it answers
classificationThe precise allocated service of the range: premium rate, satellite, M2M, payphone.
portingWhen the number last moved network, and every move on record.
presenceWhether the number is currently live on the network.
roamingWhether it is roaming, and on which network.
sim_swapWhen its SIM last changed.
scoreA credibility score from 0 to 100.
classification, porting and score read stored data and return quickly. presence, roaming and sim_swap reach the live network, so they are slower and their coverage varies by operator. Expect unavailable or inconclusive for these more often than for the stored ones.
Two properties answer questions the base lookup already touches, at higher resolution. porting gives you the date and the full history where the base lookup's ported flag only says whether a move ever happened. classification resolves line_type to the exact allocated service.

Read the status before the value

Every property block carries a status, and only ok carries a value.
Codebeispiel
{
  "phone_number": "+441904123456",
  "country_code": "GB",
  "line_type": "service",
  "classification": {
    "status": "ok",
    "value": "premium_rate"
  },
  "score": {
    "status": "unavailable"
  }
}
In that answer you were billed for the base lookup and for classification. You were not billed for score.
Read status first and treat anything other than ok as "not answered". It is an open vocabulary, so a value you do not recognize is a future status rather than an error.
Two blocks report a bound rather than an exact figure when the network will not release one. sim_swap returns min_days and max_days instead of last_swapped_at when only a recency band is known. porting sets last_ported_at_is_approximate when a registry records the period of a move but not its day.
One field reads as a negative but is a positive finding: porting.ported set to false means the registry was consulted and holds no move for this number, not that nothing could be checked. That distinction is what status is for.

Retry without paying twice

A lookup is billed, so a retried request must not buy a second answer. Send an Idempotency-Key and a repeat of the same request replays the stored answer instead of running a new lookup. See Idempotency.
The GET form of this operation, which puts the number in the URL, cannot carry an idempotency key. Use the POST form for anything automated.

Errors

CodeWhat happened
E22000The number is not a valid phone number in international format. Nothing was charged.
E22001The organization's wallet cannot cover the lookup. Top it up and retry. Nothing was charged.
E22002Lookup is temporarily unavailable. Retry with backoff. Nothing was charged.
A failing property is not an error. It comes back as a status on its block, with the base lookup served alongside it.

Next steps