Save 44% with yearly billing.View Plans
textbee Logotextbee.dev

Spreadsheets

Send SMS from Google Sheets with Apps Script and your own phone

Published and updated

A Google Sheet can send SMS with about thirty lines of Apps Script. The script reads each row, posts to the textbee send endpoint with UrlFetchApp, and writes the batch id into a Sent column so a second run skips the row. You run it from a custom menu or on a time-driven trigger. Replies land in a sheet through the Google Sheets forwarding relay or a workflow tool.

Send
HTTP POST
to the textbee send endpoint
Receive
Webhook
MESSAGE_RECEIVED, flat JSON
Pace
10 to 15 / min
per Android phone
textbee code
None
uses what the tool ships today

What you need

  • A Google account and a sheet with a Phone column in E.164 form, a Message column, and an empty Sent column.
  • A textbee account with the Android app installed on the phone that holds the SIM, and an API key from the dashboard.
  • For replies: the forwarding relay from the Google Sheets forwarding page, or a Make, Zapier or n8n flow that appends a row.
  • The textbee app on the phone. Download the app, then create an API key in the dashboard.

Send SMS from Google Sheets

The script never sees the API key in a cell. It reads the key from a script property, sends one row at a time, and records what it sent.

  1. Prepare the sheet

    Use a header row with Phone, Name, Message and Sent. Numbers need the country code, for example +12015550123. Format the Phone column as plain text so Sheets keeps the plus sign.

  2. Store the API key

    Open Extensions, then Apps Script. In Project Settings, add a script property named TEXTBEE_API_KEY with your key.

  3. Paste the script

    Replace the contents of Code.gs with the script below and save.

    Code.gs
    const SEND_URL = 'https://api.textbee.dev/api/v1/gateway/send-sms'
    
    function onOpen() {
      SpreadsheetApp.getUi().createMenu('textbee').addItem('Send to unsent rows', 'sendUnsentRows').addToUi()
    }
    
    function sendSms(recipient, message) {
      const apiKey = PropertiesService.getScriptProperties().getProperty('TEXTBEE_API_KEY')
      const response = UrlFetchApp.fetch(SEND_URL, {
        method: 'post',
        contentType: 'application/json',
        headers: { 'x-api-key': apiKey },
        payload: JSON.stringify({ recipients: [recipient], message: message }),
        muteHttpExceptions: true,
      })
      if (response.getResponseCode() !== 200) throw new Error(response.getContentText())
      return JSON.parse(response.getContentText()).data.smsBatchId || 'sent'
    }
    
    function sendUnsentRows() {
      const sheet = SpreadsheetApp.getActiveSheet()
      const rows = sheet.getDataRange().getValues()
      const header = rows[0]
      const phone = header.indexOf('Phone')
      const message = header.indexOf('Message')
      const sent = header.indexOf('Sent')
      for (let i = 1; i < rows.length; i++) {
        if (rows[i][sent] || !rows[i][phone]) continue
        const batchId = sendSms(String(rows[i][phone]), String(rows[i][message]))
        sheet.getRange(i + 1, sent + 1).setValue(batchId)
        Utilities.sleep(4000) // the phone sends roughly 15 texts a minute
      }
    }
  4. Run it

    Reload the sheet and a textbee menu appears. Choose Send to unsent rows. Google asks for permission the first time. Each sent row gets its batch id in the Sent column. For scheduled sends, add a time-driven trigger on sendUnsentRows in the Triggers panel.

Receive SMS in Google Sheets

An Apps Script web app cannot receive the textbee webhook directly: it answers a POST with a redirect and cannot check the signature. Two routes work.

  1. Use the forwarding relay

    The Google Sheets forwarding page has a small relay that verifies the textbee signature and posts to an Apps Script web app that appends the row. It is tested and keeps every cell as plain text.

  2. Or use a workflow tool

    Register the textbee webhook in Make, Zapier or n8n, then add that tool’s Google Sheets module with sender, message and receivedAt mapped to columns. Nothing to host.

Ask your AI agent to set it up

The prompt below carries the endpoints, the payloads and the Google Sheets details from this page. Change the line that starts with What I want, paste the prompt into your agent, and it walks you through the setup with your own field names and numbers.

Prompt for your AI agent

Paste it into Claude, ChatGPT, Cursor or any coding agent. It carries the textbee API facts and the Google Sheets details the agent needs, so it does not guess.

I want to connect textbee to Google Sheets. textbee is an SMS gateway that sends and receives SMS through my own Android phone. Help me set it up step by step.

Facts about the textbee API. Use only these. Do not invent endpoints or fields.
- Send an SMS: POST https://api.textbee.dev/api/v1/gateway/send-sms with the header x-api-key: <TEXTBEE_API_KEY> and a JSON body like {"recipients": ["+12015550123"], "message": "Hello"}. recipients must be a JSON array of E.164 numbers, even for one number. Optional fields: deviceId, simSubscriptionId, scheduledAt (ISO 8601, in the future). The response is {"data": {"success": true, "smsBatchId": "..."}}.
- Check delivery or read replies: GET https://api.textbee.dev/api/v1/gateway/messages with the same header. Filters: direction=received, smsBatchId=<id from the send>, from and to as ISO timestamps. Each row has a status such as sent, delivered, failed or received.
- Receive an SMS: textbee posts JSON to a webhook URL I register in the dashboard. A MESSAGE_RECEIVED delivery has these top-level fields: smsId, message, deviceId, webhookSubscriptionId, webhookEvent, idempotencyKey, sender, receivedAt. The header X-Signature holds an HMAC-SHA256 hex digest of the raw body, keyed with the signing secret I set on the webhook. Retries reuse the same idempotencyKey.
- Limits: One Android phone sends roughly 10 to 15 messages a minute. Text only, no MMS. Messages over 160 characters are split into segments.

What I want: Send a text to every row in my Google Sheet that has not been sent yet, from a menu and on a schedule, and log replies to another sheet.

Google Sheets specifics you should know:
- Apps Script sends with UrlFetchApp.fetch using method post, contentType application/json, a headers object and a JSON string payload.
- Store the key in PropertiesService script properties, never in a cell.
- A single execution stops after 6 minutes and UrlFetch has a daily call quota, so batch large sheets and pace calls to the phone.
- An Apps Script web app cannot receive the signed textbee webhook directly; use the relay from textbee.dev/forward-sms/google-sheets or a workflow tool.

Give me:
1. The exact clicks and field values in Google Sheets, using its real field labels.
2. The request body or field mapping, with placeholders for my API key and phone numbers. Never ask me to paste the real key into this chat.
3. A test with one number and what a correct result looks like.
4. The three most likely failure causes and how to check each one.

Automations that work well

Appointment list
Phone, Name and Time columns, and a Message column built with a formula such as ="Hi "&B2&", see you at "&C2&"." The script sends whatever the formula produces.
Event-day blast
Fill the sheet the day before, then let a time-driven trigger send the rows at the hour you pick.
Order status from an export
Paste the daily export from your shop into the sheet, keep only the rows that changed, and run the menu item.
Reply log
A second sheet fed by the forwarding relay, so replies to the reminders sit next to the list that sent them.

Why teams move Google Sheets SMS to their own phone

The bill
Sheets add-ons that send SMS resell a cloud provider, so every row costs a per-message fee plus the add-on subscription. The script on this page sends through a flat textbee plan.
The reply rate
Reminders from the number the recipient already has saved get answered. Texts from a rented number get ignored or filtered.
Your data stays in your sheet
No add-on reads the sheet, no provider stores the list. The script runs in your Google account and the messages sit in your textbee account.

A cloud SMS provider in Google Sheets vs textbee in Google Sheets

What you care aboutCloud SMS providertextbee
SetupInstall an SMS add-on, create the provider account it needs, buy a number, register the sender where requiredPaste the script, add one script property with the API key: about five minutes
Sender the contact seesA rented virtual number, or an alphanumeric sender where the country allows itYour own mobile number, which your contacts already have saved
Monthly costPer message, plus number rental and carrier surcharges in some countriesA flat plan with messages included: Pro is $14.99 a month for 5,000 messages. No per-message fee, no number rental, no carrier surcharge.
ThroughputSet by the provider tier and by sender registrationOne phone sends roughly 10 to 15 messages a minute. Add phones to scale.
RepliesInbound webhook from the provider, or the provider app in the tooltextbee webhook: flat JSON, signed with HMAC-SHA256
Where the message leaves fromThe provider data centerAn Android phone you own, plugged in, on Wi-Fi or mobile data
Google costAdd-on subscription on top of the provider feesNone. Apps Script is free with the account

Per-message prices by provider and country, with sources and dates, are on the provider comparison pages.

What works and what does not

Works today

  • Sending to every unsent row from a menu or on a schedule
  • Personalized messages built with sheet formulas
  • A Sent column that stops double sends and keeps the batch id for delivery checks
  • Replies logged to a sheet through the relay or a workflow tool

Not with this route

  • Receiving texts straight into the sheet without a relay or workflow tool
  • More than a few hundred rows in one run, because a script execution stops after 6 minutes
  • MMS

What Google Sheets charges

Apps Script is free with a Google account. Consumer accounts get 20,000 URL Fetch calls a day, and each script execution stops after 6 minutes. developers.google.com, checked on September 25, 2026. textbee itself charges a flat monthly plan with messages included; see pricing.

Frequently asked questions

Why does the plus sign disappear from my numbers?

Sheets treats +12015550123 as a formula or a number. Format the Phone column as plain text before you paste, or type an apostrophe in front of the number.

How do I personalize each message?

Build the Message column with a formula that references the other columns, such as ="Hi "&B2&", your order "&D2&" is ready." The script sends the computed text.

How do I avoid sending twice?

The script skips any row whose Sent column is filled and writes the batch id there after each send. To resend a row, clear its Sent cell.

Can the sheet send on its own?

Yes. In the Apps Script editor open Triggers and add a time-driven trigger on sendUnsentRows, for example every day at 9 am. Rows you add during the day go out at the next run.

Where do replies go?

To another sheet, through the Google Sheets forwarding relay or a Make, Zapier or n8n flow that appends a row. The reply keeps the sender number, so you can match it to the row that sent the text.

Can I send from a specific phone?

Yes. Add a deviceId field to the payload in the script, or a Device column in the sheet that the script copies into the body. Without it textbee uses the default phone.

Can I schedule a row for later?

Yes. Add a scheduledAt column with an ISO 8601 time and copy it into the payload. textbee holds the message and sends it at that time, so a time-driven trigger is not needed for one-off scheduled texts.

How do I check that a row was delivered?

The Sent column holds the batch id. A second script function can GET the messages endpoint with smsBatchId set to that value and write the status into a Status column. delivered, failed and unknown are final.

How long does the setup take?

About five minutes with a phone at hand: install the textbee app, register the phone, create an API key, and paste the script and run it from the menu. Receiving adds a webhook in the textbee dashboard and the forwarding relay or a workflow tool.

Sources

  1. developers.google.com, checked on September 25, 2026
  2. developers.google.com: UrlFetchApp, checked on September 25, 2026
  3. developers.google.com: Installable triggers, checked on September 25, 2026

Read next

Your phone is already an SMS gateway. Switch it on.

Set up in minutes and send your first message for free. No credit card, no per-message fees, no carrier contracts.

Free tier forever No credit card required Open source

Need self-hosting help or a custom integration? See services