By Raj

Estimated reading time: 14 minutes

Send automated SMS from Google Sheets (Twilio): Production Apps Script Guide

If you need to send automated SMS from Google Sheets (Twilio) for appointment reminders, delivery updates, or internal ops alerts, you are in the right place. This is a Twilio Apps Script tutorial written for US and Canadian teams who want a dependable Google Sheets SMS integration without signing up for yet another monthly SMS marketing platform. You will learn how to automate text messages from a spreadsheet using the Apps Script Twilio API pattern your engineering lead can approve: secrets in Script Properties, E.164 validation, per-row status, duplicate protection, and batch-friendly execution.

Why Twilio + Sheets beats another SMS SaaS

Most bundled SMS products optimize for marketing lists: per-seat pricing, contact caps, and another dashboard your team has to monitor. Twilio is different: you pay for usage, you keep carrier-grade delivery tooling, and you decide how messages are triggered. Pairing Twilio with Google Sheets puts the workflow where non-developers already work—while still letting developers enforce guardrails in code.

ApproachCost modelBest for
SMS marketing suitesMonthly platform fee + list tiersCampaigns with built-in templates
Twilio + Apps ScriptPer message (and your build time once)Transactional ops, reminders, custom logic

[!NOTE]

This guide intentionally goes beyond a 10-line demo. Production SMS means validation, idempotency, and clear row-level status your support team can read.

Prerequisites: Twilio account, From number, Apps Script

Step-by-step: Twilio

  1. Create a Twilio account and open the Console.
  2. Copy Account SID and Auth Token.
  3. Provision an SMS-capable From number (trial accounts can only SMS verified destinations until upgraded).
  4. Confirm your use case is compliant (especially marketing vs transactional).

Step-by-step: Apps Script

  1. Open your Sheet → Extensions → Apps Script.
  2. Add Script properties: TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN, TWILIO_FROM_NUMBER (E.164, e.g. +15551234567).
  3. Optional: SMS_MAX_PER_RUN (default 40), SMS_DEFAULT_REGION (US or CA), TWILIO_STATUS_CALLBACK_URL after you deploy a Web App for callbacks.
  4. Paste the full code from the downloadable .gs file (link below), save, authorize external requests on first run.

Security: storing credentials in PropertiesService keeps secrets out of Git, out of shared Sheet tabs, and out of screenshots your team shares in Slack. Rotate the Auth Token in Twilio if it is ever exposed; update the script property only—no code edit required.

The golden spreadsheet setup

Name row 1 headers exactly so playbooks, support macros, and future engineers stay aligned. The sample script expects:

ColumnHeaderPurpose
ANameOptional; use {{name}} in Message
BPhoneNormalized to E.164 before send
CMessageSMS body (watch encoding/segment length)
DStatusSent, Failed, or Skipped
EMessageSidTwilio SID for support + StatusCallback matching
FLastErrorValidation or API error text (truncated)

Duplicate protection: if someone runs Send pending SMS rows twice, rows already marked Sent are skipped so customers do not get duplicate texts.

Code implementation

Download the full, commented implementation (recommended copy source):

Section 1: Core send using UrlFetchApp (Twilio REST API)

Twilio's Messages API expects application/x-www-form-urlencoded POST data and HTTP Basic authentication. The production file parses JSON on success and maps failures into column F while setting column D to Failed.

JavaScript (Apps Script) — excerpt

function sendSmsCore_(accountSid, authToken, fromNumber, toE164, body) {
  var url =
    "https://api.twilio.com/2010-04-01/Accounts/" +
    encodeURIComponent(accountSid) +
    "/Messages.json";

  var payload =
    "To=" + encodeURIComponent(toE164) +
    "&From=" + encodeURIComponent(fromNumber) +
    "&Body=" + encodeURIComponent(body);

  // Optional: async delivery updates (deploy Web App; set TWILIO_STATUS_CALLBACK_URL)
  var callbackUrl = PropertiesService.getScriptProperties().getProperty("TWILIO_STATUS_CALLBACK_URL");
  if (callbackUrl) payload += "&StatusCallback=" + encodeURIComponent(callbackUrl.trim());

  var response = UrlFetchApp.fetch(url, {
    method: "post",
    contentType: "application/x-www-form-urlencoded",
    headers: { Authorization: "Basic " + Utilities.base64Encode(accountSid + ":" + authToken) },
    payload: payload,
    muteHttpExceptions: true,
  });
  // Parse JSON on 2xx; else write error body to LastError column (see full .gs file)
  return { code: response.getResponseCode(), text: response.getContentText() };
}

Section 2: onOpen — custom menu “🚀 Send SMS”

A custom menu trains operators to use the right entry point. The menu exposes batch send and a dry-run validator that only checks E.164 formatting without spending message credits.

JavaScript (Apps Script)

function onOpen() {
  SpreadsheetApp.getUi()
    .createMenu("🚀 Send SMS")
    .addItem("Send pending SMS rows", "sendPendingSmsRows")
    .addItem("Validate phone numbers (dry run)", "validatePhoneNumbersDryRun")
    .addToUi();
}

Status callback logging (optional but powerful)

The REST response tells you Twilio accepted the message. Delivery outcomes (delivered vs failed) can arrive asynchronously. The full .gs supports StatusCallback when you set TWILIO_STATUS_CALLBACK_URL to a deployed Apps Script Web App URL and implement doPost to match MessageSid in column E and update column D.

[!TIP]

For public Web Apps, review Google's execution identity and access settings. Prefer “Execute as me” with least-privilege Twilio keys for internal automations.

Batch processing and the six-minute limit

Apps Script executions are bounded by time quotas. The script caps how many rows it processes per run (SMS_MAX_PER_RUN) and uses a short sleep between sends to reduce burst failures. For very large lists, use a time-driven installable trigger to run every few minutes until all pending rows clear—or split recipients across tabs.

For related patterns on triggers and quotas, see Google Apps Script trigger troubleshooting and execution time limits.

Advanced troubleshooting (US & Canada)

10DLC and US carrier compliance (short overview)

If you send high-volume or marketing-class SMS to US mobile subscribers from long codes, you may need 10DLC brand and campaign registration through your provider workflow (Twilio documents this in their messaging onboarding). Errors often show up as specific Twilio error codes in the API response—your sheet's LastError column preserves them for debugging.

Canada-specific considerations

Canadian destination rules and filtering can differ from the US. Always log Twilio response bodies for failed sends and verify the destination number format (+1 NANP for many Canadian mobiles).

Common API failures

  • 21608 / trial restrictions: trial accounts must verify destination numbers.
  • 21211 invalid To: fix E.164 formatting—use the dry-run menu item first.
  • Rate limits: lower SMS_MAX_PER_RUN and add more frequent scheduled runs.

FAQ

Can I send bulk SMS from Google Sheets with Twilio?

Yes, in batches with strict row status tracking. Follow consent and STOP rules for marketing messages. Transactional reminders (appointments, pickups) are a common compliant pattern when properly worded.

How much does SMS cost per message in the US?

Twilio bills per segment; pricing depends on number type and destination. Check Twilio’s current US and Canada SMS pricing. You avoid a separate SMS SaaS subscription but still pay carrier costs.

Can I receive replies?

Yes—configure inbound webhooks on your Twilio number to post into an Apps Script Web App and append rows to a sheet. Validate Twilio request signatures for production security.

Need a custom integration?

If you want inbound reply routing, CRM sync, calendar-aware reminders, or hardened security reviews for regulated teams, the App Script Expert team can design and ship it. Start on the contact page with your volume estimates and sample sheet structure.

Contact App Script Expert

About the author

Raj builds Google Workspace automations and API integrations for operations-heavy businesses across the US and Canada.

More about Raj