Twilio Send SMS: Programmable SMS Messaging API Guide
In this tutorial, you will learn about Twilio Send SMS: Programmable SMS Messaging API Guide. We cover key concepts, practical examples, and best practices to help you master this topic.
Twilio Programmable SMS lets you send text messages to any phone number globally, supporting message scheduling, delivery status callbacks, long message concatenation, and MMS with media attachments.
What You'll Learn
How to send SMS messages with the Twilio Python SDK, compose messages with Unicode support, schedule future delivery, receive delivery status via Webhooks, send long messages (segmentation), and attach media as MMS.
Why It Matters
SMS has 98% open rates within 3 minutes, making it the most reliable notification channel. DodaTech uses SMS for order confirmations, account alerts, and verification codes with real-time delivery tracking.
Real-World Use
A customer places an order on DodaTech. The server sends an SMS confirmation, sets a Webhook for delivery status, and when the message is delivered, a status callback updates the order tracking system.
flowchart LR
A["Send\nSMS API Call"] --> B["Twilio\nProcesses"]
B --> C["Message\nQueued"]
C --> D["Carrier\nDelivery"]
D --> E{"Status\nUpdate"}
E -->|delivered| F["Status Callback\nsuccess"]
E -->|undelivered| G["Status Callback\nfailed"]
E -->|failed| G
style A fill:#f22f46,color:#fff
style D fill:#dbeafe,stroke:#2563eb
style F fill:#bbf7d0,stroke:#16a34a
style G fill:#fecaca,stroke:#dc2626
Sending a Basic SMS
import os
from twilio.rest import Client
account_sid = os.environ["TWILIO_ACCOUNT_SID"]
auth_token = os.environ["TWILIO_AUTH_TOKEN"]
from_number = os.environ["TWILIO_PHONE_NUMBER"]
client = Client(account_sid, auth_token)
message = client.messages.create(
body="Your DodaTech order ORD-12345 has been confirmed!",
from_=from_number,
to="+14155551234"
)
print(f"Message SID: {message.sid}")
print(f"Status: {message.status}")
print(f"To: {message.to}")
print(f"From: {message.from_}")
print(f"Body: {message.body[:50]}...")
# Expected output:
# Message SID: SMxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# Status: queued
# To: +14155551234
# From: +14155559876
# Body: Your DodaTech order ORD-12345 has been conf...
Scheduling Messages
from datetime import datetime, timedelta
import pytz
def schedule_sms(to_number, message_body, send_at):
message = client.messages.create(
body=message_body,
from_=from_number,
to=to_number,
send_at=send_at,
schedule_type="fixed"
)
print(f"Scheduled: {message.sid}")
print(f"Send at: {send_at}")
print(f"Status: {message.status}")
return message
# Schedule for 2 hours from now
send_time = datetime.now(pytz.UTC) + timedelta(hours=2)
schedule_sms(
to_number="+14155551234",
message_body="Reminder: Your appointment is tomorrow at 10 AM.",
send_at=send_time
)
# Expected output:
# Scheduled: SMxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# Send at: 2026-06-28 15:00:00+00:00
# Status: scheduled
Delivery Status Callbacks
# Set status_callback when sending
def send_with_tracking(to_number, message_body):
message = client.messages.create(
body=message_body,
from_=from_number,
to=to_number,
status_callback="https://example.com/sms/status"
)
print(f"Sent with tracking: {message.sid}")
# The status endpoint receives POST with:
# - MessageSid
# - MessageStatus (queued, sent, delivered, failed, undelivered)
# - ErrorCode
# - To, From, Body
# Flask handler example
"""
@app.route("/sms/status", methods=["POST"])
def sms_status():
message_sid = request.form["MessageSid"]
status = request.form["MessageStatus"]
error_code = request.form.get("ErrorCode")
print(f"Message {message_sid}: {status}")
if error_code:
print(f"Error: {error_code}")
return "", 200
"""
Long Messages and MMS
# Twilio automatically concatenates messages > 160 characters
# Each segment = 160 chars (GSM-7) or 70 chars (UCS-2/Unicode)
def send_long_message(to_number, long_text):
message = client.messages.create(
body=long_text,
from_=from_number,
to=to_number
)
print(f"SID: {message.sid}")
print(f"Body length: {len(long_text)} chars")
print(f"Segment count: {message.num_segments}")
return message
# 300 character message (2 segments)
long_body = "Your DodaTech Pro subscription has been upgraded. " * 6
send_long_message("+14155551234", long_body)
# Expected output:
# SID: SMxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# Body length: 312 chars
# Segment count: 2
# Send MMS with media
def send_mms(to_number, media_url):
message = client.messages.create(
body="Check out this image!",
from_=from_number,
to=to_number,
media_url=[media_url]
)
print(f"MMS sent: {message.sid}")
print(f"Media count: {len(message.media.list())}")
return message
# send_mms("+14155551234", "https://example.com/photo.jpg")
Common Mistakes
1. Sending Without a Registered Sender ID
In many countries, you need an alphanumeric Sender ID (like DODATECH) registered with carriers. Without registration, messages show as random numbers or get blocked.
2. Not Handling Status Callbacks
Without status callbacks, you don't know if the message was delivered or failed. Always set status_callback for production use to track delivery and detect issues.
3. Ignoring Message Segmentation Costs
Each SMS segment is billed separately. A 500-character message costs up to 4x a single message. Test the num_segments property to estimate costs.
4. Sending 10DLC-Unregistered Messages
In the US, A2P 10DLC registration is required for application-to-person messaging. Unregistered traffic may be blocked or filtered. Register your messaging use case in the Twilio Console.
5. Using Incorrect Number Formatting
Always use E.164 format (+14155551234). Never use formatted numbers like (415) 555-1234 or 415-555-1234. Strip all formatting from user input before passing to the API.
Practice Questions
- What is the maximum length of a single SMS segment?
- How do you know if a message was delivered?
- What is message concatenation?
- How do you send an MMS with Twilio?
Answers:
- 160 characters for GSM-7 encoding (standard ASCII). 70 characters for UCS-2 (Unicode, emoji, special characters). Beyond these limits, Twilio concatenates into segments.
- Set a
status_callbackURL when sending. Twilio POSTs status updates (queued, sent, delivered, failed, undelivered) to your endpoint. - Messages longer than 160 characters are split into multiple segments. Twilio and the receiving phone reassemble them transparently. Each segment costs separately.
- Include a
media_urlparameter with a publicly accessible URL to an image. The message is sent as MMS instead of SMS. MMS is more expensive than SMS.
Challenge: Build a notification system that sends SMS order confirmations, tracks delivery with status callbacks, handles failed deliveries with 3 retry attempts, segments long messages correctly, sends MMS receipts with order images, and logs all status transitions.
FAQ
Mini Project
Build an SMS notification system: send a welcome message to a test number, set up a status callback endpoint, schedule a reminder message for 24 hours later, send a long message and log segment count, send an MMS with a test image, and review delivery logs in the Twilio Console.
What's Next
Receive SMS — handle incoming text messages with webhooks.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro