Quickstart — your first lookup in 5 minutes
Make a free test lookup right now. No account, no charges, no real phone networks touched.
-
Use the test credentials
Point at the testing endpoint with the shared test key. Test lookups are free.
Endpoint: https://testing.hlrlookup.com/apiv2/hlr api_key: speedtest api_secret: speedtest -
Make a request
Pick your language:
-
Read the result
You'll get JSON back. The field that matters most is
live_status:"live_status": "LIVE"That's it — the number is active and reachable. See what every live_status value means →
-
Go to production
Swap the URL to
https://api.hlrlookup.com/apiv2/hlrand use your real key and secret from portal.hlrlookup.com. Nothing else changes.
Understanding the response
Every successful lookup returns JSON. Three fields do most of the work — here's the shape of a response and where to look.
{
"body": {
"results": [
{
"error": "NONE",
"uuid": "4562ab7e-af26-4d42-87a0-b84bb4e5f96c",
"credits_spent": 1,
"detected_telephone_number": "447540822872",
"live_status": "LIVE", // ← is it active & reachable?
"telephone_number_type": "MOBILE", // ← mobile, landline, VoIP…
"current_network_details": { // ← which network, right now
"name": "EE Limited (T-Mobile)",
"mccmnc": "23430",
"country_iso3": "GBR"
},
"is_ported": "YES",
"timestamp": "2022-10-07T13:06:15Z"
}
]
}
}The three fields most customers use
live_status — is this number active and reachable? Most business logic hangs off this one field.
telephone_number_type — mobile, landline, VoIP, premium and more. Use it to route calls or exclude non-SMS numbers.
current_network_details — the network the number is on today, including after porting. Useful for routing and least-cost decisions.
Two rules that will save you an integration headache
1. JSON is unordered. Never rely on fields appearing in the same order — parse by field name.
2. We add fields, we never change them. New features arrive as new response fields, so upgrades never break your integration. Anything that costs extra is opt-in (like get_ported_date) — you must ask for it in your request.
Matching responses to requests
Sending multiple numbers in one request? Use request_parameters.telephone_number in each result to match it back to what you submitted. Every result also carries a unique uuid.
live_status values
live_status tells you whether a phone number is real, active and reachable right now. It's always an UPPERCASE string — match it exactly.
LIVEActive and reachable — safe to call or SMSKeep it ✓
This telephone number is live and is assigned to a subscriber who has recently used their telephone.
DEADConfirmed inactive by the network — will not receive calls or SMSRemove it ✗
This telephone number has been confirmed to be a dead telephone number by the telephone network and will not receive telephone calls or text messages because the telephone number is not assigned to an existing SIM card or user.
ABSENT_SUBSCRIBERReal number, but currently unreachableRetry later ↻
This telephone number is assigned to a mobile SIM card, but either:
- There has been a prolonged period of inactivity from the mobile handset
- The mobile phone has been turned off, or gone out of radio coverage, and someone has attempted to call or send an SMS to the telephone number
- The number is allocated to a new SIM card and the SIM card has not yet been registered to a user
NO_TELESERVICE_PROVISIONEDActive, but the subscription can't receive SMSCall, don't text ☎
If the mobile subscription identified by the given phone number does not include the short message service, the teleservice not provisioned error is returned. This means that the number is active in the HLR database for that network, but the user is not able to receive SMS messages.
INCONCLUSIVECouldn't get a definitive answer this timeRetry later ↻
We were currently unable to ascertain the status for this number.
NOT_AVAILABLE_NETWORK_ONLYThis network doesn't share live/dead status — porting info onlyUse network data
The telephone network who owns the telephone number does not provide Live/Dead type status, but will provide porting information if the telephone number has been ported to another network.
current_network and is_ported from the same response — they're still accurate. The Coverage Checker shows which networks fall into this category.NO_COVERAGEWe can't check this network — you are not chargedFree result
We do not currently have coverage to detect the live status for this telephone network. You are not charged for a NO_COVERAGE result.
NOT_APPLICABLENo valid phone number detected in your inputFix the input
Used if there is no detected telephone number. You'll usually see telephone_number_type: "BAD_FORMAT" alongside this.
LIVE, remove DEAD, re-queue ABSENT_SUBSCRIBER and INCONCLUSIVE for a second pass in 3–7 days.telephone_number_type values
What kind of number is it? Use this field to route calls, exclude non-SMS numbers, or spot premium-rate traps. The example numbers are real test values — use them to test your handling of each type.
| Value | Example / test number | What it means |
|---|---|---|
MOBILE | 447540822872 | A phone number assigned to a Mobile Telephone Operator / Cell Phone Operator |
LANDLINE | 441133910781 | A fixed wired telephone in a fixed location such as home or office |
MOBILE_OR_LANDLINE | 917795519731 | Where the telephone number could belong to either a mobile or landline type phone |
VOIP | 6531522665 | Assigned to a VoIP provider who delivers calls and text messages over the internet to the end user or service |
TOLL_FREE | 818000804085 | Also called Freephone. The number can be called from within the home country at no cost. There may be a charge to call this number from outside the home country |
PREMIUM | 448719429222 | There is a premium charge added to call this number, often there is a service provided on the number and by calling the number the user pays for the service using their telephone bill. The callee typically receives revenue from the call |
SHARED_COST | 420844844850 | This telephone number will charge both the caller and callee at the same time. There will be an additional charge to call this type of number |
PERSONAL_NUMBER | 543876478000 | A telephone number intended to be used by individuals which may route to multiple locations (home, office and cell phone). Some PERSONAL_NUMBERs are charged at a PREMIUM number rate |
UNIVERSAL_ACCESS_NUMBER | 443332022222 | A single telephone number which can be used to route calls to multiple destinations depending on parameters such as time of day, where the caller resides, capacity or more. This is not linked to any specific locality within the country. Typically used by companies to provide a single telephone number instead of multiple telephone numbers |
STAGE_AND_SCREEN | — | Reserved by a regulatory body specifically for use with stage and screen for example used in films when speaking a telephone number. No real end user will be assigned to this number and often they are used as fictitious numbers. |
PAGER | 447640123456 | Allocated to a device that receives and displays numeric or alphanumeric messages |
VOICEMAIL_ONLY | 3538956000000 | Access number for voicemail only |
MACHINE_TO_MACHINE | 337000011460640 | IoT style devices that have a sim card and need a phone number to connect to the mobile network. They're usually devices that report back sensor readings, or need to connect to the phone network for a data connection. |
BAD_FORMAT | 7540 | The number you submitted to be checked could not be identified as a valid international telephone number |
UNKNOWN | — | Any number which does not fall into any of the above categories or error=INSUFFICIENT_CREDIT or error=INTERNAL_ERROR |
All response fields
Every field in a lookup result, in one searchable table.
| Field | Type | What it tells you |
|---|---|---|
error | String | NONE, INSUFFICIENT_CREDIT or INTERNAL_ERROR. Match exactly. |
uuid | String | Unique ID for this individual result. |
request_parameters.* | Object | Echo of your request with defaults filled in. Use .telephone_number to match results to your submitted numbers. If you don't send save_to_cache, it defaults to YES; omitted cache_days_private/cache_days_global default to your account settings. |
credits_spent | Float | Credits used for this lookup. Parse to 1 decimal place to allow for 0.5 / 1.5-credit services. |
detected_telephone_number | String | The number standardised to E164 — valid country code, area and digit count. Empty string if no valid number was detected. |
formatted_telephone_number | String | The detected number in your requested output_format. Empty if you didn't request one. |
live_status | String | Is the number active and reachable? All values → |
telephone_number_type | String | Mobile, landline, VoIP, premium… All values → |
original_network | String | AVAILABLE (details populated), NOT_AVAILABLE or NOT_APPLICABLE (details will be {}). |
original_network_details | Object | Network the number was first assigned to: name, mccmnc, country_name, country_iso3, area, country_prefix. Names can change over time (mergers, re-allocation). |
current_network | String | AVAILABLE, NOT_AVAILABLE or NOT_APPLICABLE (e.g. BAD_FORMAT, NO_COVERAGE, DEAD, INCONCLUSIVE). |
current_network_details | Object | Network the number is on now — same keys as original, minus area. mccmnc is empty for non-mobile numbers. |
is_ported | String | YES, NO or UNKNOWN — has the number moved from its original network? |
ported_date | String | Optional — only returned when requested. ISO 8601 date last ported, NOT_AVAILABLE or NOT_APPLICABLE. |
landline_status | String | Optional — returned when requested and the number is a landline. Raw values map to LIVE / DEAD / INCONCLUSIVE — full mapping table →. |
timestamp | ISO 8601 | When this record was checked. If served from cache, this is the original check time. |
sms_email | String | Email-to-SMS gateway address for this number, where the network provides one (e.g. 07540822872@t-mobile.uk.net). Empty string if not available. |
mms_email | String | Email-to-MMS gateway address for this number, where the network provides one. Empty string if not available. |
disposable_number | String | YES, NO or UNKNOWN — whether this is a disposable/virtual number. Not present on every result. |
usa_status (top-level) | String | Only present when you requested the usa_status feature — confirms the feature was turned on for this lookup. Not to be confused with request_parameters.usa_status, which just echoes what you sent. |
Errors & HTTP codes
Two layers: HTTP status codes on the whole request, and an error field on each individual result.
HTTP status codes
| Code | Meaning | What to do |
|---|---|---|
200 | Request succeeded — check each result's error field. | Parse the JSON body. |
400 | Bad Request. Your request itself was malformed — for example: an invalid output_format, an invalid save_to_cache value, a cache_days_private/cache_days_global outside 0–30 or not an integer, a required parameter missing, a parameter we don't recognise, or an input_format that isn't a valid ISO3 code. | Check your request body against the request parameters reference. You were not charged. |
401 | Unauthorized — your api_key or api_secret is wrong. | Double-check your credentials in the portal. You were not charged. |
404 | Not Found — the request went to an endpoint we don't support. | Check you're calling https://api.hlrlookup.com/apiv2/... and not a typo'd path. |
429 | Too many requests. A telephone network is slowing down, so we backed off. Your number was not sent to the network. | Resubmit the number. Slow your rate overall, or just for that network. Most clients under 50 req/s never see this — but handle it anyway. |
500 | Internal Server Error — we hit something we didn't expect on our end. | Retry. If it persists, contact support@hlrlookup.com. You were not charged. |
200 response, just with telephone_number_type: "BAD_FORMAT" in the body. Only build HTTP-level error handling for genuine request problems — not for bad numbers, which come back as ordinary results.The error field (per result)
| Value | Meaning |
|---|---|
NONE | No errors — the lookup ran normally. |
INSUFFICIENT_CREDIT | No credit left on the api_key you used. Top up, or check your balance via the balance endpoint. |
INTERNAL_ERROR | We hit something unexpected. Retry; if it persists, contact support. |
Rate limits at a glance
You're limited to 210 concurrent requests. Don't wait for a response before sending the next request — fire in parallel. If a slow network takes 10 seconds per lookup, 20 requests/second still gives you a steady throughput of 20/second after the initial lag.
Number formatting — input
Use the input_format parameter to submit locally-formatted numbers without converting them to E.164 yourself.
How it works
By default the API expects international E.164 format (digits only, no leading +). If you pass a local number like 07790606023 without an input_format, the API can't identify which country it belongs to and returns BAD_FORMAT.
Add "input_format": "GBR" (a 3-letter ISO country code) to your request and the API will prepend the correct country code automatically before processing.
Examples
| Scenario | Input | Result |
|---|---|---|
| No format specified | 07790606023 (UK local) | BAD_FORMAT — unrecognisable without country context |
With input_format: GBR | 07790606023 or 447790606023 | Converts to +447790606023 — identified as MOBILE |
| No format specified | 7148307000 (USA local) | BAD_FORMAT |
With input_format: USA | 7148307000 or 17148307000 | Converts to +17148307000 — identified as LANDLINE |
| Invalid ISO3 code | Any number | HTTP 400 Bad Request |
Request example
curl -H "Content-Type: application/json" -X POST \
-d '{"api_key":"your_api_key","api_secret":"your_api_secret",
"requests":[{"telephone_number":"07790606023","input_format":"GBR"}]}' \
https://api.hlrlookup.com/apiv2/hlrinput_format parameter is per-number — you can mix local and international numbers in the same batch request by setting it on individual entries only.Number formatting — output
Use the output_format parameter to get the detected number back in a consistent format for your database or downstream system.
Available formats
| Value | Output | Example |
|---|---|---|
| omitted or empty | No formatted number returned — formatted_telephone_number is empty | |
E164 | Digits only, no leading + | 447790606023 |
PLUS_E164 | Standard international format with + prefix | +447790606023 |
NATIONAL | Formatted as you'd dial it domestically, within the number's own country | varies by country |
USA or ISO3 code | Formatted for dialling from that country | 011447790606023 (from USA) |
| Unsupported value | HTTP 400 Bad Request |
Request example
curl -H "Content-Type: application/json" -X POST \
-d '{"api_key":"your_api_key","api_secret":"your_api_secret",
"requests":[{"telephone_number":"447790606023","output_format":"PLUS_E164"}]}' \
https://api.hlrlookup.com/apiv2/hlrThe formatted number appears in the formatted_telephone_number field of the response.
Cache system
The cache stores lookup results for up to 30 days, reducing latency and saving credits on repeated checks of the same number.
Two cache levels
Private cache — results from your own account's previous lookups. Retrieving from your private cache is free.
Global cache — results from other accounts that chose to share their results. Costs ½ credit per lookup.
How the cache is checked
- If
cache_days_privateis set, the private cache is checked first. A match is returned free. - If no private match, and
cache_days_globalis set, the global cache is checked. A match costs ½ credit. - If no cached result exists, a live network lookup is performed.
Cache parameters
| Parameter | Values | What it does |
|---|---|---|
cache_days_private | 0–30 | How many days back to look in your private cache. 0 = skip private cache. |
cache_days_global | 0–30 | How many days back to look in the global cache. 0 = skip global cache. |
save_to_cache | YES / NO / PRIVATE | YES = save to global cache and earn ¼ credit when others retrieve it. PRIVATE = save to private cache only. NO = don't cache this result. |
save_to_cache in your request at all, it defaults to YES — your result is saved to the global cache automatically unless you explicitly opt out.save_to_cache: YES) and another account retrieves your result, you earn ¼ credit per retrieval.Request example
curl -H "Content-Type: application/json" -X POST \
-d '{"api_key":"your_api_key","api_secret":"your_api_secret",
"requests":[{"telephone_number":"441133910781",
"cache_days_private":30,
"cache_days_global":15,
"save_to_cache":"YES"}]}' \
https://api.hlrlookup.com/apiv2/hlrPorted date feature
Retrieve the date a number was last ported to a different network. Useful for detecting port-out fraud and verifying recent number history.
Why it matters
Port-out fraud is a scheme where criminals hijack a phone number by transferring it to a new network in order to intercept one-time passwords for banking and email accounts. Knowing when a number was last ported lets you flag suspicious recent activity.
Limitations
If a number was ported away from its original network and then ported back, the last ported date cannot be retrieved.
The cache is automatically disabled when get_ported_date: YES is set, since the feature is specifically designed to catch numbers that have been recently ported.
Pricing
| Outcome | Cost |
|---|---|
| Ported date successfully retrieved | 2 credits |
| Requested but date not available | 1 credit |
Coverage
65 countries, listed below with the date each was added. Most were added July 2020; a batch of newer countries (India, Bangladesh, Nigeria, Iran, Jamaica, Georgia and others) was added July 2025. Check the Coverage Checker for the latest.
| Country | ISO3 | Date added |
|---|---|---|
| Albania | ALB | 2020-07-31 |
| Argentina | ARG | 2020-08-17 |
| Australia | AUS | 2020-07-31 |
| Austria | AUT | 2020-07-31 |
| Bahrain | BHR | 2025-07-10 |
| Bangladesh | BGD | 2025-07-10 |
| Belarus | BLR | 2020-07-31 |
| Belgium | BEL | 2020-07-31 |
| Benin | BEN | 2020-07-31 |
| Bolivia | BOL | 2020-08-17 |
| Bosnia / Herzegovina | BIH | 2020-07-31 |
| Brazil | BRA | 2020-07-31 |
| Bulgaria | BGR | 2020-07-31 |
| Canada | CAN | 2020-07-31 |
| Chile | CHL | 2020-07-31 |
| Colombia | COL | 2020-07-31 |
| Costa Rica | CRI | 2020-07-31 |
| Croatia | HRV | 2020-07-31 |
| Czech Republic | CZE | 2020-07-31 |
| Denmark | DNK | 2020-07-31 |
| Ecuador | ECU | 2020-08-17 |
| El Salvador | SLV | 2020-07-31 |
| Estonia | EST | 2020-07-31 |
| France | FRA | 2020-07-31 |
| Georgia | GEO | 2025-07-10 |
| Germany | DEU | 2020-07-31 |
| Ghana | GHA | 2020-07-31 |
| Greece | GRC | 2020-07-31 |
| Guam | GUM | 2025-07-10 |
| Hong Kong | HKG | 2020-07-31 |
| Hungary | HUN | 2020-07-31 |
| India | IND | 2025-07-10 |
| Iran | IRN | 2025-07-10 |
| Israel | ISR | 2020-07-31 |
| Italy | ITA | 2020-07-31 |
| Jamaica | JAM | 2025-07-10 |
| Kazakhstan | KAZ | 2020-07-31 |
| Latvia | LVA | 2020-07-31 |
| Lithuania | LTU | 2020-07-31 |
| Macedonia | MKD | 2020-07-31 |
| Mexico | MEX | 2020-07-31 |
| Moldova | MDA | 2025-07-25 |
| Netherlands | NLD | 2020-07-31 |
| New Zealand | NZL | 2020-07-31 |
| Nigeria | NGA | 2025-07-10 |
| Northern Marina Islands | MNP | 2025-07-10 |
| Palestine | PSE | 2020-08-17 |
| Peru | PER | 2020-07-31 |
| Poland | POL | 2020-07-31 |
| Puerto Rico | PRI | 2025-07-10 |
| Romania | ROU | 2020-07-31 |
| Russia | RUS | 2020-07-31 |
| Serbia | SRB | 2020-07-31 |
| Slovakia | SVK | 2020-07-31 |
| South Africa | ZAF | 2020-07-31 |
| South Korea | KOR | 2020-08-17 |
| Spain | ESP | 2020-08-17 |
| Sweden | SWE | 2020-07-31 |
| Switzerland | CHE | 2020-07-31 |
| Thailand | THA | 2020-07-31 |
| Turkey | TUR | 2020-07-31 |
| United States | USA | 2020-07-31 |
| Uruguay | URY | 2025-07-10 |
| US Virgin Islands | VIR | 2025-07-10 |
| Vietnam | VNM | 2025-07-10 |
Request example
curl -H "Content-Type: application/json" -X POST \
-d '{"api_key":"your_api_key","api_secret":"your_api_secret",
"requests":[{"telephone_number":"447540822872","get_ported_date":"YES"}]}' \
https://api.hlrlookup.com/apiv2/hlrThe response includes a ported_date field with an ISO 8601 date, NOT_AVAILABLE, or NOT_APPLICABLE.
Landline status feature
Check whether a UK or Irish landline number is live, dead, or has a more specific status such as out-of-order or call-barred.
Coverage
Currently available for:
- United Kingdom (GBR) — available since January 2020
- Ireland (IRL) — available since July 2021
Pricing
There is a 1 credit surcharge for landline status checks, bringing the total to 2 credits when the number is a landline in coverage.
How it works
The result is mapped into the standard live_status field (LIVE / DEAD / INCONCLUSIVE) and an additional landline_status field with the raw, more granular status the network returned. Every raw value falls into one of three buckets — the table below is the complete mapping, so any raw value you see can be looked up here:
landline_status value | Bucket |
|---|---|
LIVE | LIVE |
DEAD | DEAD |
OUT_OF_ORDER | DEAD |
NUMBER_CHANGED | DEAD |
CALL_BARRED | LIVE |
INCONCLUSIVE | INCONCLUSIVE |
UNKNOWN_NOT_AUTH | INCONCLUSIVE |
UNKNOWN_NOT_AVAIL | INCONCLUSIVE |
UNKNOWN_NOT_IMPL | INCONCLUSIVE |
UNKNOWN_REJECTED | INCONCLUSIVE |
UNKNOWN_INTERWORKING | INCONCLUSIVE |
ERROR_UNKNOWN_RESPONSE | INCONCLUSIVE |
CALL_BARRED maps to LIVE, not dead — the line exists and is in service, it's just barred from certain calls. Don't treat it as a dead number.Request example
curl -H "Content-Type: application/json" -X POST \
-d '{"api_key":"your_api_key","api_secret":"your_api_secret",
"requests":[{"telephone_number":"441133910781","get_landline_status":"YES"}]}' \
https://api.hlrlookup.com/apiv2/hlrUSA status feature
Check the live status of US mobile numbers. Standard HLR lookups return NOT_AVAILABLE_NETWORK_ONLY for US numbers — this feature provides actual live/dead status.
Coverage limitations
The feature supports US mobile numbers only. The following US territories are not covered:
- Puerto Rico
- Guam
- US Virgin Islands
Non-mobile US numbers return NOT_AVAILABLE_NETWORK_ONLY even with this feature enabled.
Pricing
| Number type | Cost |
|---|---|
| USA mobile numbers | 2 credits |
| USA non-mobile numbers | 1 credit |
| US territory mobile numbers | 1 credit |
| Non-USA numbers (feature ignored) | 1 credit |
Request example
curl -H "Content-Type: application/json" -X POST \
-d '{"api_key":"your_api_key","api_secret":"your_api_secret",
"requests":[{"telephone_number":"17148307000","usa_status":"YES"}]}' \
https://api.hlrlookup.com/apiv2/hlrThe response includes a usa_status field indicating that this optional feature was used.
Bulk uploads via API
Submit large lists of numbers via the batch API — upload a CSV, monitor progress, pause and resume, then download results.
apikey and secret as URL query parameters (e.g. ?apikey=your_api_key&secret=your_api_secret) — not the api_key / api_secret JSON body fields used elsewhere in these docs.Upload methods
Two approaches are supported:
- CSV file upload — upload a locally-stored CSV file.
- JSON body — submit numbers directly in the request body. Limited to 100,000 numbers or 2 MB.
If your CSV has multiple columns (e.g. your own reference ID alongside the number), tell us which column holds the phone numbers by adding &column= to the upload URL:
| Numbers stored in | Mapping number |
|---|---|
| Column A | 0 |
| Column B | 1 |
| Column C | 2 |
| Column D | 3 |
Omitting &column= always checks the first column. Example — numbers in column B: .../source?apikey=your_api_key&secret=your_api_secret&column=1
Batch operations
| Operation | Method | What it does |
|---|---|---|
| List all batches | GET | Retrieve status and metadata for all your batches |
| Get a batch | GET | Details and progress for a specific batch by ID |
| Create a batch | POST | Upload numbers and start or schedule processing |
| Update a batch | PUT | Pause or resume processing |
| Delete a batch | DELETE | Remove a completed batch and its results (not recoverable — download first) |
"type": "HLR_V2" when creating a batch: {"filename":"mybatch.txt","type":"HLR_V2"}. Without it, your results file is formatted using the older, incomplete legacy format and will be missing newer response fields.Optional parameters
Batch creation accepts the same optional parameters as single lookups: cache_days_private, cache_days_global, save_to_cache, output_format, input_format, get_ported_date, get_landline_status, and usa_status. Include them inside a batchArguments object when you create the batch.
Downloading results
Completed batches can be downloaded as CSV or JSON. Results include live_status, telephone_number_type, original_network, is_ported, and all other standard response fields.
Common errors
| Error | Likely cause |
|---|---|
Bad Request | Something's wrong with a parameter |
Validation failed | Wrong request method (e.g. GET instead of POST), or check filename/type/scheduledStart are correct |
Invalid batch arguments | One or more values inside batchArguments is invalid |
Invalid apikey or secret | Check your credentials |
Batch not found | Wrong batch ID |
Unauthorized | This batch doesn't belong to you, or apikey/secret is wrong |
Internal Server Error (HTTP 400) | Something's wrong with the formatting of your request — check all quotes and braces are correctly entered |
Internal Server Error (HTTP 500, with an errorId) | You've tried to upload data to a batch that already has data loaded into it |
batches.hlrlookup.com), separate from the single-lookup API. See the OpenAPI schema for the complete batch endpoint documentation.Testing suite
Test your integration for free without touching a real phone network. The testing endpoint returns pre-populated responses that simulate every status and error.
Test credentials
Endpoint: https://testing.hlrlookup.com/apiv2/hlr
api_key: speedtest
api_secret: speedtestHow it works
The testing suite is not connected to the telephone network. It contains a dataset of pre-populated example responses. Submit any number and it returns the closest match from the dataset — it won't return an exact match for every number.
Simulating 429 errors
The testing suite simulates rate limiting. Submit more than 200 requests per second to trigger 429 responses. Afghanistan numbers (country code 93) trigger 429 responses at lower rates to simulate congested network conditions — and also return slower response times.
If you know in advance which network a number is on, you can reduce requests to just that network rather than slowing down overall. Ask your account manager about our e164 validation service — it detects a number's network without performing a full HLR lookup.
Sample test numbers
| Country | Number |
|---|---|
| Afghanistan | 93749769622, 93770554876 |
| South Africa | 27846073982, 27847152603, 27848591518 |
| Greece | 306907339774, 306917695569, 306923469843 |
| Spain | 34699000823, 34722245534 |
| Portugal | 351911543003, 351964950422 |
| Ukraine | 380950937486, 380980328342, 380996049969 |
| Serbia | 381615582482, 381621601528 |
This is a small sample — a longer list is available on request from support@hlrlookup.com. You can also submit any number not in this list; you'll get back the closest match from the test dataset rather than an error.
Charging
Credits are only spent on HTTP 200 responses. Errors (400, 401, 429, 500, 404) are always free. See the pricing page for the per-credit cost.
Always free
- HTTP 400, 401, 429, 500, 404 responses
error: INSUFFICIENT_CREDITorerror: INTERNAL_ERRORresultstelephone_number_type: BAD_FORMAT- Non-mobile numbers with
live_status: NO_COVERAGE
Base charges (HLR — no optional features)
| Result | Credits |
|---|---|
| Live lookup (LIVE, DEAD, ABSENT_SUBSCRIBER, NO_TELESERVICE_PROVISIONED, NOT_AVAILABLE_NETWORK_ONLY, INCONCLUSIVE) | 1 credit |
| Retrieved from global cache | ½ credit |
| Retrieved from private cache | Free |
NO_COVERAGE | Free |
Optional features (additional charges)
| Feature | When available | Total cost |
|---|---|---|
| Ported date — retrieved | 65 countries — see list | 2 credits |
| Ported date — not available | 65 countries — see list | 1 credit |
| Landline status (UK/Ireland) | GBR, IRL | 2 credits |
| USA status — mobile | USA only | 2 credits |
| USA status — non-mobile | USA only | 1 credit |
MNP endpoint
MNP lookups cost half the HLR rate. Base charge: ½ credit live, ¼ credit from global cache. With ported date: ½–1½ credits. Ported dates are never cached on MNP lookups.
save_to_cache: YES) and earn ¼ credit every time another account retrieves your result.Request parameters
All parameters you can send in a lookup request, including authentication, per-number options, and optional features.
Endpoints
| Lookup type | URL |
|---|---|
| HLR (live status + network) | https://api.hlrlookup.com/apiv2/hlr |
| MNP (network only, cheaper) | https://api.hlrlookup.com/apiv2/mnp |
| Validator (format check only) | https://api.hlrlookup.com/apiv2/validate |
| Balance check | https://api.hlrlookup.com/apiv2/balance |
| Testing (free, no real network) | https://testing.hlrlookup.com/apiv2/hlr |
Checking your balance
Check your remaining credits at any time — just your api_key and api_secret, no other parameters needed:
curl -s -H "Content-Type: application/json" -X POST \
-d '{"api_key":"your_api_key","api_secret":"your_api_secret"}' \
https://api.hlrlookup.com/apiv2/balance{
"body": {
"Status": "OK",
"Credits": 15000
}
}Authentication (required)
| Parameter | Type | Description |
|---|---|---|
api_key | String | Your account API key — found in the portal under account settings |
api_secret | String | Your account API secret — found alongside the key |
requests | Array | Array of lookup objects. Up to 80 numbers per request. Results are returned in the same order as submitted. |
requests[].telephone_number | String | The number to look up. E.164 digits preferred (e.g. 447540822872). Non-digit characters (+, spaces, dashes, parentheses) are accepted. |
Optional parameters (per number)
| Parameter | Values | Description |
|---|---|---|
cache_days_private | 0–30 | Look back in your private cache this many days. 0 = bypass. Free. |
cache_days_global | 0–30 | Look back in the global cache this many days. 0 = bypass. Costs ½ credit. |
save_to_cache | YES / NO / PRIVATE | Whether to save this result to cache. YES = global (earn ¼ credit on retrieval). PRIVATE = your cache only. |
output_format | E164 / PLUS_E164 / ISO3 | Format for formatted_telephone_number in the response. See Number formatting — output. |
input_format | ISO3 country code | Treat the submitted number as a local number from this country. See Number formatting — input. |
get_ported_date | YES / NO | Retrieve the date the number was last ported. Additional charge applies. Disables cache. |
get_landline_status | YES / NO | Retrieve detailed landline status (UK and Ireland only). Additional charge applies. |
usa_status | YES / NO | Retrieve live status for US mobile numbers. Additional charge applies. |
HTTP POST format
The API accepts requests in JSON (recommended) or form-encoded format. JSON supports batch requests; form-encoded is single numbers only.
JSON format
Set Content-Type: application/json and POST a JSON body.
Single number
{
"api_key": "your_api_key_here",
"api_secret": "your_api_secret_here",
"requests": [
{ "telephone_number": "447540822872" }
]
}Single number with parameters
{
"api_key": "your_api_key_here",
"api_secret": "your_api_secret_here",
"requests": [
{
"telephone_number": "447540822872",
"cache_days_private": 30,
"output_format": "PLUS_E164",
"save_to_cache": "YES"
}
]
}Multiple numbers
{
"api_key": "your_api_key_here",
"api_secret": "your_api_secret_here",
"requests": [
{ "telephone_number": "447540822872", "cache_days_private": 30 },
{ "telephone_number": "441133910781", "output_format": "E164" },
{ "telephone_number": "07790606023", "input_format": "GBR" }
]
}Form-encoded format
Set Content-Type: application/x-www-form-urlencoded. Single numbers only — batching is not supported.
curl -X POST \
-d "api_key=your_api_key&api_secret=your_api_secret&telephone_number=447540822872&cache_days_private=8&output_format=PLUS_E164&save_to_cache=YES" \
https://api.hlrlookup.com/apiv2/hlrOptional HTTP headers
| Header | Description |
|---|---|
ReferenceID | Your own reference ID — echoed back in the response for reconciliation |
AccountID | Your account reference ID |
Example requests & responses
Working examples of common lookup scenarios — single number, multiple numbers, optional features, and error responses.
Single number lookup
curl -H "Content-Type: application/json" -X POST \
-d '{"api_key":"your_api_key","api_secret":"your_api_secret",
"requests":[{"telephone_number":"447540822872"}]}' \
https://api.hlrlookup.com/apiv2/hlr{
"body": {
"results": [{
"error": "NONE",
"uuid": "4562ab7e-af26-4d42-87a0-b84bb4e5f96c",
"credits_spent": 1,
"detected_telephone_number": "447540822872",
"live_status": "LIVE",
"telephone_number_type": "MOBILE",
"original_network": "AVAILABLE",
"original_network_details": {
"name": "O2 (UK)", "mccmnc": "23410",
"country_name": "United Kingdom", "country_iso3": "GBR",
"area": "United Kingdom", "country_prefix": "44"
},
"current_network": "AVAILABLE",
"current_network_details": {
"name": "EE Limited (T-Mobile)", "mccmnc": "23430",
"country_name": "United Kingdom", "country_iso3": "GBR",
"country_prefix": "44"
},
"is_ported": "YES",
"timestamp": "2022-10-07T13:06:15Z"
}]
}
}With optional parameters
Adding get_ported_date: YES and output_format: PLUS_E164:
{
"error": "NONE",
"credits_spent": 1,
"detected_telephone_number": "447540822872",
"formatted_telephone_number": "+447540822872",
"live_status": "LIVE",
"is_ported": "YES",
"ported_date": "NOT_AVAILABLE",
"timestamp": "2022-10-07T13:06:15Z"
}Only 1 credit here — the ported date feature is charged at 1 credit when requested but not available, and 2 credits only when a date is actually returned.
Multiple numbers
Each number in the requests array gets its own result in the same position. Use request_parameters.telephone_number to match results back to your inputs.
Error responses
| HTTP code | Body | Cause |
|---|---|---|
401 | {"error":"UNAUTHORIZED","message":"Invalid api_key or api_secret"} | Wrong credentials |
400 | {"error":"BAD_REQUEST","message":"Invalid output_format requested"} | Invalid parameter value |
429 | {"error":"TOO_MANY_REQUESTS","message":"Please try again later"} | Resubmit this number |
404 | no body | Wrong endpoint URL — check you're calling api.hlrlookup.com/apiv2/... |
500 | no body | Something went wrong our end — retry, then contact support if it persists |
MCP Server
Give AI tools and coding agents — like Claude Code — direct access to HLR Lookup through the Model Context Protocol (MCP). No custom integration code required.
Server details
| Production endpoint | https://api.hlrlookup.com/mcp |
| Server version | hlr-api-mcp v1.0.0 |
| MCP protocol version | 2025-06-18 |
| Transport | Stateless streamable HTTP. There's no session ID — responses come back as Server-Sent Events (SSE). |
Available tools
The server exposes four tools:
| Tool | What it does | Credits |
|---|---|---|
hlr_lookup | Live HLR lookup for up to 80 numbers in one request. | Yes |
mnp_lookup | Mobile number portability lookup, same request/response shape as hlr_lookup. | Yes |
validate | Offline format/network validation — no live lookup performed. | No |
balance | Returns the remaining credit balance for the authenticated account. | No |
hlr_lookup
Performs a live HLR lookup for up to 80 telephone numbers in a single request. The response can include telephone_number_type, live_status, original network, current network, mobile number portability information, and the sms_email/mms_email gateway addresses.
Optional additional fields: ported_date, landline_status, usa_status. Requesting usa_status performs an extra live US status check and consumes an additional credit.
mnp_lookup
Same general request and response structure as hlr_lookup.
+1 country-code (NANP) numbers, the mnp_lookup response does not include telephone_number_type. [To confirm: our own MNP example elsewhere in these docs shows telephone_number_type missing for a UK number too — worth checking whether this is actually a NANP-specific limitation or true of MNP lookups generally, before this goes live.]validate
Checks formatting and network information without performing a live HLR lookup — useful for pre-flight checks before spending credits on a real lookup. Free — consumes no credits.
balance
Returns the remaining credit balance for the authenticated account. Free — consumes no credits.
Authentication
Every tool call needs authentication — including validate and balance, which don't consume credits. "Free" means zero credits, not no login: you still need a valid API key and secret. Only MCP schema discovery works without authentication.
Send credentials as a Bearer token in the Authorization header:
Authorization: Bearer <api_key>:<api_secret>Manage your API credentials in the HLR Lookup portal.
Shared lookup options
All lookup tools (hlr_lookup, mnp_lookup, validate) share the same input, output and cache options.
Input format
Submit numbers in national or E.164 format. For national format, add input_format with the relevant ISO 3166-1 alpha-3 country code — e.g. GBR.
Output format
| Value | What you get |
|---|---|
E164 | Digits only, no leading + |
PLUS_E164 | Standard international format with + prefix |
NATIONAL | Formatted as you'd dial it domestically, within the number's own country |
| ISO3 country code | Formatted for dialling from that country |
Cache controls
| Parameter | Values |
|---|---|
cache_days_global | 0–30 |
cache_days_private | 0–30 |
save_to_cache | YES / NO / PRIVATE |
Error handling
Authentication and request errors come back as valid MCP error responses with isError: true. The structured content includes a link to the account settings page, the expected Authorization header format, and an explanation that valid credentials are required.
Connecting with Claude Code
Add the server with:
claude mcp add --transport http hlrlookup https://api.hlrlookup.com/mcp \
--header "Authorization: Bearer <api_key>:<api_secret>"Replace <api_key> and <api_secret> with your own credentials. Once added, Claude Code can discover and call the available HLR Lookup tools directly.