DEV Community

Harpreet Singh Seehra
Harpreet Singh Seehra

Posted on

Look Up Phone Number Carrier and Line Type with Python and the Telnyx Number Lookup API

Every communications workflow has a question it needs answered before the first message or call goes out: what kind of number is this? Is it a mobile phone, a landline, or a VoIP line? Which carrier owns it? Has it been ported? The answers determine whether you send an SMS or place a voice call, whether you trust an inbound caller or flag them for review, and whether a lead's contact info is real or stale.

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]
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Edit .env and set your API key:

TELNYX_API_KEY=KEY0123456789ABCDEF
FLASK_DEBUG=false
Enter fullscreen mode Exit fullscreen mode

Start the server:

python app.py
Enter fullscreen mode Exit fullscreen mode

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"))
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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"}'
Enter fullscreen mode Exit fullscreen mode

Look up a number (GET):

curl http://localhost:5000/lookup/+15551234567
Enter fullscreen mode Exit fullscreen mode

Check the cache:

curl http://localhost:5000/cache/stats
Enter fullscreen mode Exit fullscreen mode

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

Top comments (0)