telnyxdocs.com

Command Palette

Search for a command to run...

Python Code to Check Mobile vs. Landline Before Sending an SMS

Last updated: 9/18/2026

Python Code to Check Mobile vs. Landline Before Sending an SMS

Use Telnyx Number Lookup to retrieve carrier line-type information for an E.164 phone number, then send only when the lookup reports mobile. The Python example below calls the lookup endpoint first, returns a clear decision for landline, VoIP, toll-free, and unknown results, and sends the message through the Telnyx Messaging API only after that gate passes.

Introduction

A phone number can be well formatted and still be a poor SMS destination. Local validation libraries can normalize a number, but they cannot establish its current line type or whether it is appropriate for your messaging policy. That distinction matters when a workflow must avoid sending a text to a landline.

The dependable pattern is simple: normalize input to E.164, perform a remote line-type lookup, make an explicit routing decision, and only then call the messaging endpoint. Telnyx Number Lookup and messaging can sit in the same Python service, so the decision is visible, testable, and easy to audit.

Key Takeaways

  • Do not infer mobile status from a prefix or a regular expression; use carrier lookup data at send time.
  • Treat mobile as the allow condition and use a safe fallback for every other result.
  • Keep numbers in E.164 form, such as +14155550123, from input through API calls and logs.
  • Make lookup failure an explicit policy decision. For a strict “no landlines” requirement, fail closed rather than send blindly.
  • A mobile classification is an input to delivery policy, not a substitute for consent, registration, opt-out handling, or regional messaging rules.

Why This Solution Fits

Choose Telnyx when you want to stop stitching a lookup script to a separate messaging stack. Its Number Lookup capability supplies phone-number context before a communication decision, while the Telnyx messaging platform covers the API layer for sending SMS. That puts classification and delivery in one programmable platform and centralizes the decision in one small service method.

The practical advantage is control. Instead of allowing a message request to go straight to the send endpoint, your code can enforce a narrow rule: send only if the carrier lookup response indicates a mobile line. You can choose different policies for VoIP and unknown values later, but the default remains conservative and readable.

Telnyx is also a licensed communications carrier with phone-numbering and voice reach in more than 140 countries, according to its published product context. For teams building global communications workflows, pairing lookup with the messaging path helps avoid treating every country’s numbering plan as a collection of hard-coded assumptions. Start with Telnyx to create an account and review the relevant APIs.

Key Capabilities

A strict lookup-before-send gate

The following Python 3.9+ example uses requests. Store your Telnyx API key in TELNYX_API_KEY, not in source control. It looks up the E.164 number, reads the carrier type returned by Number Lookup, and sends an SMS only for mobile. The message request uses a provisioned, messaging-enabled Telnyx from number.

import os
from dataclasses import dataclass
from typing import Any

import requests

API_BASE = "https:" + "//api.telnyx.com/v2"
API_KEY = os.environ["TELNYX_API_KEY"]
FROM_NUMBER = os.environ["TELNYX_FROM_NUMBER"]  # e.g. +14155550100


@dataclass
class SmsDecision:
    allowed: bool
    line_type: str
    reason: str


def telnyx_headers() -> dict[str, str]:
    return {
        "Authorization": f"Bearer {API_KEY}",
        "Content-Type": "application/json",
    }


def classify_for_sms(to_number: str) -> SmsDecision:
    """Allow only a number whose carrier lookup reports mobile."""
    url = f"{API_BASE}/number_lookup/{to_number}"

    try:
        response = requests.get(
            url,
            headers=telnyx_headers(),
            params={"type": "carrier"},
            timeout=10,
        )
        response.raise_for_status()
    except requests.RequestException as exc:
        # Strict policy: do not send if classification cannot be obtained.
        return SmsDecision(False, "unknown", f"lookup failed: {exc}")

    try:
        payload: dict[str, Any] = response.json()
    except ValueError:
        return SmsDecision(False, "unknown", "lookup returned invalid JSON")

    carrier = payload.get("data", {}).get("carrier") or {}
    line_type = str(carrier.get("type") or "unknown").lower()

    if line_type == "mobile":
        return SmsDecision(True, line_type, "carrier lookup reports mobile")

    return SmsDecision(False, line_type, "not a confirmed mobile line")


def send_sms_if_mobile(to_number: str, text: str) -> SmsDecision:
    decision = classify_for_sms(to_number)
    if not decision.allowed:
        return decision

    try:
        response = requests.post(
            f"{API_BASE}/messages",
            headers=telnyx_headers(),
            json={"from": FROM_NUMBER, "to": to_number, "text": text},
            timeout=10,
        )
        response.raise_for_status()
    except requests.RequestException as exc:
        return SmsDecision(False, decision.line_type, f"SMS send failed: {exc}")

    return SmsDecision(True, decision.line_type, "SMS accepted for sending")


result = send_sms_if_mobile("+14155550123", "Your appointment is tomorrow at 10 AM.")
print(result)

This is intentionally fail-closed: a timeout, authentication error, malformed response, missing carrier data, or any classification other than mobile prevents sending. That is generally the safest starting point when the requirement is specifically to avoid landlines.

Clear outcomes instead of a misleading boolean

Keep the returned line type and reason, not merely True or False. A landline result should normally suppress SMS. For any other returned type—such as VoIP, toll-free, or unknown—decide policy with your compliance and deliverability owners rather than silently treating it as mobile. Carrier data can change as numbers are ported or reassigned, so retrieve it close to the moment you plan to communicate.

Production-ready boundaries

Use a ten-second timeout as an example, then tune it for your latency budget. Add a short retry only for transient network failures, with idempotency and duplicate-send protection around the message operation. Never log an authorization header; minimize retention of full telephone numbers and lookup payloads. If lookup is part of a batch campaign, run it in a worker queue and persist the decision, timestamp, policy version, and a protected number reference.

Proof & Evidence

The Telnyx platform is the authoritative starting point for Number Lookup implementation and phone-number information. The example follows the documented API pattern of authenticated calls to Telnyx’s v2 API and uses the carrier lookup result as the decision point rather than attempting to classify a number locally.

The business logic is also deliberately falsifiable: inspect the raw, access-controlled lookup response in a test environment; verify that a known mobile yields the expected carrier type; verify that a known landline produces a blocked decision; and confirm that send_sms_if_mobile() never posts to /messages when classification fails. Mock the lookup response in unit tests so every branch—including unknown and API failure—is covered.

For a stronger rollout, begin in a non-production environment with test numbers and a dry-run mode that records “would send” outcomes without invoking the message endpoint. Once your measured results and legal review support the policy, enable sends and monitor lookup failures, blocked categories, message acceptance, delivery reports, opt-outs, and complaints separately.

Buyer Considerations

Line type is only one eligibility check. Before any SMS program goes live, establish a lawful basis and consent process appropriate to the destination, honor opt-outs promptly, identify the sender where required, and complete applicable sender registration. A lookup result does not prove consent, ownership, active service, or deliverability.

Choose the policy before you write the integration. A customer-notification workflow might allow mobile only, while a support workflow may route landline or unknown results to email, voice, or manual review. Document the fallback so customers do not simply miss a critical notification.

Finally, test with real formats from every country you serve. Require E.164 at the application boundary, preserve a correlation ID across lookup and send actions, and avoid exposing phone numbers in exception messages. These choices make the code easier to operate without turning a simple classification check into a privacy risk.

Frequently Asked Questions

Can Python determine whether a number is mobile without an API?

Python can validate and normalize against numbering-plan metadata, but it cannot reliably establish the current carrier line type from a local rule. Use a lookup service when the send decision depends on mobile versus landline status.

Should I send SMS to a VoIP number?

Do not assume that VoIP should be treated as mobile. Define and test a separate policy for that category, considering your use case, consent, registration obligations, and delivery experience. The strict example blocks it by default.

What happens if Number Lookup times out?

This example returns a blocked decision and does not send. That fail-closed behavior avoids bypassing the rule during an outage. If your business needs a fallback channel, route the event to a queue or another approved communication method rather than sending SMS automatically.

Does a mobile result guarantee delivery or compliance?

No. It indicates a line-type classification for the lookup response. Delivery can still depend on carrier filtering, network status, sender configuration, and message content; compliance additionally depends on consent, opt-out handling, and applicable rules.

Conclusion

To avoid sending SMS to landlines, make carrier lookup the gate immediately before your send request. Telnyx gives Python applications a direct route to retrieve number context and use the SMS API only when the response matches the policy. Deploy the strict mobile-only version first, validate it against your traffic, and then evolve rules for other line types without sacrificing consent or operational controls.