If your leads live in a Google Sheet, you already know the annoying part isn't sending one text. It's sending the fiftieth one without opening your phone fifty times, and doing it from a list that's in a spreadsheet, not a CRM you pay for.
The obvious next step, a Twilio add-on for Sheets, runs into the same wall every other Twilio product does: it's A2P messaging, which means brand registration, campaign registration, and a content review before your first message goes out. If you're in real estate wholesaling, lending, credit repair, or cannabis retail, that review is often where the process stops.
There's a simpler wiring diagram: a phone, an API key, and one function in Apps Script. Here's how it actually goes together, and where it fits (and doesn't) if you're sending real volume.
Why the Twilio-for-Sheets route hits the same wall
Every major SMS aggregator, Twilio, MessageBird, Plivo, Bandwidth, moves traffic through A2P 10DLC, the U.S. carrier system for vetting business text messages sent from standard 10-digit numbers. To send anything through that pipeline you register a Brand with The Campaign Registry, register a Campaign describing your use case and industry, and wait for carrier approval. CTIA's Messaging Principles and Best Practices, the industry document carriers point to when they reject or throttle a campaign, spells out the content and consent standards that vetting is checking against.
A Google Sheets add-on that sends SMS through Twilio, MSG91, or a similar aggregator is still A2P traffic underneath the spreadsheet interface. It inherits the same registration step and the same content categories carriers won't approve, including the SHAFT-C list (sex, hate, alcohol, firearms, tobacco, cannabis) plus verticals like payday lending, debt collection, and multi-level marketing that carriers treat as high-risk regardless of state legality. A nicer spreadsheet UI doesn't change which pipe the message travels through.
There's also a DIY path that skips aggregators entirely: build your own Android app with a tool like MIT App Inventor, have it poll a Sheet-backed API, and send through your phone's SIM. It works, and it proves the underlying idea (your own phone, sending as itself, isn't A2P traffic at all) but it means building and maintaining an app, and you get no inbox, no reply handling, and no record of what actually sent.
What you actually need instead
Android Texter turns an Android phone you already own into the sending endpoint, with a dashboard and a REST API in front of it instead of a hand-built app. Because the message leaves through the phone's own carrier line, it's person-to-person SMS, the same category as texting a friend, not A2P traffic. That's a real architectural difference, not a workaround: there's no rule against sending a text from your own phone, so there's nothing to register or get approved.
The part that matters for a spreadsheet workflow is the REST API: POST /api/v1/messages/send queues an outbound message through your paired device. Every request carries a per-user API key (prefixed atx_) in an X-API-Key header, generated from /dashboard/settings/api-keys. The full spec is at /api/openapi.json, with an interactive Swagger UI at /api/swagger if you want to test calls before you script anything.
That's a small enough surface that a single Apps Script function can drive it directly, no Zapier subscription or middleware account required.
Step by step: wiring a Sheet to the API
1. Lay out the sheet. Three columns is enough to start: Phone, Message, Status. Add rows as you gather leads; leave Status blank until a message sends.
2. Get an API key. In the Android Texter dashboard, go to /dashboard/settings/api-keys and generate a key. Pair your phone first if you haven't (/dashboard/devices, scan the QR code with the Android Texter app) so there's a device to send from.
3. Write the Apps Script function. From your Sheet, open Extensions > Apps Script and add a function that loops the rows and calls the API with UrlFetchApp.fetch, Apps Script's built-in method for making HTTP requests with custom headers and a JSON body:
function sendFromSheet() {
const sheet = SpreadsheetApp.getActiveSheet();
const rows = sheet.getDataRange().getValues();
const apiKey = PropertiesService.getScriptProperties().getProperty('ATX_API_KEY');
for (let i = 1; i < rows.length; i++) {
const [phone, message, status] = rows[i];
if (!phone || !message || status) continue;
const response = UrlFetchApp.fetch('https://androidtexter.com/api/v1/messages/send', {
method: 'post',
contentType: 'application/json',
headers: { 'X-API-Key': apiKey },
payload: JSON.stringify({ to: phone, body: message }),
muteHttpExceptions: true
});
sheet.getRange(i + 1, 3).setValue(response.getResponseCode() === 200 ? 'sent' : 'failed');
}
}
Store the API key in Script Properties (Project Settings > Script Properties in the Apps Script editor) rather than pasting it into the code, so it doesn't end up in your Sheet's version history.
4. Run it. Add a custom menu item (onOpen() with addMenu) so anyone with edit access can trigger sendFromSheet without opening the script editor, or attach a time-driven trigger under Triggers if you want it to run on a schedule.
5. For sends that need to land at a specific time, use the API's own scheduling instead of a Sheets trigger: pass an RFC 3339 timestamp in scheduled_at on the same send call, and manage or cancel queued sends later from /dashboard/scheduled.
Keep an eye on volume, not just the row count
The send endpoint accepts up to 500 numbers in a single broadcast call, but a phone number is still a phone number: it has the same practical sending capacity whether the message came from your thumbs or a script. If your sheet has a few thousand rows, don't fire them all from one device in one pass. Android Texter supports pairing multiple phones on one account and targeting a specific one with a device_id on the send call, which is the more realistic way to spread a large list across several lines instead of pushing all of it through one number.
Handling replies without babysitting the sheet
A script that only sends is half a workflow. Two ways to get replies back:
- Poll for them.
GET /api/messages/receivedreturns your latest inbound messages, and a time-driven Apps Script trigger can pull it every few minutes and write new rows into aRepliestab. - Register a webhook.
POST /api/v1/webhookstakes an HTTPS URL and pushesmessage.received,message.sent, andmessage.deliveredevents to it as they happen, with a signing secret returned at registration so you can verify each delivery with HMAC before trusting it. A webhook needs a public endpoint to receive the callback (an Apps Script web app deployment, a small serverless function, or anything else with a URL), which is more setup than polling but means you're not waiting on a trigger interval to see a reply.
For most solo spreadsheet workflows, polling every few minutes is the lower-effort starting point. Move to a webhook once reply speed actually matters, for instance if you're running speed-to-lead follow-up off the same list.
Where this fits (and where it doesn't)
This is a good fit if your list lives in Sheets already, your volume is in the hundreds to low thousands, and you're in an industry where the A2P route is blocked or throttled outright: real estate wholesaling working a list of off-market homeowner numbers, a lender sending payment-due reminders, a credit repair shop or dispensary running a loyalty list. In all of those cases, the phone-based P2P channel doesn't carry the SHAFT-C or high-risk-vertical content review that stops the same message in an aggregator's queue.
It's a worse fit once you're past a few thousand contacts a day or need short codes and guaranteed throughput. At that scale you want a registered A2P campaign and an aggregator built for it, not a spreadsheet script.
One thing a script can't do for you: consent. The TCPA applies to every text you send regardless of which pipe it travels through, and it requires prior express consent for marketing messages and a way to honor opt-outs. Sending through your own phone instead of an aggregator removes the carrier's registration gate; it does not touch the legal one. Keep your consent records in the sheet or somewhere you can produce them, and make sure STOP actually stops future sends from that number, whether that means checking replies before each send or building the check into your script.
Frequently Asked Questions
Do I need to register anything to send SMS this way?
No. Because the message routes through your own paired Android phone as person-to-person SMS, it isn't A2P traffic, so there's no Brand or Campaign to register with The Campaign Registry. You still need an Android Texter account, a paired device, and an API key.
Will Google flag my Apps Script for sending SMS?
Apps Script itself isn't sending the text; it's making an HTTPS call to the Android Texter API, the same kind of request Apps Script makes to any external service. There's no SMS-specific restriction on UrlFetchApp calls to third-party APIs.
Can I send MMS or just images this way, not just SMS?
The send endpoint supports media URLs for MMS in addition to plain text, so a script can pass an image URL alongside the message body. You'll need the media hosted somewhere with a public URL the API call can reference.
What happens if I send to a landline by mistake?
The message will fail to deliver as SMS, and you've spent a send for nothing. If your Sheet is a purchased or scraped list, running it through a landline-detection step before you send is worth the extra minute, since debugging a bad number after the fact from delivery status alone is guesswork.
Is this the same as the Zapier integration you've already written about?
No. The Zapier path is triggers and actions with no code, useful if you're already paying for Zapier and want Sheets connected to other tools too. This is a direct script against the API, no third-party subscription, built specifically for a spreadsheet-only workflow.
If your list is already in a Sheet and Twilio's registration process is the only thing standing between you and sending, Android Texter gives you the API and the phone without asking what industry you're in first.
