How to Send WhatsApp Messages from Google Sheets Automatically
Automatically send WhatsApp messages to a list of contacts in Google Sheets using Rapiwa API and Google Apps Script or n8n. Send bulk messages, notifications, and alerts from a spreadsheet.
You can send WhatsApp messages directly from Google Sheets by connecting it to Rapiwa API using Google Apps Script or n8n. Build a spreadsheet with a phone number column and message column, then run a script or workflow that calls Rapiwa's POST /send-message endpoint for each row. Rapiwa charges $5/month flat with no per-message fees — ideal for bulk notifications from a spreadsheet.
What You Can Build
- Bulk WhatsApp notifications from a contact list in Google Sheets
- Appointment reminders — trigger messages for appointments in a Sheet
- Payment reminders — send WhatsApp reminders for unpaid invoices tracked in Sheets
- Event invitations — message all attendees from an event registration spreadsheet
- Daily reports — send each person in a Sheet a personalized daily summary
Prerequisites
- Rapiwa account (free 3-day trial at rapiwa.com)
- Your Rapiwa API key (Dashboard → API Keys)
- A Google account with access to Google Sheets
Method 1: Google Apps Script (No Extra Tools)
This method uses only Google Sheets and the built-in Apps Script — no n8n or external tools needed.
Step 1: Set Up Your Google Sheet
Create a Google Sheet with these columns:
| A (phone) | B (name) | C (message) | D (status) | E (sent_at) |
|---|---|---|---|---|
| 8801234567890 | Sarah Johnson | Hi Sarah! Your appointment is tomorrow at 2PM. | ||
| 447700900123 | James Smith | Hi James! Your invoice #1234 is due today. | ||
| 12125551234 | Emma Davis | Hi Emma! Your order has shipped. |
Step 2: Open Apps Script
- In your Google Sheet, click Extensions → Apps Script
- Delete any existing code
- Paste the following script:
const RAPIWA_API_KEY = 'YOUR_API_KEY'; // Replace with your Rapiwa API key
const RAPIWA_URL = 'https://app.rapiwa.com/send-message';
/**
* Send WhatsApp messages to all rows where column D (status) is empty.
* Run this function from the Apps Script editor or trigger it on a schedule.
*/
function sendWhatsAppMessages() {
const sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
const lastRow = sheet.getLastRow();
// Start from row 2 (row 1 = headers)
for (let row = 2; row <= lastRow; row++) {
const phone = sheet.getRange(row, 1).getValue().toString().trim();
const name = sheet.getRange(row, 2).getValue().toString().trim();
const message = sheet.getRange(row, 3).getValue().toString().trim();
const status = sheet.getRange(row, 4).getValue().toString().trim();
// Skip if already sent or if data is missing
if (status === 'sent' || !phone || !message) {
continue;
}
// Send the WhatsApp message via Rapiwa
const result = sendMessage(phone, message);
if (result.status === 'success') {
// Mark as sent
sheet.getRange(row, 4).setValue('sent');
sheet.getRange(row, 5).setValue(new Date().toISOString());
console.log(`Sent to ${name} (${phone}): ${result.messageId}`);
} else {
sheet.getRange(row, 4).setValue('failed: ' + JSON.stringify(result));
console.error(`Failed to send to ${name} (${phone})`);
}
// Add 1-second delay between messages to respect rate limits
Utilities.sleep(1000);
}
console.log('All messages processed.');
}
/**
* Call the Rapiwa API to send a single WhatsApp message.
*/
function sendMessage(phone, message) {
const options = {
method: 'post',
contentType: 'application/json',
headers: {
'Authorization': 'Bearer ' + RAPIWA_API_KEY
},
payload: JSON.stringify({
number: phone,
message: message
}),
muteHttpExceptions: true
};
const response = UrlFetchApp.fetch(RAPIWA_URL, options);
return JSON.parse(response.getContentText());
}
/**
* Add a custom menu to the Google Sheet for easy access.
*/
function onOpen() {
SpreadsheetApp.getUi()
.createMenu('WhatsApp')
.addItem('Send Messages', 'sendWhatsAppMessages')
.addToUi();
}
Step 3: Save and Run
- Click Save (Ctrl+S)
- Click Run → Run function → sendWhatsAppMessages
- Authorize the script when prompted (needed for Google Sheets access + external fetch)
- Check the execution log for results
- Check column D in your sheet — it should show "sent" for each successful message
Step 4: Add a Custom Menu Button
The onOpen function adds a WhatsApp → Send Messages menu to your Google Sheet. This lets non-technical users trigger the send without opening Apps Script.
Step 5: Schedule Automatic Sending (Optional)
Set up a time-based trigger in Apps Script:
- In Apps Script → Triggers (clock icon)
- Click + Add Trigger
- Function:
sendWhatsAppMessages - Event source: Time-driven
- Type: Day timer → 9:00 AM – 10:00 AM
- Save
This runs the sender every day at 9 AM, sending any unsent messages.
Method 2: n8n Workflow (No Code)
For a no-code approach using n8n:
- Google Sheets Trigger node — triggers when a new row is added
- HTTP Request (Rapiwa) node — sends the WhatsApp message
- Google Sheets node — updates the status column to "sent"
n8n HTTP Request configuration:
{
"method": "POST",
"url": "https://app.rapiwa.com/send-message",
"headers": {
"Authorization": "Bearer YOUR_API_KEY"
},
"body": {
"number": "={{ $json.phone }}",
"message": "={{ $json.message }}"
}
}
Personalized Messages with Template Variables
Instead of writing the full message in column C, use a template with variables:
function buildPersonalizedMessage(name, appointmentTime, location) {
return `Hi ${name}! 👋\n\nThis is a reminder for your appointment:\n📅 Time: ${appointmentTime}\n📍 Location: ${location}\n\nReply CONFIRM to confirm or CANCEL to cancel.`;
}
// In the main loop, replace the message column with a built message:
const personalMessage = buildPersonalizedMessage(
name,
sheet.getRange(row, 3).getValue(), // Column C: appointment time
sheet.getRange(row, 4).getValue() // Column D: location
);
const result = sendMessage(phone, personalMessage);
Testing with cURL First
Before running the script for all rows, test your API key:
curl -X POST https://app.rapiwa.com/send-message \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"number": "8801234567890",
"message": "Test from Google Sheets! This is a test message from Rapiwa."
}'
Expected response:
{
"status": "success",
"messageId": "msg_sheets_test123",
"timestamp": "2026-06-16T10:30:00Z"
}
Common Errors and Fixes
- ScriptError: UrlFetchApp is not defined: You're not using Apps Script — ensure the file is in Extensions → Apps Script, not a standalone file
- Authorization error in Apps Script: Click "Review Permissions" when prompted and allow the script to access your Google Sheets and make network requests
- 401 from Rapiwa: Check that
RAPIWA_API_KEYin the script matches exactly the key in your Rapiwa Dashboard - Some rows skipped: Check column D — if a row already has any value in the status column, the script skips it. Clear the status to retry
- Rate limit errors (429): The 1-second
Utilities.sleep(1000)between messages helps. Increase to 2000ms for large lists
FAQ
Can I send WhatsApp messages from Google Sheets without Apps Script? Yes. Use n8n with a Google Sheets trigger node — it watches for new rows and sends a WhatsApp message via Rapiwa for each one without writing code.
Is there a limit on how many rows I can send? Google Apps Script has a daily runtime limit of 6 minutes for free accounts (30 minutes for Workspace). At 1 second per message, you can send ~360 messages per run. For larger lists, run in batches or upgrade to Google Workspace.
Does Rapiwa charge per message for bulk sends from Google Sheets? No. Rapiwa charges $5/month flat regardless of message volume — send 100 or 10,000 messages at the same price.
Can I send images or PDFs from Google Sheets via WhatsApp?
Yes. Store the media URL in a column and modify the script to call Rapiwa's /send-image or /send-document endpoint instead of /send-message.
Can I track which messages were opened? WhatsApp doesn't provide open tracking via API (only in WhatsApp Business Platform's template messages). However, Rapiwa provides delivery confirmation which you can log in the status column.
