The Telnyx Number Lookup API returns carrier, line type, and portability data for any phone number in a single request. This walkthrough builds a Python Flask service that wraps that API with a 24-hour in-memory cache so repeat lookups are free and fast.
The canonical code example is in the Telnyx code examples repo:
https://github.com/team-telnyx/telnyx-code-examples/tree/main/phone-number-lookup-python
What This Example Builds
The Flask app exposes three endpoints:
| Method |
Path |
Purpose |
POST |
/lookup |
Look up a number from a JSON body |
GET |
/lookup/<phone_number> |
Look up a number from the URL path |
GET |
/cache/stats |
Inspect the in-memory cache |
When a lookup request arrives, the app checks the cache first. On a cache miss, it calls the Telnyx Number Lookup API, extracts carrier name, carrier type, line type, number type, and portability status, caches the result for 24 hours, and returns a clean JSON response. Cached responses include "from_cache": true so the caller knows the data was served locally.
Why Phone Number Lookup Matters
Number lookup is not a standalone product — it is a building block. Developers use it to:
-
Screen inbound calls — Check carrier and line type before connecting a caller to an agent. VoIP numbers from unknown carriers may warrant additional verification.
-
Enrich leads — Sales teams want to know whether a prospect's number is a real mobile line or a disposable VoIP number before spending time on outreach.
-
Route messages — Some messaging workflows behave differently for landlines (which cannot receive SMS) versus mobile numbers.
-
Detect porting — A number that was recently ported may indicate a customer switching carriers, which matters for compliance and deliverability.
The Telnyx Number Lookup API provides this data from Telnyx's own carrier network — not resold from a third party.
Products Used
telnyx_products: [Number Lookup]
Number Lookup is a single API call: GET /v2/number_lookup/{phone_number}. No webhooks, no long-running jobs, no polling.
Architecture
HTTP Request (POST /lookup or GET /lookup/<number>)
|
v
+--------------------+ hit
| In-memory cache |-----------> cached result (from_cache: true)
| (24h TTL) |
+--------+-----------+
| miss
v
+--------------------+
| Telnyx Number |
| Lookup API |
+--------+-----------+
|
+---> carrier + line type + portability (cached, returned)
Prerequisites
- Python 3.8+
- A Telnyx account with a funded balance
- A Telnyx API key from the Telnyx Portal
- curl or Postman to test the endpoints
Setup
git clone https://github.com/team-telnyx/telnyx-code-examples.git
cd telnyx-code-examples/phone-number-lookup-python
cp .env.example .env
pip install -r requirements.txt
Edit .env and set your API key:
TELNYX_API_KEY=KEY0123456789ABCDEF
FLASK_DEBUG=false
Start the server:
python app.py
The app runs on http://localhost:5000.
How The Code Works
Client Initialization
The Telnyx Python SDK client is created once at startup:
client = telnyx.Telnyx(api_key=os.getenv("TELNYX_API_KEY"))
The Lookup Function
The core function validates the phone number format, checks the cache, and calls the Telnyx API on a miss:
def lookup_phone_number(phone_number: str) -> dict:
if not phone_number.startswith("+"):
raise ValueError("Phone number must be in E.164 format (e.g., +15551234567)")
cached_result = get_cached_lookup(phone_number)
if cached_result:
cached_result["from_cache"] = True
return cached_result
response = client.number_lookup.retrieve(phone_number)
lookup_data = {
"phone_number": response.data.phone_number,
"country_code": response.data.country_code,
"carrier": {
"name": response.data.carrier.name if response.data.carrier else None,
"type": response.data.carrier.type if response.data.carrier else None,
},
"line_type": response.data.line_type,
"number_type": response.data.number_type,
"portability": {
"status": response.data.portability.status if response.data.portability else None,
"last_checked_at": response.data.portability.last_checked_at if response.data.portability else None,
},
"from_cache": False,
}
cache_lookup_result(phone_number, lookup_data)
return lookup_data
Caching
The cache is a Python dict with a 24-hour TTL. Each entry stores the lookup result and a timestamp. is_cache_valid() compares the stored timestamp against the TTL, and expired entries are replaced on the next lookup.
This keeps repeated lookups of the same number free — the Telnyx API is only called once per number per 24-hour window.
Error Handling
The Flask endpoints catch Telnyx SDK exceptions and return appropriate HTTP status codes:
-
401 for invalid API keys
-
429 for rate limit hits
-
503 for network errors
-
400 for malformed requests
Test It
Look up a number (POST):
curl -X POST http://localhost:5000/lookup \
-H "Content-Type: application/json" \
-d '{"phone_number": "+15551234567"}'
Look up a number (GET):
curl http://localhost:5000/lookup/+15551234567
Check the cache:
curl http://localhost:5000/cache/stats
Run the same lookup twice — the second response returns "from_cache": true.
Going to Production
This example uses an in-memory cache for simplicity. For production:
-
Shared cache — Replace the in-memory dict with Redis so the cache survives restarts and is shared across instances.
-
Authentication — Add API key validation on your endpoints so the lookup service is not open to the internet.
-
Rate limiting — Protect your endpoints and stay within Telnyx Number Lookup limits.
-
Persistence — Store lookup history in a database if you need an audit trail.
-
Monitoring — Add structured logging and alert on error rates.
Resources
Related Examples