International Phone Validator - Free Beta avatar

International Phone Validator - Free Beta

Pricing

Pay per usage

Go to Apify Store
International Phone Validator - Free Beta

International Phone Validator - Free Beta

Validate, classify, deduplicate, and format up to 1,000 international phone numbers offline with bundled libphonenumber metadata.

Pricing

Pay per usage

Rating

0.0

(0)

Developer

Rex Law

Rex Law

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

4 days ago

Last modified

Categories

Share

Validate, normalize, classify, and deduplicate up to 1,000 phone numbers per run. The Actor uses the pinned libphonenumber-js/max metadata package entirely inside its container. An Actor run makes zero outbound network requests and needs no external API, API key, login, cookie, proxy, or target website.

The package installation happens when the image is built. After that, validation is deterministic for the metadata version bundled in that image. The Docker smoke runs the finished image with --network=none to prove that the runtime path has no network dependency.

Input

{
"phoneNumbers": [
"+1 202-555-0123",
"(202) 555-0123",
"+44 20 7946 0018",
"+33 1 23 45 67 89",
"123"
],
"defaultCountry": "US"
}
  • phoneNumbers is required and must contain 1-1,000 strings.
  • Each string is limited to 120 characters; the maximum raw input payload represented by phone strings is therefore bounded at 120,000 characters.
  • International numbers should start with + and their country calling code.
  • defaultCountry is optional. It must be a supported two-letter ISO 3166-1 code and is used only when an input does not contain an international calling code.
  • Unknown top-level fields, unsupported default countries, non-string items, oversized items, and arrays outside the hard limit fail before processing.
  • Malformed or invalid phone-number strings inside an otherwise valid request produce their own dataset rows and stable error codes. They do not discard other results.

Deduplication

Parsed numbers are deduplicated by E.164, so +1 202-555-0123, (202) 555-0123, and 202.555.0123 with defaultCountry: "US" produce one result. The first input and its zero-based inputIndex remain the primary row. Later matches appear in duplicateInputs, and duplicateCount reports how many were removed.

Inputs that cannot be parsed do not have an E.164 identity. Those are deduplicated only when their whitespace-trimmed input text is identical. Output is strictly bounded at 1,000 rows.

Dataset output

{
"type": "phone_validation",
"inputIndex": 0,
"input": "+1 202-555-0123",
"valid": true,
"possible": true,
"e164": "+12025550123",
"internationalFormat": "+1 202 555 0123",
"nationalFormat": "(202) 555-0123",
"country": "US",
"callingCode": "+1",
"numberType": "FIXED_LINE_OR_MOBILE",
"duplicateCount": 1,
"duplicateInputs": [
{
"inputIndex": 1,
"input": "(202) 555-0123"
}
],
"error": null,
"checkedAt": "2026-07-18T12:00:00.000Z"
}

Every row always contains every documented field. Fields that cannot be derived are null; they are never guessed. country is the ISO alpha-2 numbering-plan country when the metadata can identify one. numberType can be FIXED_LINE, MOBILE, FIXED_LINE_OR_MOBILE, TOLL_FREE, PREMIUM_RATE, SHARED_COST, VOIP, PERSONAL_NUMBER, PAGER, UAN, VOICEMAIL, or null.

An invalid row includes a structured error:

{
"input": "123",
"valid": false,
"possible": false,
"e164": "+1123",
"error": {
"code": "TOO_SHORT",
"message": "The number is too short for the applicable numbering plan."
}
}

Stable error codes are EMPTY_INPUT, INVALID_CHARACTERS, NOT_A_NUMBER, INVALID_COUNTRY, TOO_SHORT, TOO_LONG, INVALID_LENGTH, IMPOSSIBLE_NUMBER, and INVALID_NUMBER.

Structured summary

The OUTPUT key-value record contains a bounded summary rather than phone-number data:

{
"type": "phone_validation_summary",
"status": "completed_with_errors",
"defaultCountry": "US",
"inputsReceived": 5,
"uniqueNumbers": 4,
"duplicatesRemoved": 1,
"resultsReturned": 4,
"uniqueValidNumbers": 3,
"uniqueInvalidNumbers": 1,
"uniquePossibleNumbers": 3,
"uniqueImpossibleNumbers": 1,
"uniqueErrorsByCode": {
"TOO_SHORT": 1
},
"deduplication": "E164_THEN_TRIMMED_INPUT",
"engine": {
"library": "libphonenumber-js",
"version": "1.13.9",
"metadata": "max",
"networkRequests": 0
},
"limits": {
"maxInputs": 1000,
"maxResults": 1000,
"maxCharactersPerInput": 120
}
}

status is success only when every unique result is valid. It is completed_with_errors when one or more rows are invalid or unparseable. Per-item validation failures are expected data outcomes, so even an all-invalid batch completes and returns all bounded error rows. Invalid top-level input still fails the run closed.

Capability boundary

possible means the digit count is possible for the numbering plan in the bundled metadata. valid means the number pattern is valid under that same metadata. Neither value proves that a number:

  • has been assigned;
  • is currently active or reachable;
  • can receive calls, SMS, or messaging-app traffic;
  • belongs to any person or organization;
  • uses a particular carrier;
  • is located where the country or calling code suggests at validation time.

This Actor does not perform carrier lookup, subscriber or real-name lookup, HLR lookup, line reachability tests, live calling, SMS verification, geolocation, or data-broker enrichment. It does not claim those capabilities in output or logs.

Phone numbers can be personal data. The Actor does not transmit them to a third party, and logs contain counts only. Inputs and results are still stored in the run's Apify storage according to the user's Apify account settings and retention policy; callers remain responsible for lawful handling and deletion.

Local verification

Requires Node.js 22 or newer.

npm ci
npm test
npm run check
npm run smoke

The deterministic local smoke executes the full Actor entrypoint against five fixed inputs. It expects three valid unique numbers, one invalid number, and one E.164 duplicate.

Docker offline smoke

Docker must be running:

npm run smoke:docker

The script builds the release image, runs the same fixed Actor input with Docker networking disabled, reads the dataset and OUTPUT record from a temporary local Apify store, and validates every expected count and representative field.

Fail-closed FREE/LIMITED release

Local release validation makes no external request and changes no state:

npm run release -- --validate-only

The exact intentional publication command is:

CONFIRM_PUBLISH_FREE_BETA=international-phone-validator-free-beta npm run release

The real release path is deliberately gated. It creates or updates the beta while private, enforces LIMITED_PERMISSIONS, refuses to overwrite an already-public beta, uploads the pinned source, waits for a successful build, and runs a fixed five-number private-cloud smoke. The smoke is offline with respect to phone data: it uses only bundled metadata and asserts networkRequests: 0. Only after the smoke and a second private/FREE check does the workflow change isPublic.

The release workflow never creates or modifies pricing configuration. It rejects any non-FREE state it can observe, scans every outbound request body for the forbidden pricing field, and attempts to return the Actor to private if final publication verification fails. Any failed build, smoke, permission, privacy, or FREE-state gate stops publication.

Do not run the intentional publication command unless a public Apify release is desired. This repository verification did not call the Apify API and did not publish anything.

Maintenance

Phone numbering plans change. Upgrade libphonenumber-js deliberately, review its metadata and changelog, regenerate the lockfile, rerun all unit and offline smoke tests, and publish a new build only after the same gates pass. Results should always be interpreted against the metadata version in the deployed image.