How I Added Instant SMS Price Alerts to My Solana Tracker
Introduction
In Part 1, I built a free, serverless Solana portfolio tracker that runs entirely inside Google Sheets. It pulls live data from the Jupiter API, logs daily snapshots, and emails me when a price or market cap alert fires.
The email alerts worked. But email has a fundamental problem: it sits in your inbox until you open it.
At 2 AM, when a token I've been watching for weeks crosses a market cap milestone, I'm asleep. The email arrives. I wake up at 8 AM, see it, check the chart — the window has already closed.
What I needed was something that would interrupt me immediately. Not a push notification from an app I'd have to install. Not a Telegram bot requiring a new account. Just an SMS, the most reliable interrupt mechanism that exists, delivered to the number already in my pocket.
This is how I added Twilio SMS integration to my portfolio tracker, the issues I hit along the way, and what it costs to run.
What I Added
The tracker already had a fully functioning email alert engine — hourly evaluation of all active alerts against live Jupiter API data, support for price-level and percentage-move conditions, one-shot and recurring modes. Adding SMS was purely additive.
When an alert fires, the script now:
Sends the email (existing behaviour, unchanged)
Immediately calls Twilio's API and delivers concise Twilio SMS messages to my phone
SMS failures never block the email — if Twilio is down or credentials are wrong, the error logs to the Apps Script execution log and the email still goes out. The alert engine doesn't care.
New Settings sheet rows:
Key | Default | Description |
sms_enabled | FALSE | Global SMS on/off switch — no code change needed |
sms_phone | — | Your number in E.164 format (e.g. +27821234567) |
New Script Properties (stored in Apps Script, never in code or the sheet):
Property | What it holds |
TWILIO_ACCOUNT_SID | Your Twilio Account SID (starts with AC…) |
TWILIO_AUTH_TOKEN | Your Auth Token from the Twilio Console |
TWILIO_FROM_NUMBER | Your Twilio number in E.164 format |
Why Not WhatsApp?
WhatsApp Business API — the official channel for programmatic WhatsApp messages — requires:
A verified Meta Business account
An approved message template for every outbound message type
Business verification that can take days or weeks
Ongoing compliance with Meta's messaging policies
For a personal portfolio tracker sending one notification at a time to my own number, that's bureaucratic overkill. WhatsApp is built for broadcast messaging at scale. It's the wrong tool when you just want to text yourself.
SMS is universal, instant, and requires no app on the recipient's side. Twilio's REST API is trivially callable from Google Apps Script. The only requirement is a Twilio account with a phone number.
The Architecture
The tracker runs inside Google Apps Script — not on a server I control. No separate machine runs in the background. Google's infrastructure handles everything, triggered by time-based schedules.
There's also no traditional way to "call Twilio" — no Node.js process, no backend endpoint. Instead, Apps Script has a built-in HTTP client for making web requests. Twilio's SMS API is a standard web request: send it a phone number and a message, and Twilio delivers the SMS. That HTTP client is all that's needed.
When an alert fires, the script checks three things before attempting to send: SMS is enabled in the Settings sheet, a destination phone number is provided, and all three Twilio credentials are present. If anything is missing, it logs a note and moves on — the email sends regardless.
If everything is in order, the script builds a short text message and sends it to Twilio, which delivers the SMS within seconds. The whole process adds less than a second to each alert check.
Twilio credentials — Account SID, Auth Token, and sending phone number — are stored in Apps Script's secure properties store. They never appear in the spreadsheet, in the code, or anywhere in this Replit project. The script reads each value at runtime, uses it once, and never exposes it.
Feature Deep-Dive
The Two-Level Off Switch
I didn't want a setup where disabling SMS requires code changes or deleting credentials. Two controls handle this with zero code changes:
Global kill switch: Set sms_enabled to FALSE in the Settings sheet. The function returns immediately and nothing sends. Credentials stay intact in Script Properties, ready to re-enable with a single cell edit.
Per-token silence: Use Pause in the Manage Alerts sidebar on any individual alert. That alert stops evaluating — no email, no SMS — until you Activate it again. The rule is preserved; only evaluation stops.
During weeks when I'm watching 2–3 tokens closely, I can pause everything else without touching code or credentials.
The SMS Cost Trap
The first test message cost $0.271. That seemed high, so I dug in.
How Twilio charges for SMS:
SMS is billed per segment. In standard GSM-7 encoding, one segment is 160 characters. But if your message contains any emoji, the entire message switches to UCS-2 encoding — and one UCS-2 segment is only 70 characters. A 200-character message with one emoji becomes 3 segments, not 2.
My original body looked like this:
One emoji. Long detail string. Result: 3–4 segments at $0.04–0.05 each, plus Twilio's South Africa carrier surcharge. Total: $0.271.
The fix: strip the emoji and build a compact body from alert fields rather than the verbose detail string:
No emoji → GSM-7 encoding → 160 chars/segment. That message is well under 160 characters. The second test came in at $0.1355 — still includes the SA carrier surcharge, but no longer paying for extra segments.
The Phone Number Formatting Quirk
Google Sheets has an unintuitive behaviour: type +27715670387 into a cell and Sheets reads the + as a formula operator, strips it, and stores 27715670387 as a number.
When the script reads that back as a string, it may return 2.7715330386e10 — scientific notation for a large number — producing a completely wrong phone number.
The fix has two parts:
First, format the sms_phone cell as Plain text (Format → Number → Plain text) before typing the number. This forces Sheets to treat it as a string and preserve the +.
Second, the code defensively restores the + if Sheets strips it anyway:
if (phone.charAt(0) !== "+") phone = "+" + phone;
This covers cells already saved as numbers before the formatting was fixed.
Credentials: Auth Token vs API Key
Twilio offers two credential types: Account SID + Auth Token (on your Console homepage) and API Keys (SK… prefix, created separately).
Script Properties need the Auth Token — displayed directly on the Twilio Console dashboard. An API Key here will fail with a 401.
If you've used the Replit Twilio integration, note that the Replit connector form asks for API Key + API Key Secret, not the Auth Token. These are different credential types, relevant only for the Replit connector. Apps Script uses the Auth Token directly.
Setting It Up — Step by Step
1. Get a Twilio account and number
Sign up at twilio.com. The free trial includes a Twilio number and roughly $15 of credit — enough for months of personal alert testing.
2. Store credentials in Script Properties
In the Apps Script editor: ⚙️ Project Settings → Script Properties → Add script property:
Property | Value TWILIO_ACCOUNT_SID | From your Twilio Console homepage (starts with AC) TWILIO_AUTH_TOKEN | From your Twilio Console homepage (the token, not an API key) TWILIO_FROM_NUMBER | Your Twilio number, e.g. +12025550123
3. Configure the Settings sheet
Click the sms_phone value cell
Format → Number → Plain text (do this before typing)
Type your number: +27715670387
Set sms_enabled to TRUE
4. Test with a forced alert
Rather than waiting for a real price move, temporarily lower one alert's threshold below the token's current value:
Open Manage Alerts → Edit target on any active alert → set it below/above the current price
Run Menu → 🔄 Run Alert Check Now
Check your phone and the Apps Script execution log (look for SMS sent to +27...)
Restore the threshold
Problems Solved Along the Way
Problem | Solution |
Email alerts sit unread for hours | Added Twilio SMS delivery right after every email send |
WhatsApp required Meta business registration + templates | Used plain SMS instead — universal, instant, no approval required |
No server to call Twilio from | UrlFetchApp.fetch() with Basic Auth — Google provides the HTTP client |
Twilio credentials can't live in code or the sheet | Stored in PropertiesService.getScriptProperties() — same pattern as Gemini key |
First SMS cost $0.271 — too expensive | Removed emoji (switched encoding from UCS-2 to GSM-7), built compact single-segment body |
Sheets strips the + prefix from phone numbers | Format cell as Plain Text before entry; code also defensively restores + |
Auth Token vs API Key confusion | Auth Token (from Console homepage) goes in Script Properties; API Keys are for other Twilio integrations |
Want to silence specific tokens without disabling SMS globally | Per-alert Pause in Manage Alerts sidebar; sms_enabled = FALSE kills everything globally |
Trial account only allows verified numbers | Verify destination number in Twilio Console → Phone Numbers → Verified Caller IDs |
What This Demonstrates About Replit
The Twilio SMS integration in Replit made credential setup and initial testing straightforward — the integration form walks you through obtaining credentials and verifying the connection before you touch any Apps Script configuration.
This feature also illustrates a workflow pattern worth noting:
Replit for development and credential discovery — use Replit's environment and integrations to obtain and test third-party credentials, then transfer them to Script Properties for runtime use
Apps Script as the runtime — once credentials are in Script Properties, Apps Script handles everything: hourly evaluation, HTTP calls to Twilio, logging, error handling
UrlFetchApp as the universal API client — any REST API accepting HTTPS + JSON or form-encoded bodies is callable this way. The Twilio SMS integration pattern here works identically for Africa's Talking, Vonage, or any other SMS provider
Adding SMS cost one new function in code.js, two new Settings rows, and a npm run push. No new infrastructure, no new server, no new deployment pipeline.
Running Costs
I'm on Twilio PAYG with roughly $18 of credit remaining. Per-SMS cost to South Africa runs $0.05–0.14 depending on message length and carrier surcharges — the compact, emoji-free body keeps it toward the lower end.
With a 24-hour cooldown on recurring alerts (default_cooldown_hours in Settings), usage is predictable: a token sends at most one SMS per day per alert, and only when the price condition is actually met.
If costs grow, Africa's Talking is the natural alternative — built for African markets, with per-SMS rates around $0.01–0.02 for South Africa. The code change is a single function swap; the credential pattern is identical.
Who This Is For
Solana degens tracking 5–30 positions who miss moves because they're not watching a screen at 2 AM
Developer-curious traders who want Twilio SMS notifications in real time without paying for a SaaS service
Google Sheets power users who already have the tracker from Part 1 and want one more layer of immediacy
Anyone who's dismissed SMS as "old-fashioned" — it's still the most reliable interrupt mechanism that works universally, requires no app install, and costs pennies per message