How to Lookup Phone Numbers in Plain PHP With Twilio's Lookup API

July 16, 2021
Written by
Reviewed by

How to Lookup Phone Numbers in Plain PHP With Twilio's Lookup API

Phone numbers are a very common part of user profiles in modern applications. For example, they let users receive an OTP (One Time Password) to log in to an account or receive a recovery code when they can't access their second-factor device, and they let support staff contact a customer when there is suspicious activity on their account.

However, how often do we check that the phone number which users store in their profiles is genuine? Today, I'm going to show you how to do it in plain PHP using Twilio's Lookup APIno framework required!

Tutorial requirements

To follow this tutorial you'll need three things:

Create the project directory and install the required dependencies

Before you can begin writing code, run the commands below to both create a project directory named php-phone-number-lookup and switch into it.

mkdir php-phone-number-lookup
cd php-phone-number-lookup

Next, you need to install three dependencies:

  • Twilio's PHP Helper Library: You need this to interact with Twilio's Lookup API.
  • PHP dotenv: This avoids storing your Twilio credentials in with the PHP code, which is one of the 12 principles of modern application design.
  • CLImate: This isn't strictly necessary. However, it makes writing to the terminal a lot less involved—and more professional in appearance—than using functions such as echo, print, and printf. (Thanks to everyone who gave me suggestions on Twitter about which CLI package to use.)

To install them, run the command below.

composer require \
    league/climate \
    twilio/sdk \
    vlucas/phpdotenv

Retrieve your Twilio credentials

Next, you need to retrieve your Twilio credentials so that you can make authenticated requests to Twilio's Lookup API. You will store these credentials in an external file named .env. Create the file in the root directory of the project, then paste the code below into it.

TWILIO_AUTH_SID="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
TWILIO_AUTH_TOKEN="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

Next, from the Twilio Console's Dashboard, which you can see below, copy your "Account SID" and "Auth Token" and replace the respective placeholder values in .env with them.

Retrieve your Twilio auth token and account SID

Time to write the code!

The entire application will consist of just one file, index.php. Create it in the root directory of your project and then paste the code below into it. Then, let's step through what it does.

<?php

require_once './vendor/autoload.php';

use Twilio\Rest\Client;

$dotenv = Dotenv\Dotenv::createImmutable(__DIR__);
$dotenv->load();

$sid = $_ENV["TWILIO_ACCOUNT_SID"];
$token = $_ENV["TWILIO_AUTH_TOKEN"];
$twilio = new Client($sid, $token);

try {
    $phoneNumber = $client
        ->lookups
        ->v1
        ->phoneNumbers("+18043768640")
        ->fetch(
            [
                "type" => ["caller-name"]
            ]
        );
} catch (\Twilio\Exceptions\TwilioException $e) {
    (new League\CLImate\CLImate)->out('The phone number does not exist.');
    exit;
}

$output = new League\CLImate\CLImate;
$output->green()->bold()->out('Phone Number Details');
$output->table([
    [
        'Country Code' => $phoneNumber->countryCode,
        'National Format' => $phoneNumber->nationalFormat,
        'International Format' => $phoneNumber->phoneNumber,
        'Carrier Name' => $phoneNumber->carrier['name'],
        'Carrier Type' => strtoupper($phoneNumber->carrier['type']),
    ]
]);

It starts how you'd likely expect, by requiring the Composer-generated autoloader and adding the required use statements for the external classes used in the code.

<?php

require_once './vendor/autoload.php';

use Twilio\Rest\Client;

After that, PHP dotenv reads .env and loads the variables stored in it into PHP's $_ENV superglobal. That way, the code can access them using native PHP functionality.

NOTE: We don't have to store .env in the root directory of the project, nor name it .env. However, it's common practice, so I'm not going against that without a good reason.

After that, a new Twilio Client object, named $client, is instantiated with the Twilio Account SID and Auth Token retrieved from the $_ENV superglobal passed to its constructor. For what it's worth, this object provides the ability to interact with any Twilio API, not just the Lookup API.

$dotenv = Dotenv\Dotenv::createImmutable(__DIR__);
$dotenv->load();

$client = new Client(
    $_ENV["TWILIO_ACCOUNT_SID"], 
    $_ENV["TWILIO_AUTH_TOKEN"]
);

Next, a request is made to the Lookup API using the Client's fluent interface. Reading the call in reverse, it fetches the phone number details for "+18043768640" using V1 of the Lookup API.

try {
    $phoneNumber = $client
        ->lookups
        ->v1
        ->phoneNumbers("+18043768640")
        ->fetch([
            "type" => ["caller-name"]
        ]);
} catch (\Twilio\Exceptions\TwilioException $e) {
    (new League\CLImate\CLImate)->out('The phone number does not exist.');
    exit;
}

This meshes nicely with the actual endpoint which the code calls: https://lookups.twilio.com/v1/PhoneNumbers/{PhoneNumber}.

The Lookup API supports several options, most of which are supplied in the array passed to fetch, listed below.

  • addOns: The unique name of an Add-on you would like to use with the request. Add-ons are similar to add-ons or extensions in other applications, such as browsers like Firefox or Google Chrome. They let you retrieve a richer set of data from requests to the API. They're available in the Add-on Catalog in the Twilio Console.
  • addOnsData: Data specific to the add-on you would like to use with the request.
  • countryCode: The ISO country code of the phone number to fetch. This is used to specify the country when the phone number is provided in a national format. If no country is provided, this will default to US.
  • phoneNumber: The phone number to lookup in E.164 format. It consists of a + followed by the country code and subscriber number. This is supplied in the call to the phoneNumbers method.
  • type: The type of information to return. This can be one of two optional values: carrier or caller-name, or null. Carrier information costs $0.005 USD per phone number looked up. Caller name information is currently available only in the United States and costs $0.01 per phone number looked up.

Because the type element's array contains caller-name, if available, the phone number's caller name information will be returned, in addition to the normal phone number details.

The call is wrapped in a try/catch block because, if the phone number is not available, a TwilioException is thrown. If this happens, a message is printed to the terminal informing the user that the number could not be found.

If the request was successful, however, a JSON payload will be returned, which you can see an example of below. This is used by the fetch method to instantiate and return a PhoneNumberInstance object.

{
  "caller_name": null,
  "carrier": {
        "error_code": null,
        "mobile_country_code": "310",
        "mobile_network_code": "456",
        "name": "verizon",
        "type": "mobile"
  },
  "country_code": "US",
  "national_format": "(510) 867-5310",
  "phone_number": "+15108675310",
  "add_ons": null,
  "url": "https://lookups.twilio.com/v1/PhoneNumbers/+15108675310"
}

You can see that it's a somewhat simplistic structure, containing:

  • Caller details, if available
  • Carrier details, if requested
  • The country code
  • The phone number in both national and international formats
  • A list of any add-ons that were used (in this case, none)
  • The URI that was hit to make the request. I find this handy when debugging requests
$output = new League\CLImate\CLImate;
$output->green()->bold()->out('Phone Number Details');
$output->table([
    [
        'Country Code' => $phoneNumber->countryCode,
        'National Format' => $phoneNumber->nationalFormat,
        'International Format' => $phoneNumber->phoneNumber,
        'Carrier Name' => $phoneNumber->carrier['name'],
        'Carrier Type' => strtoupper($phoneNumber->carrier['type']),
    ]
]);

As no exception's been thrown, then it's time to print out the retrieved information. The code does this using CLImate. If you're not familiar with the package, perhaps hearing about it for the first time, here's how it's described:

CLImate allows you to easily output colored text, special formatting, and more. It makes output to the terminal clearer and debugging a lot simpler.

As I said at the top of the article, the package isn't strictly necessary. The phone number information could be printed out using any one of PHP's string methods, such as echo or print. However, I wanted to give the terminal output a bit of extra polish and love.

The PhoneNumber object returned from fetch uses PHP's magic __get method to make each of the JSON payload's top-level elements accessible as, effectively, public properties.

The code uses that functionality to print out the country code, and the phone number in both national and international formats, and some of the carrier's details.

Test the code

To test the code, run the command php index.php in your terminal. You should see output in your terminal similar to the screenshot below.

The phone number information rendered to the terminal in a nice looking table.

That's how to look up a phone number in plain PHP with Twilio

When you're not using a PHP framework, there's not a whole lot to it.

  • Install the required dependencies
  • Make a request for the phone number details using Twilio's PHP Helper Library, passing the required options.

Have a good read through the helper library and the Lookup API's documentation and experiment with the code to see what else you can do.

I'm excited to hear about what you build!

Matthew Setter is a PHP Editor in the Twilio Voices team and a polyglot developer. He’s also the author of Mezzio Essentials and Docker Essentials. When he’s not writing PHP code, he’s editing great PHP articles here at Twilio. You can find him at msetter@twilio.com; he's also settermjd on Twitter and GitHub.