MSISDN HLR API
The MSISDN HLR REST API lets you enrich Spanish (ES) and Mexican (MX) mobile phone numbers with MCC, MNC, operator name and portability status — updated from official portability databases.
Two modes are available: single-number real-time lookup and bulk batch enrichment (CSV/XLSX file upload). Both modes are credit-based. Credits are deducted at job submission and are non-refundable.
ES · prefix 34)
and Mexico (MX · prefix 52).
Numbers from other countries are returned with status: "unknown".
Base URL
All paths below are relative to this base URL. All requests and responses use JSON unless otherwise noted.
Authentication
All API endpoints require a Bearer token in the Authorization header.
Alternatively, pass the token as a query parameter:
Credits & Pricing
Credits are consumed per enriched number. Pricing is tiered and progressive — each band applies only to numbers within that range. Minimum charge is $25.00 USD.
| Volume | Rate per number |
|---|---|
| 1 – 1,000,000 | $0.0025 |
| 1,000,001 – 5,000,000 | $0.0020 |
| 5,000,001 – 10,000,000 | $0.0015 |
| 10,000,001 – 20,000,000 | $0.0010 |
| Over 20,000,000 | $0.0005 |
Minimum charge $25.00. Credits can be purchased from the Dashboard → Billing page.
/api/lookup
Enrich a single mobile number in real time. Consumes 1 credit per call (deducted immediately).
Request body (JSON)
| Field | Type | Required | Description |
|---|---|---|---|
| msisdn | string | Yes | The phone number to look up. Accepts E.164 (+34612345678), with prefix without +, or local national format. |
Example request
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"msisdn":"+34612345678"}'
Response 200 OK
"original": "+34612345678",
"normalized": "34612345678",
"country_iso": "ES",
"status": "ported",
"mcc": "214",
"mnc": "07",
"operator_name": "Movistar"
}
/api/batch
Submit a CSV or XLSX file for bulk enrichment. Credits are deducted at submission. The job runs asynchronously — poll /api/batch/:uuid for progress.
Request multipart/form-data
| Field | Type | Required | Description |
|---|---|---|---|
| file | file | Yes | CSV, XLSX or TXT file. Numbers must be in column A, one per row. Include country prefix (+34, +52…). Max 512 MB. |
Example request
-H "Authorization: Bearer YOUR_API_TOKEN" \
-F "file=@/path/to/numbers.csv"
Response 202 Accepted
"uuid": "550e8400-e29b-41d4-a716-446655440000",
"status": "processing",
"total_numbers": 150000,
"price": 375.00
}
/api/batch/{uuid}
Poll the status of a batch job. Recommended polling interval: every 3 seconds.
Path parameter
| Parameter | Description |
|---|---|
| uuid | The UUID returned by POST /api/batch. |
Response 200 OK
"uuid": "550e8400-e29b-41d4-a716-446655440000",
"status": "done",
"progress_pct": 100,
"total_numbers": 150000,
"enriched_count": 148320,
"failed_count": 1680,
"expires_at": "2026-04-11T14:30:00+00:00"
}
Status lifecycle
| Value | Meaning |
|---|---|
| pending | Job is queued, not yet started. |
| processing | Enrichment in progress. progress_pct increases from 0 to 100. |
| done | Enrichment complete. Download available until expires_at. |
| failed | Processing failed. Contact support with the job UUID. |
/api/batch/{uuid}/download
Download the enriched CSV file for a completed job. Only available when status is done.
Files expire 24 hours after completion.
Example request
-H "Authorization: Bearer YOUR_API_TOKEN" \
https://msisdnhlr.com/api/batch/550e8400-e29b-41d4-a716-446655440000/download
Response
Returns the enriched CSV file as a download with Content-Type: text/csv.
| CSV Column | Description |
|---|---|
| original_number | The number as submitted in your input file. |
| normalized | E.164 digits without + (e.g. 34612345678). |
| country_iso | ISO 3166-1 alpha-2 country code (ES, MX), or empty if unknown. |
| status | See status values. |
| mcc | Mobile Country Code (e.g. 214 for ES). |
| mnc | Mobile Network Code (e.g. 07 for Movistar ES). |
| operator_name | Operator name (e.g. Movistar, Telcel), or empty if unknown. |
Result Object
Every enriched number (in single lookup responses and CSV rows) has the following fields:
| Field | Type | Nullable | Description |
|---|---|---|---|
| original | string | No | Input number as received. |
| normalized | string | No | Digits only, no country prefix sign. |
| country_iso | string | Yes | ES or MX. null if not identified. |
| status | string | No | See status values. |
| mcc | string | Yes | 3-digit Mobile Country Code. |
| mnc | string | Yes | 2–3 digit Mobile Network Code. |
| operator_name | string | Yes | Human-readable operator name. |
Status Values
| Value | Meaning |
|---|---|
| ported | Number found in the official portability database. The returned operator is the current one (post-port). |
| range | Number matched to a number range. Operator is the original range holder (may have ported away). |
| unknown | Number could not be matched to any operator or country. MCC, MNC and operator will be null. |
| invalid | Input could not be parsed as a phone number (e.g. too short, non-numeric). |
HTTP Error Codes
| Code | Meaning |
|---|---|
| 401 | Missing or invalid API token. |
| 402 | Insufficient credit balance. Top up via the Dashboard → Billing page. |
| 404 | Job not found or does not belong to your account. |
| 409 | Download requested but job is not yet complete. |
| 410 | Download file has expired (older than 24 hours). |
| 422 | Validation error (missing field, unsupported file type, etc.). Error details in response body. |
| 500 | Internal server error. Please retry or contact support. |
Error response body
"error": "Insufficient credit balance.",
"balance": 12.50,
"required": 25.00
}
Quick Start — Python Example
TOKEN = "YOUR_API_TOKEN"
BASE = "https://msisdnhlr.com"
HEADERS = {"Authorization": f"Bearer {TOKEN}"}
# 1. Submit batch
with open("numbers.csv", "rb") as f:
r = requests.post(f"{BASE}/api/batch", headers=HEADERS, files={"file": f})
r.raise_for_status()
uuid = r.json()["uuid"]
# 2. Poll until done
while True:
s = requests.get(f"{BASE}/api/batch/{uuid}", headers=HEADERS).json()
print(s["progress_pct"], "%")
if s["status"] == "done": break
if s["status"] == "failed": raise RuntimeError("Job failed")
time.sleep(3)
# 3. Download results
r = requests.get(f"{BASE}/api/batch/{uuid}/download", headers=HEADERS)
with open("enriched_numbers.csv", "wb") as f:
f.write(r.content)