How to filter landlines before sending SMS with the Lookup API

September 06, 2022
Written by
Reviewed by

How to filter landlines and other undeliverable destinations before sending SMS

You successfully created a text message but shortly after received the unfortunate [Twilio Alert] On 1st error each day email. Maybe it was a 30006 - Landline or unreachable carrier or other undelivered message error. If you're lucky, your application handles status callbacks for undelivered messages.

What if there was a way to catch undeliverable messages proactively? This blog post will walk you through using the Lookup API to determine line type, so that you can filter landline numbers before sending an SMS to them (and being charged for that SMS).

Console screenshot showing error 30006

How to determine if you have undelivered messages

Twilio uses status callbacks to track message delivery status with webhooks. Learn more about tracking the delivery status of messages in this support article.

The two most common types of undelivered message errors this blog post will address include:

  1. Error 30006: Landline or unreachable carrier
  2. Error 30003: Unreachable destination handset

Tutorial prerequisites

To follow along with this blog post you'll need:

  1. A free or paid Twilio Account. Sign up or sign in.
  2. A Twilio phone number. Buy a new phone number or use an existing one.
  3. The Twilio Node.js helper library. Follow the instructions to install.

This blog post will use Node.js (download) but you can use the code samples in the documentation to write a similar program in the language of your choice.

Using the Lookup API to detect landline phone numbers

The Lookup API provides various types of phone number information with different data packages. These include information like SIM swap status or call forwarding. This post will use the Line Type Intelligence package which includes carrier and line type information.

First, store your Twilio credentials as environment variables.

From your terminal make the following cURL request to determine the line type of the New York City Hall phone number.

curl -X GET 'https://lookups.twilio.com/v2/PhoneNumbers/+12126399675?Fields=line_type_intelligence' -u $TWILIO_ACCOUNT_SID:$TWILIO_AUTH_TOKEN

Here's the JSON response:

{
  "phone_number": "+12126399675",
  "validation_errors": [],
  "call_forwarding": null,
  "url": "https://lookups.twilio.com/v2/PhoneNumbers/+12126399675",
  "calling_country_code": "1",
  "live_activity": null,
  "sim_swap": null,
  "caller_name": null,
  "national_format": "(212) 639-9675",
  "country_code": "US",
  "line_type_intelligence": {
    "mobile_network_code": null,
    "carrier_name": "Verizon",
    "error_code": null,
    "mobile_country_code": "310",
    "type": "landline"
  },
  "valid": true
}

The phone number line type in the response is "landline". Possible values include mobile, fixedVoip, nonFixedVoip, tollFree, and others. You can filter out any type of phone number, but this code will focus on filtering landlines.

Write a program to filter landlines before sending SMS

While we're using JavaScript here, you can use the code samples in the documentation to write a similar program in Python, Java, PHP, Ruby, C#, Go, or the language of your choice. Create a new file called "filter-landline.js" and open it in your favorite code editor. Start by instantiating the Twilio client. Add the following code to the top of the file:

const accountSid = process.env.TWILIO_ACCOUNT_SID;
const authToken = process.env.TWILIO_AUTH_TOKEN;
const client = require("twilio")(accountSid, authToken);

Create a variable to store your Twilio phone number. You can either store this in another environment variable or hard code it:

const FROM_NUMBER = // your twilio number here

Next, add a function called safeSendSms. The function will take a phone number parameter, check its line type, throw an error if it's a landline, and proceed with sending the message if not.

async function safeSendSms(to) {
 const lookup = await client.lookups.v2
   .phoneNumbers(to)
   .fetch({ fields: "line_type_intelligence" });

 const lineType = lookup.lineTypeIntelligence.type;

 const filterTypes = ["landline"];
 if (filterTypes.includes(lineType)) {
   throw new Error(`${lineType} detected - not sending message.`);
 }

 const message = await client.messages.create({
   body: "Hi there!",
   from: FROM_NUMBER,
   to: to,
 });

 return message.sid;
}

Finally, bring it all together by looping through a list of numbers and attempting to send a message to each of them. Add your personal mobile number to the list to see a non-filtered message:

const numbers = ["+12126399675", "+15017122661"];

numbers.map((num) => {
 safeSendSms(num)
   .then((sid) => console.log(`${num}: sent '${sid}'`))
   .catch((err) => {
     console.log(`${num}: ${err.message}`);
   });
});

Run the program from your terminal with node filter-landline.js. You should see the following output:

$ node filter-landline.js
+12126399675: landline detected - not sending message.
+15017122661: sent 'SMdc50f42d60d7a895f75b3bf23c697fc9'

Cost efficiency of using Lookup before sending an SMS

You only need to make a Lookup request once* compared to unknowingly sending dozens of SMS (marketing message, appointment reminder, support update, etc.) to undeliverable locations. Using Lookup can actually save you money if deployed smartly. Consider this example with 1000 US users: if just 5% of the numbers need to be filtered, you only need to send about 25 messages per user before you'll start saving money with Lookup.

|                       |  No Lookup | With Lookup | Notes                                 | |-----------------------|:----------:|:-----------:|---------------------------------------| |                 Users |    1000    |     1000    |                                       | | % Filtered (estimate) |     0%     |      5%     |                                       | |         Filtered users |      0     |      50     |                                       | |     Messages per user |     25     |      25     |                                       | |           Lookup Cost |     $ -    |     $ 8.00  | Pricing | |        Messaging Cost |  $ 197.50  |   $ 187.63  | Pricing | |            Total Cost |  $ 197.50  |   $ 195.63  |                                       |

*Customers often use Lookup periodically (quarterly, annually, or when you notice a spike in undelivered messages) to clean up their database and update customer information.

If you have questions about whether the cost makes sense for your usage, we're happy to help evaluate - get in touch.

How to use the Lookup API for more phone number information

You could also detect landlines in order to default to voice calls for something like phone verification or 2FA. The Lookup API will also be handy to block the rare pager number or filter VoIP numbers at sign up.

Lookup is also a great way to clean up your database or validate phone number input with our free formatting API. Learn more in our blog post about best practices for phone number validation.