Skip to content

Twilio Messaging Service: Scalable SMS with Pools and Failover

DodaTech Updated 2026-06-28 6 min read

In this tutorial, you will learn about Twilio Messaging Service: Scalable SMS with Pools and Failover. We cover key concepts, practical examples, and best practices to help you master this topic.

Twilio Messaging Service is a container that manages multiple phone numbers with intelligent sender selection, geo-routing, automatic failover, A2P 10DLC Compliance, and sticky sender assignments for high-volume SMS.

What You'll Learn

How to create and configure a Messaging Service, add numbers to sender pools, configure sender selection strategies (sticky, random, geo-routing), enable A2P 10DLC compliance, handle failover, and monitor message throughput.

Why It Matters

Sending from a single number limits throughput to 1 message per second and creates a single point of failure. Messaging Service distributes load across number pools, improves deliverability, and ensures regulatory compliance. DodaTech uses Messaging Service for all customer notifications.

Real-World Use

DodaTech sends 10,000 promotional messages. The Messaging Service auto-selects the best sender based on recipient geography and carrier, distributes across 5 numbers to maintain throughput, and automatically fails over if one number is rate-limited.

flowchart LR
    A["Send Message\nvia Service"] --> B["Sender\nSelection"]
    B --> C{"Strategy"}
    C -->|Sticky| D["Same Sender\nper Recipient"]
    C -->|Random| E["Random Sender\nfrom Pool"]
    C -->|Geo| F["Local Sender\nby Country"]
    D --> G["Send Message"]
    E --> G
    F --> G
    G --> H{"Success?"}
    H -->|Yes| I["Message\nDelivered"]
    H -->|No| J["Failover to\nNext Sender"]
    J --> G
    style A fill:#f22f46,color:#fff
    style G fill:#dbeafe,stroke:#2563eb
    style I fill:#bbf7d0,stroke:#16a34a

Creating a Messaging Service

import os
from twilio.rest import Client

client = Client(
    os.environ["TWILIO_ACCOUNT_SID"],
    os.environ["TWILIO_AUTH_TOKEN"]
)

# Create a messaging service
def create_messaging_service(name, sticky_sender=True):
    service = client.messaging.services.create(
        friendly_name=name,
        status_callback="https://example.com/sms/status",
        sticky_sender=sticky_sender,
        area_code_geomatch=True,  # Auto-match area codes
        fallback_to_long_code=True,
        number_pool_enabled=True
    )
    print(f"Service SID: {service.sid}")
    print(f"Name: {service.friendly_name}")
    print(f"Sticky Sender: {service.sticky_sender}")
    return service

service = create_messaging_service("DodaTech Notifications")
# Expected output:
# Service SID: MGxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# Name: DodaTech Notifications
# Sticky Sender: True

Adding Senders to the Pool

# Add existing phone numbers to the service
def add_sender_to_pool(service_sid, number_sid):
    sender = client.messaging.services(service_sid).phone_numbers.create(
        phone_number_sid=number_sid
    )
    print(f"Added sender: {sender.sid}")
    print(f"Number SID: {sender.phone_number_sid}")
    return sender

# Add multiple numbers
number_sids = [
    "PN1111111111111111111111111111111111",
    "PN2222222222222222222222222222222222",
    "PN3333333333333333333333333333333333"
]
for nsid in number_sids:
    add_sender_to_pool(service.sid, nsid)
    # Expected per addition:
    # Added sender: PSxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
    # Number SID: PNxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

# List senders in pool
def list_pool_senders(service_sid):
    senders = client.messaging.services(service_sid).phone_numbers.list()
    print(f"Pool size: {len(senders)}")
    for s in senders:
        print(f"  {s.phone_number} (SID: {s.sid[:30]}...)")
    return senders

# list_pool_senders(service.sid)
# Expected output:
# Pool size: 3
#   +14155551234 (SID: PSxxxxxxxxxxxxxxxxxxxx...)
#   +14155555678 (SID: PSxxxxxxxxxxxxxxxxxxxx...)
#   +14155559012 (SID: PSxxxxxxxxxxxxxxxxxxxx...)

Sending via the Messaging Service

def send_via_messaging_service(service_sid, to_number, body):
    message = client.messages.create(
        messaging_service_sid=service_sid,
        to=to_number,
        body=body
    )
    print(f"Message SID: {message.sid}")
    print(f"From: {message.from_}")
    print(f"Status: {message.status}")
    print(f"Service: {message.messaging_service_sid}")
    return message

message = send_via_messaging_service(
    service.sid,
    "+14155551234",
    "Your DodaTech order is confirmed!"
)
# Expected output:
# Message SID: SMxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# From: +14155559876
# Status: queued
# Service: MGxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

A2P 10DLC Compliance

# A2P 10DLC registration (US requirement)
# 1. Register your brand
def register_brand(brand_name, website, vertical="software"):
    brand = client.messaging.brand_registrations.create(
        brand_name=brand_name,
        website=website,
        vertical=vertical,
        # Additional fields as required
    )
    print(f"Brand SID: {brand.sid}")
    print(f"Status: {brand.status}")
    print(f"Verification: {brand.verification_status}")
    return brand

# 2. Register campaign for a use case
def register_campaign(brand_sid, use_case="2FA", description="Two-factor authentication codes"):
    campaign = client.messaging.us_app_to_person.create(
        brand_registration_sid=brand_sid,
        description=description,
        message_flow=use_case,
        # Additional fields
    )
    print(f"Campaign SID: {campaign.sid}")
    print(f"Status: {campaign.status}")
    return campaign

# Expected output:
# Brand SID: BRxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# Status: PENDING
# Verification: SELF_DECLARED

Status Callback Handling

# The status_callback URL receives POST requests for each message status change
@app.route("/sms/status", methods=["POST"])
def message_status():
    message_sid = request.form["MessageSid"]
    message_status = request.form["MessageStatus"]
    error_code = request.form.get("ErrorCode", "0")
    from_number = request.form["From"]
    to_number = request.form["To"]

    print(f"Status update: {message_sid}")
    print(f"  Status: {message_status}")
    print(f"  Error: {error_code}")
    print(f"  From: {from_number} -> To: {to_number}")

    if message_status == "delivered":
        print("Message delivered successfully")
    elif message_status in ["undelivered", "failed"]:
        print(f"Delivery failed, error code: {error_code}")
    elif message_status == "sent":
        print("Message sent to carrier")

    return "", 200

Common Mistakes

1. Using a Single Number Instead of a Service

Single numbers are limited to ~1 msg/sec throughput and have no failover. Always use Messaging Service for production, even for low volume, for future scalability.

2. Not Configuring Fallback to Long Code

If your toll-free number is rate-limited, messages fail. Enable fallback_to_long_code: true so the service auto-fails over to a local number when the primary sender is blocked.

3. Skipping A2P 10DLC Registration

Without registration, messages from US numbers may be blocked or filtered by carriers. Register your brand and campaign before sending high-volume traffic to US numbers.

4. Setting Incorrect Sticky Sender Behavior

Sticky sender ensures the same recipient always sees the same sender number, which is important for reply continuity. Disable it only if you don't need reply routing.

5. Not Monitoring Pool Health

If a number in the pool gets carrier-blocked, it stays in the pool and causes failures. Regularly check delivery rates per sender and auto-remove numbers with high failure rates.

Practice Questions

  1. What is the difference between sending from a single number vs a Messaging Service?
  2. How does sticky sender work and why is it important?
  3. What is A2P 10DLC and why is it required?
  4. How does geo-routing improve deliverability?

Answers:

  1. A single number has ~1 msg/sec throughput with no failover. A Messaging Service pools multiple numbers, provides intelligent routing, failover, geo-matching, and higher throughput.
  2. Sticky sender ensures the same recipient always receives messages from the same number. This allows them to reply to that number and maintains conversation context across messages.
  3. A2P 10DLC is a US carrier requirement for application-to-person messaging. Brands register their use case and receive a campaign ID. Unregistered traffic may be blocked or filtered.
  4. Geo-routing selects a sender number local to the recipient's country or area code. Local numbers have higher deliverability, lower latency, and appear as domestic rather than international messages.

Challenge: Build a high-volume SMS system: create a Messaging Service with 5 numbers in the pool, configure sticky sender with geo-routing, register for A2P 10DLC compliance, implement status callbacks with delivery rate monitoring, set up failover testing (remove one number from pool and verify auto-failover), and generate a throughput report.

FAQ

How many numbers can I add to a Messaging Service?

You can add up to 200 phone numbers per Messaging Service. For higher throughput, create multiple services or request a limit increase.

What is maximum throughput of a Messaging Service?

Throughput scales with the number of senders. Each number adds ~1 msg/sec. With 10 numbers, you get ~10 msg/sec. Higher throughput requires a dedicated short code.

Does Messaging Service work internationally?

Yes, add numbers from different countries to a single service. Geo-routing auto-selects the best local number per recipient country.

What happens if all numbers in the pool are blocked?

Messages fail with error 21610 (unreachable carrier). Add more numbers to the pool and monitor pool health to prevent complete failure.

Can I use alphanumeric sender IDs with Messaging Service?

Yes, you can add alphanumeric sender IDs (like DODATECH) to the service. They work in countries that support alphanumeric senders (UK, EU, AU, etc.).

Mini Project

Build a scalable messaging system: create a Messaging Service with 3 numbers, configure geo-routing and sticky sender, register for A2P 10DLC, implement status callback logging, test failover by temporarily removing a number, send 100 test messages and measure throughput, and generate a delivery rate report.

What's Next

Conversation API — build multi-channel conversations with Twilio Conversations.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro