DEV Community

Jack
Jack

Posted on

How to Look Up Verified Phone Numbers with TypeScript

If you're building outbound sales tooling, a CRM integration, or any pipeline that needs real mobile phone numbers, here's how to do it in TypeScript with a single API call.

We'll use the Million Phones API to look up a verified phone number from a LinkedIn profile handle.

Setup

Initialize a project and install dependencies:

mkdir phone-lookup && cd phone-lookup
npm init -y
npm install tsx
Enter fullscreen mode Exit fullscreen mode

No extra HTTP libraries needed — we'll use the native fetch available in Node 18+.

The Lookup Function

Create lookup.ts:

interface PhoneResponse {
  phone_numbers: string[];
}

async function lookupPhone(socialUrl: string): Promise<string[] | null> {
  const apiKey = process.env.MILLIONPHONES_API_KEY;

  if (!apiKey) {
    throw new Error("Missing MILLIONPHONES_API_KEY environment variable");
  }

  const url = `https://millionphones.com/v1/phone?social_url=${encodeURIComponent(socialUrl)}`;

  const response = await fetch(url, {
    headers: {
      "x-api-key": apiKey,
    },
  });

  if (!response.ok) {
    console.error(`Lookup failed: ${response.status} ${response.statusText}`);
    return null;
  }

  const data = (await response.json()) as PhoneResponse;
  return data.phone_numbers?.length ? data.phone_numbers : null;
}
Enter fullscreen mode Exit fullscreen mode

Using It

Add a simple runner at the bottom of the same file:

async function main() {
  const handle = process.argv[2];

  if (!handle) {
    console.log("Usage: npx tsx lookup.ts <linkedin-handle>");
    process.exit(1);
  }

  console.log(`Looking up: ${handle}`);
  const phones = await lookupPhone(handle);

  if (phones) {
    phones.forEach((phone) => console.log(`✅ ${phone}`));
  } else {
    console.log("❌ No verified number found");
  }
}

main();
Enter fullscreen mode Exit fullscreen mode

Run it:

export MILLIONPHONES_API_KEY=your_key_here
npx tsx lookup.ts williamhgates
Enter fullscreen mode Exit fullscreen mode

Output:

Looking up: williamhgates
✅ +1-833-457-0192
Enter fullscreen mode Exit fullscreen mode

Batch Lookups

Most real pipelines need to process a list, not a single handle. Here's a version that reads from a file and respects rate limits:

import { readFileSync, writeFileSync } from "fs";

async function batchLookup(inputFile: string, outputFile: string) {
  const handles = readFileSync(inputFile, "utf-8")
    .split("\n")
    .map((line) => line.trim())
    .filter(Boolean);

  console.log(`Processing ${handles.length} handles...`);

  const results: { handle: string; phones: string[] | null }[] = [];

  for (const handle of handles) {
    const phones = await lookupPhone(handle);
    results.push({ handle, phones });

    // Simple rate limiting — 200ms between requests
    await new Promise((resolve) => setTimeout(resolve, 200));
  }

  writeFileSync(outputFile, JSON.stringify(results, null, 2));
  console.log(`Results written to ${outputFile}`);
}
Enter fullscreen mode Exit fullscreen mode

Create a handles.txt with one LinkedIn handle per line, then:

npx tsx lookup.ts --batch handles.txt results.json
Enter fullscreen mode Exit fullscreen mode

Error Handling in Production

For anything beyond a quick script, add retry logic:

async function lookupWithRetry(
  socialUrl: string,
  retries = 3
): Promise<string[] | null> {
  for (let attempt = 1; attempt <= retries; attempt++) {
    try {
      const result = await lookupPhone(socialUrl);
      if (result) return result;
    } catch (err) {
      console.error(`Attempt ${attempt} failed:`, err);
      if (attempt < retries) {
        await new Promise((r) => setTimeout(r, 1000 * attempt));
      }
    }
  }
  return null;
}
Enter fullscreen mode Exit fullscreen mode

What's Next

This is the building block for a composable GTM stack — no spreadsheet UI, no credit-based pricing, just a script that does what you need. Plug this into a Supabase database, sync results to Google Sheets for your SDRs, and you've replaced most of what tools like Clay charge $150+/month for.

Full API docs at millionphones.com/docs. Free tier includes 50 credits to test with.


Built by Jack — building verified phone data for sales teams at Million Phones.

Top comments (0)