Experimenting with Twilio

I've always wanted an excuse to try working with Twilio for programmatic messaging and SMS.  This week I got my chance, and wanted to share some of what I learned.

One of the many side projects I maintain is an IoT system I've been cobbling together for my family and neighbours.  It's a mix of native code, node.js, and React that provides a user-friendly way to interact with local sensor data (video, images, XML, and JSON sources via an event stream).

During the stay-at-home pandemic, we've needed to use this data in new ways, and a recent request came in for mobile notifications based on certain sensor conditions.  My first thought was to use Web Push Notifications via a Service Worker in my web app.  However, I have to support Windows, macOS, Chrome (desktop and mobile), and Safari on iOS (iPad and iPhone).  Essentially, my users represent just about every platform and form-factor you can imagine, and unfortunately Safari doesn't support the Push API, so I gave up on that approach.

This left me thinking about SMS.  I knew that services like Twilio allow programmatic SMS, but I'd never tried using them before.  It turned out to be quite simple, taking about 30 minutes to add to my node.js web server.  Here's what's involved.

First, I had to understand which of the various "SMS," "MessageService," and "Notification" APIs I needed.  Twilio's docs are excellent, but I found it a bit confusing to land on exactly what I needed at first.

You create an account to start, and Twilo gives you some credits to play with upfront.  I didn't end up using these, since I didn't want to have extra text added to my messages.  I found it odd when I "upgraded" my account to instantly be charged $20 on my credit card vs. simply opening an account that they could charge using the pay-as-you-go model I'm using.

Next, I had to "buy" a phone number.  It's really "rent" a phone number monthly.  You can specify which features you need for the phone number, and pick a country and local area.  After some searching and figuring out their UI, I got a number in Toronto.  The phone numbers cost $1 per month.

Now I used my account console to get the Account SID and Auth Token I'd need to write code against their API.  Because I'm working in node.js, I used their open source node helper library:

npm install twilio

The code to send an SMS looks like this:

const Twilio = require('twilio');
const twilio = new Twilio(accountSID, authToken);

await twilio.messages.create({
  body: 'This is your text message content',
  to: '+15551235555',   // the number to text
  from: '+15553215555'  // the Twilio number you bought
});

The phone numbers you use have to be in the +15551234567 format.  I needed to text multiple numbers at the same time for various events, so I do something like:

await Promise.all(
  [+15551234567', ...].map((number) =>
    twilio.messages.create({
      body: '...',
      to: number,
      from: '...',
    })
  )
);

That's basically it.  It's amazingly simple.

Now let's talk about cost.  I was surprised at how expensive it is.  I'm sending SMS messages in Canada, and my real cost per text looks like this:

  • Outbound SMS: $0.0085 (Carrier Fee) + $0.0075 (Twilio) = $0.016
  • Inbound SMS: $0.0085 (Carrier Fee) + $0.075 (Twilio) = $0.0835

So it basically costs 1.6 cents to send, and 8 cents to receive a text with Twilio in Canada.  That's more than double what Twilio tells you, unless you really squint at the fine print--surprise! Canadian telecom carriers want their cut of the action too.

It might not sound like a lot per text, but it adds up as you start scaling this.  I noticed today that Twitter has disabled it's SMS tweet feature, I assume due to cost, despite their deals with carriers.  In my case, based on historical sensor data, it would have cost me $13.84 to send these notifications last week.  So far I've spent a little over $4 since I started running this new feature.

Needless to say, I'm pretty motivated to make sure my code behaves and throttle and debounce excessive events.  It's too bad Twilio's clients don't bake this in automatically, but they do have some nice error and event reporting tools that let you keep track of things in real time.

All in all, I'm pleased with the service.  I suspect when the pandemic is over I'll probably end the experiment, and it will be interesting to see what the final bill is.  Until then, it works perfectly.  I think this is called "You get what you pay for."