How to Look Up a Phone Number Carrier in Node.js

Most developer tutorials show you how to get a carrier name back from a phone number. That's useful, but it's half the picture. If you're routing SMS campaigns, filtering TCPA-exempt leads, or auditing traffic quality, the question your system actually needs to answer is: which messaging aggregator is handling SMS delivery for this number? This guide shows you how to call the VeriRouteIntel API from Node.js in both JavaScript and TypeScript, returning carrier, messaging provider, line type, and LRN in a single request at $0.0009/lookup.

Key Takeaways

  • VeriRouteIntel returns carrier and messaging provider (Twilio, Bandwidth, Sinch, etc.) in a single API call
  • Node.js 18+ ships native fetch — no additional packages needed for the HTTP request
  • TypeScript interfaces for the full response are provided; copy-paste ready
  • The messaging.provider field is what SMS routing tutorials skip — it's the field that determines deliverability
  • Bulk lookup (up to 1,000 numbers per request) is supported for pre-send list validation

Prerequisites

  • Node.js 18+ (native fetch is available without any packages)
  • A free VeriRouteIntel API key — get yours here (10 free lookups included, no credit card)

Step 1: Get Your API Key

Sign up at verirouteintel.com/signup. After confirming your email, your API key appears on the dashboard under API Keys. Copy it — you'll pass it as a Bearer token in every request.

Store it as an environment variable rather than hardcoding it:

export VRI_API_KEY="your_key_here"

Or add it to a .env file if you're using dotenv.

Step 2: Make Your First Lookup (JavaScript)

No npm packages needed for the request itself. Node.js 18+ ships fetch natively.

// carrier-lookup.js

const API_KEY = process.env.VRI_API_KEY;
const BASE_URL = 'https://verirouteintel.com/api/v1';

async function lookupCarrier(phoneNumber) {
  const response = await fetch(`${BASE_URL}/lrn`, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${API_KEY}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      phone_number: phoneNumber,
      include_enhanced_lrn: true,
      messaging_lookup: true,
    }),
  });

  if (!response.ok) {
    const error = await response.json();
    throw new Error(`Lookup failed [${response.status}]: ${error.error || response.statusText}`);
  }

  return response.json();
}

// Usage
(async () => {
  const result = await lookupCarrier('14155550100');
  console.log(result);
})();

Run it:

VRI_API_KEY=your_key_here node carrier-lookup.js

Step 3: TypeScript Version

The same function with full type coverage:

// carrier-lookup.ts

interface EnhancedLRN {
  carrier: string;
  carrier_type: 'mobile' | 'landline' | 'voip' | 'unknown';
  city: string;
  zip_code: string;
}

interface MessagingInfo {
  provider: string | null;
  enabled: boolean;
  country: string;
  country_code: string;
  reference_id: string;
}

interface CarrierLookupResult {
  phone_number: string;
  lrn: string;
  lrn_activated_at: string | null;
  enhanced_lrn: EnhancedLRN;
  messaging: MessagingInfo;
}

const API_KEY = process.env.VRI_API_KEY!;
const BASE_URL = 'https://verirouteintel.com/api/v1';

async function lookupCarrier(phoneNumber: string): Promise<CarrierLookupResult> {
  const response = await fetch(`${BASE_URL}/lrn`, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${API_KEY}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      phone_number: phoneNumber,
      include_enhanced_lrn: true,
      messaging_lookup: true,
    }),
  });

  if (!response.ok) {
    const error = await response.json();
    throw new Error(`Lookup failed [${response.status}]: ${error.error || response.statusText}`);
  }

  return response.json() as Promise<CarrierLookupResult>;
}

export { lookupCarrier, CarrierLookupResult, MessagingInfo, EnhancedLRN };

Step 4: Parse the Response

A successful lookup returns:

{
  "phone_number": "14155550100",
  "lrn": "14153000000",
  "lrn_activated_at": "2018-03-15T00:00:00Z",
  "enhanced_lrn": {
    "carrier": "T-Mobile USA",
    "carrier_type": "mobile",
    "city": "San Francisco",
    "zip_code": "94102"
  },
  "messaging": {
    "provider": "Twilio",
    "enabled": true,
    "country": "United States",
    "country_code": "US",
    "reference_id": "MSG-ref-abc123"
  }
}
Field What it tells you
lrn Line Routing Number — reflects number porting
lrn_activated_at When this LRN was last activated; a recent date signals a recent port
enhanced_lrn.carrier The voice network: AT&T, Verizon, T-Mobile, or regional MVNO
enhanced_lrn.carrier_type mobile, landline, or voip — essential for TCPA scoping
messaging.provider The SMS aggregator: Twilio, Bandwidth, Sinch, Telnyx, Vonage, etc.
messaging.enabled Whether the number can receive SMS at all

Step 5: Error Handling

The API uses standard HTTP status codes. Here's a production-ready error handler:

async function lookupCarrierSafe(phoneNumber) {
  try {
    const response = await fetch(`${BASE_URL}/lrn`, {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${API_KEY}`,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        phone_number: phoneNumber,
        include_enhanced_lrn: true,
        messaging_lookup: true,
      }),
    });

    const data = await response.json();

    switch (response.status) {
      case 200:
        return { success: true, data };
      case 400:
        return { success: false, error: 'Invalid phone number format', code: 'INVALID_NUMBER' };
      case 401:
        return { success: false, error: 'Invalid or missing API key', code: 'AUTH_ERROR' };
      case 402:
        return { success: false, error: 'Insufficient account balance', code: 'INSUFFICIENT_BALANCE' };
      case 429:
        return { success: false, error: 'Rate limit exceeded — retry after delay', code: 'RATE_LIMITED' };
      default:
        return { success: false, error: data.error || 'Unknown error', code: 'API_ERROR' };
    }
  } catch (err) {
    return { success: false, error: 'Network error', code: 'NETWORK_ERROR' };
  }
}

Retry strategy: retry on 429 with exponential backoff. Do not retry on 400 (bad input) or 402 (account balance) — those require action before retrying.

Bulk Lookups for High-Volume Use Cases

For campaign pre-flight, lead list validation, or contact center hygiene, use the /api/v1/lrn/bulk endpoint. It accepts up to 1,000 numbers per request, deduplicates automatically, and bills only for unique numbers.

const response = await fetch(`${BASE_URL}/lrn/bulk`, {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    phone_numbers: ['14155550100', '12125550199', '13105550177'],
    include_messaging: true,
  }),
});

const { results, summary, billing } = await response.json();
// summary.duplicates_removed shows how many numbers were deduplicated
// billing.actual_cost shows the final charge

Copy the code — free API key. VeriRouteIntel includes 10 free lookups on signup. No credit card required.

Get your free API key

Frequently Asked Questions

What's the difference between a carrier and a messaging provider?

The carrier is the voice network — AT&T, Verizon, T-Mobile, or a regional MVNO. The messaging provider is the SMS aggregator sitting between the carrier and the sender: Twilio, Bandwidth, Sinch, Telnyx, Vonage, and others. A Verizon subscriber can have their SMS traffic routed through Bandwidth. Most carrier lookup APIs return only the voice carrier; VeriRouteIntel returns both in a single call.

Does this work for ported numbers?

Yes. The API uses LRN (Line Routing Number) data, which reflects the current routing state of a number including porting events. The lrn_activated_at field shows when the current LRN was activated — a recent date indicates a recent port.

What phone number formats does the API accept?

The API accepts NANP (North American Numbering Plan) numbers in any common format: 10-digit (4155550100), E.164 (+14155550100), or punctuated (415-555-0100). The API normalizes the number before lookup.

What's the rate limit on the free tier?

Free accounts include 10 free lookups and are subject to standard API rate limits. For high-volume throughput requirements, see the pricing page for plan options.