Security
Spotting fake phone numbers in signup forms
A phone field on a signup form collects a lot of numbers that nobody can be reached on: fiction ranges copied from films, strings with the wrong length, area codes that do not exist, and numbers that parse perfectly but belong to nobody in particular. A numbering-plan check removes the first three groups cheaply. This post shows what each pattern looks like, what the LookValid phone check returns for it, and where the check stops.
Four kinds of fake number
Most bad entries in a phone field fall into a small set of patterns. They differ in how easy they are to catch, and the difference decides which of them a rules-based check can remove.
- Reserved fiction ranges. Regulators set aside numbers for films, television and advertising so that nobody gets called by mistake. In the North American plan, 555-0100 through 555-0199 are described by NANPA as fictitious, non-working numbers reserved for entertainment and advertising 2. They are the numbers people type when they want to get past a form.
- Impossible lengths. International numbers are variable in length, but the ITU-T E.164 recommendation fixes the maximum at 15 digits 1. A string that is too short for its country, or longer than any plan allows, cannot be a number.
- Invalid area codes. North American area codes follow the NXX pattern, where the first digit is 2 through 9 3. A number that starts with 0 or 1 where the area code belongs cannot be assigned to anyone.
- Numbers that parse but are not assigned. A string can have a plausible length and still fall outside every range the plan allows. A string can also fall inside an allowed range and never have been given to a subscriber. These are the hardest to separate from real numbers.
What a numbering-plan check proves
The LookValid phone check reads the number against the numbering plan for its country. It returns the number in E.164, national and international formats, the country, the number type the plan assigns, and for North American numbers the area code and its region. A number the plan rejects is returned as not found, with a reason, and is not billed.
Two separate tests sit behind that answer. The library that supplies the plan data describes isPossible() as a length check only, and isValid() as a length check followed by a check of the digits against the country's patterns 4. The first test catches impossible lengths. The second catches area codes and ranges that the plan does not allow. The reason text in the result tells you which one failed: either the length does not match the plan, or the length is plausible but the number cannot be assigned to a subscriber.
| Sample | Pattern | Result |
|---|---|---|
| +12025550143 | Fiction range, real area code | Found. The plan allows it, so it passes |
| +12125550123 | Fiction range, real area code | Found. The plan allows it, so it passes |
| +10125550143 | Area code starts with 0 | Not found: plausible length, not assignable |
| +12005550143 | Area code 200 is not valid | Not found: plausible length, not assignable |
| +1202555014 | One digit short | Not found: length does not match the plan |
| +12025550143999 | Too many digits | Not found: length does not match the plan |
Sample numbers and what the numbering-plan check reports (US default)
The first two rows are the part many teams miss. The fiction block is reserved, yet the plan does not mark 555-0100 through 555-0199 as invalid, so a pure length-and-pattern check passes it. If you want to keep those numbers out of your database, add a rule of your own on the last seven digits after the check. That rule is a few lines of code and depends only on the NANPA reservation 2.
Run one check and read the result
A rejected number costs nothing, and a found number costs 1 credit at the catalog price. This request sends an area code that starts with 0.
curl -X POST https://YOUR_API_HOST/v1/phone \
-H "Authorization: Bearer $LOOKVALID_API_KEY" \
-H "Content-Type: application/json" \
-d '{"phone":"+10125550143"}'
A number with an impossible area code
The response has found set to false and a reason you can log or show to the visitor. For a number the plan accepts, read number_type and area_code_region as well. You can try the same check without a key in the phone number validator.
What it cannot tell you
The check never contacts a carrier. It cannot say whether a number is switched on, who holds it, or whether a person can answer it. The result says so itself: carrier is always null, and the line type comes from the numbering plan, not from a live record.
That matters most for VoIP and throwaway numbers. The library lists VOIP as one of the types it can return 4, and the phone check passes that type through when the plan data marks a range that way. A VoIP number taken from an ordinary range is reported as the range's type, so a person can hold a working, disposable number that looks the same as any other valid number. The check also cannot tell a number a visitor owns from a valid number they copied from somewhere else.
- Use the check to refuse numbers that cannot exist and to store clean E.164 values.
- Treat a pass as "well formed", never as "reachable" or "owned by this person".
- Where you need proof that the visitor controls the number, send a one-time code to it and compare. That is a different test with a different cost.
A practical order of checks
- Strip spaces and punctuation, then run the numbering-plan check with a country hint if the form knows the visitor's country.
- Refuse not-found results at the form and show the reason so a typo can be fixed.
- Reject the reserved fiction block with a rule of your own.
- Store the E.164 value, and keep the original text only if you need it for support.
- Confirm control of the number with a code only where the stakes justify it.
For the same steps in code, see the Python tutorial. The pricing page lists what each plan includes, and the docs cover every field in the phone result.
Sources
- ITU-T Recommendation E.164 (1988 edition): The international public telecommunication numbering plan, read 2026-10-19
- NANPA: 555 line numbers, read 2026-10-19
- NANPA annual report 2000: area code (NPA) format, read 2026-10-19
- libphonenumber-js README: isPossible, isValid and getType, read 2026-10-19