Phone validation · Integrations · JavaScript

Integration guide

Validate a phone number in JavaScript and Node.js.

To validate a phone number in JavaScript, parse it with libphonenumber-js to catch typos and get E.164, then send it to the Trueguard phone validation API from Node.js for line type, carrier, VoIP type and disposable history. 100 checks a month are free, no card. Working code for both steps below.
Get your free API keyAPI referenceUpdated 11 November 2026

In this guide

Free100 / month
Standard$49.99 / 20,000
Overage$0.0025
Median responseUnder 500 ms

Step 1: check the format with libphonenumber-js

Free, runs in the browser and in Node, no network call. Install: npm install libphonenumber-js.

// npm install libphonenumber-js import parseMin from "libphonenumber-js"; // default: min metadata import parseMax from "libphonenumber-js/max"; const input = "+1 800 199 1234"; parseMin(input)?.isValid(); // true (min checks length only) parseMax(input)?.isValid(); // false (max checks the digits) const phone = parseMax("+1 415 992 9960"); phone?.isPossible(); // true phone?.isValid(); // true phone?.getType(); // "FIXED_LINE_OR_MOBILE" phone?.number; // "+14159929960", send this to the API // parse failures return undefined, so guard before you call the API if (!parseMax(userInput)?.isPossible()) { // show "Add the country code, starting with +" }

The default import only checks length

The default min metadata (about 80 kB) has no digit patterns, so isValid() behaves like isPossible() and getType() returns undefined for most countries. Import libphonenumber-js/max (about 145 kB) for full validation.
  • In the browser, use isPossible(). The maintainer prefers it because a strict isValid() "could get stale over time and start rejecting freshly-assigned phone number ranges."
  • Store .number (E.164) and send that to the API.

Where libphonenumber stops

The library knows the numbering plan. It does not know who serves the number now or where it has been listed.

"Do not rely on libphonenumber to determine whether numbers are currently assigned to a specific user and reachable."
Questionlibphonenumber-jsTrueguard API
Is the length possible?isPossible(), any buildvalidity.isPossible
Does it match the numbering plan?isValid(), digits checked only with /maxvalidity.isValid plus validity.reason (tooShort, unallocatedRange, fictionalRange...)
E.164, international, national.number, .format("INTERNATIONAL"), .format("NATIONAL")format.e164, format.international, format.national
Country.country, .countryCallingCodelocation.countryCode, countryName, callingCode
Region, city, time zonesNo fieldlocation.region, location.city, location.timezones
Line typegetType() with /max or /mobile, from the numbering planline.type, plus line.voipType fixed or nonFixed and line.smsCapable
CarrierNo fieldline.originalCarrier, line.carrierType; line.currentCarrier and line.isPorted for US numbers
Listed on a receive-SMS siteNo fieldrisk.isDisposable (last 30 days), risk.previouslyDisposable (31 to 180 days)
SMS pumping exposureNo fieldrisk.smsDestinationRisk (country), risk.rangeRisk (number block)
Runs whereBrowser or Node, offlineYour server, one HTTPS call, median under 500 ms

Step 2: call the phone validation API from Node.js

No SDK. One axios POST from your server.

Node.js, in your signup route

// npm install axios import axios from "axios"; const validatePhone = async (phone) => { const { data } = await axios.post( "https://api.trueguard.io/v2/phone/validation", { phone }, { headers: { "X-API-KEY": process.env.TRUEGUARD_KEY }, timeout: 3000 } ); return data; }; const result = await validatePhone("+14159929960"); result.risk.status; // "safe" result.line.voipType; // "nonFixed" decideSignup(result); // "stepUp"

Run this against your own number on the free tier.

Get your free API key

One header, two body fields

Set X-API-KEY to your key. In the JSON body, phone is required (max 64 chars). country is an ISO-2 code, only read for national format and ignored when the number starts with +.

Fields you will branch on

risk.status

safe, risky, invalid or unknown.

validity.reason

Why a number is invalid or unknown.

line.type

mobile, landline, voip, premiumRate and more.

line.voipType

fixed, nonFixed or unknown, for VoIP lines.

risk.isDisposable

Listed on a receive-SMS site in the last 30 days.

risk.previouslyDisposable

Listed 31 to 180 days ago.

Run +14159929960 before you write any code.

The same endpoint the snippet calls. 5 free checks a day, no key needed.

Or try

Where should the call live?

Browser (client-side)

Front-end JavaScript ships your key to every visitor. Run libphonenumber-js in the browser, send the API call from your server.

Server-side (recommended)

Call validatePhone in the signup or OTP route, before you write the account row or send the code. Works the same in Next.js route handlers, Express, NestJS, Cloudflare Workers and Lambda.

What the endpoint returns

{ "phone": "+14159929960", "rawPhone": "+14159929960", "validity": { "isValid": true, "isPossible": true, "reason": null }, "format": { "e164": "+14159929960", "international": "+1 415 992 9960", "national": "(415) 992-9960" }, "location": { "countryCode": "US", "countryName": "United States", "callingCode": "1", "region": "CA", "city": "San Francisco", "timezones": [ "America/Los_Angeles" ] }, "line": { "type": "voip", "voipType": "nonFixed", "smsCapable": true, "originalCarrier": "Lumen", "currentCarrier": "Twilio", "isPorted": true, "carrierType": "cpaasVirtual", "mcc": null, "mnc": null }, "risk": { "status": "safe", "isDisposable": false, "previouslyDisposable": false, "smsDestinationRisk": "low", "rangeRisk": "low", "carrierStirShaken": null, "blockAgeDays": 7756, "complaints90d": null } }

What to do with each risk status

The example number is valid and safe, and the rule below still steps it up: it is a non-fixed VoIP line, ported to Twilio.

risk.status

When and what to do

safe

Valid, not disposable, not premium rate. Allow. Step up non-fixed or unknown VoIP and any previouslyDisposable number. VoIP never changes status.

risky

risk.isDisposable true or line.type premiumRate. Block. Do not send an SMS code to a premium-rate line.

invalid

validity.reason notANumber, invalidCountryCode, tooShort, tooLong, invalidLength, fictionalRange. Reject and ask the user to re-enter with the country code.

unknown

validity.reason unknownRange, unallocatedRange, ambiguousCountry. Step up, for example a code by another channel. For ambiguousCountry, ask for the number starting with +.

const decideSignup = ({ line, risk }) => { if (risk.status === "invalid") { return "reject"; } const nonFixed = line.type === "voip" && line.voipType === "nonFixed"; if (risk.status === "risky" || (nonFixed && risk.previouslyDisposable)) { return "block"; } if (nonFixed || line.voipType === "unknown" || risk.status === "unknown" || risk.previouslyDisposable) { return "stepUp"; } return "allow"; };

The full VoIP rule and rollout: Block VoIP numbers at signup

How do I handle errors in Node.js?

const checkSignupPhone = async (phone) => { try { return decideSignup(await validatePhone(phone)); } catch (err) { if (err.response?.status === 400) { return "reject"; // bad input: ask the user to re-enter } // 401, 403, 429, timeouts and network errors: log and fail open console.error("Trueguard phone check failed", err.message); return "allow"; } };

Status

What to do

200

Not an error when validity.reason is set. phone is null and status is invalid or unknown. Counts as one check.

400

phone missing or over 64 chars, unsupported country, malformed JSON. Fix the request. Not counted.

401

"Invalid API key". Check TRUEGUARD_KEY.

403

The plan on this key does not include phone validation. Add the phone plan in the dashboard.

429

Quota for the billing period used up. Fail open and alert.

Timeout

axios rejects after timeout ms with no err.response. Fail open.

What it costs once you ship

Free

$0 forever

100 checks / month

Overage: n/a

Get started free

Standard

Popular

$49.99 / month

20,000 checks / month

Overage: $0.0025 per check

Get your API key

Custom

Contact us

500,000+ checks / month

Overage: Negotiated

Talk to us

Standard is $49.99 a month for 20,000 checks: $0.0025 a signup at full use. At 30,000 signups a month that is $49.99 + 10,000 x $0.0025 = $74.99. Every 200 response counts as a check, including an invalid number. 400, 401, 403 and 429 do not. Running isPossible() before the call means a mistyped number never costs a check. Compare free tiers.

Frequently asked questions

The full field reference lives in the API documentation.

How do I validate a phone number in JavaScript?

Use two steps. Parse the input with libphonenumber-js to catch typos and get the E.164 form, then send that number from your Node.js server to the Trueguard phone validation API. It returns line type, VoIP type, carrier and disposable history in one JSON response. 100 checks a month are free with no card, then $49.99 for 20,000.

Is libphonenumber-js enough on its own?

It is enough for format and the numbering plan. It has no field for the current carrier, fixed or non-fixed VoIP, or whether a number was listed on a public receive-SMS site. Google's libphonenumber FAQ says not to rely on it to tell whether a number is "currently assigned to a specific user and reachable."

Why does isValid() return true for a fake number?

The default import uses min metadata, which only checks length, so isValid() acts like isPossible(). Import from libphonenumber-js/max to check the digits against the numbering plan.

Should I use isValid() or isPossible() in the browser?

Use isPossible(). It catches typos without rejecting newly assigned ranges if your bundled metadata is old. The API does the full validity check on the server.

Can I call the phone validation API from React or the browser?

No. The key would ship to every visitor. Add a route on your server that calls the API and returns only the decision your form needs.

Does the API tell me if the number is in service?

No. There is no live line or HLR test. A valid number matches the numbering plan of its country. To prove the user holds the number, send a verification code after the check.

Does an invalid number cost a check?

Yes, if the API returns it. An unreadable number comes back as 200 with validity.reason set, and every 200 counts. Errors such as 400 and 429 do not count. Filter with isPossible() first.

Which Node.js version do I need?

Any current Node.js LTS release. The example uses axios and one POST request. There is no Trueguard SDK and you do not need one.

Validate phone numbers from Node.js today.

100 checks a month, free, no card. The code above runs against the production endpoint.

Get your free API key

No credit card required.

trueguard-logo© 2026 Trueguardinfo@trueguard.io