A complete technical migration guide. The actual code diff is 12 lines. The validation process takes 24-48 hours. The full migration — parallel-send validation, webhook updates, phone-number porting decisions, final cutover — fits in 4-8 hours of engineering time for a standard Twilio SDK integration. This guide walks the 9 concrete steps, maps every Twilio API field to its smsroute equivalent, and shows the real code diffs you'll commit.
The 9-step migration checklist
- Create an smsroute accountSign up at smsroute.cc with email only. 30 seconds, no KYC. Top up $5 USDT TRC-20 for testing. Generate two API keys in /dashboard/api-keys: one sandbox (sk_test_*) and one production (sk_live_*).
- Install the smsroute clientPython:
pip install smsroute. Node.js:npm install smsroute. Go:go get github.com/smsroute/smsroute-go. PHP:composer require smsroute/smsroute. All official clients are MIT-licensed and mirror the smsroute REST API surface. - Replace the Twilio send callSwap the client instantiation and update the messages.create() arguments. Field shape is close — see the code diff below. Reserve a feature flag so you can switch between Twilio and smsroute with a config change, not a redeploy.
- Update your webhook handlerOption A: update the handler to accept smsroute's payload shape (see field mapping table below). Option B: install github.com/smsroute/twilio-compat in front of your handler — it translates smsroute webhooks into Twilio shape with <5ms latency and zero handler changes.
- Validate in sandboxSend to these magic test numbers: +15005550001 (returns accepted → delivered), +15005550002 (returns accepted → failed), +15005550003 (returns accepted → undelivered), +15005550004 (HTTP 429 rate_limited), +15005550005 (HTTP 500 internal_error). Verify your app handles every status and error path. Sandbox sends are free.
- Parallel-send validationTurn on shadow-traffic mode: for 24-48 hours, duplicate 10% of outbound SMS to smsroute while the other 100% goes through Twilio. Compare delivery receipts. Any edge cases (weird sender IDs, UCS-2 characters, specific destination carriers) surface during this window. Cost of the 10% shadow traffic at 50k total volume: ~$1.50.
- Flip the flagRoute 100% of outbound traffic to smsroute. Keep Twilio credentials in place and the Twilio account funded for 7 days as emergency rollback. Monitor Grafana / Datadog / your equivalent for delivery-rate regressions vs the Twilio baseline.
- Migrate phone numbers (if needed)Alphanumeric sender IDs: no action, they work day one on smsroute. Long codes: submit RespOrg transfer via smsroute support, 7-10 business days. US A2P 10DLC campaigns: re-register campaign under smsroute's TCR relationship, 2-5 days + small TCR fees. Shortcodes: plan a 6-12 week port or keep on Twilio during migration.
- Cancel TwilioAfter 7 days of clean smsroute operation in production, cancel your Twilio subscription. Keep the Twilio account open with $0 balance for 30 days in case of a surprise regression in a low-volume destination country. Export Messaging Logs from Twilio first if you need the historical data for compliance.
API endpoint mapping — every Twilio endpoint to its smsroute equivalent
| Twilio endpoint | smsroute equivalent | Notes |
|---|---|---|
POST /Accounts/{SID}/Messages.json | POST /v1/messages | JSON body instead of form-encoded. Fields To/From/Body → to/from/body (lowercase). |
GET /Accounts/{SID}/Messages/{MessageSid} | GET /v1/messages/{id} | Message ID format differs: Twilio SMxxxxxxx...; smsroute msg_01HZX... (ULID). |
| (no bulk endpoint — loop Messages.json) | POST /v1/messages/bulk | smsroute supports up to 1,000 messages in one call. Single auth + TLS handshake for the batch. |
GET /Accounts/{SID}/Balance.json | GET /v1/balance | Both return USD balance as decimal string. |
GET /Pricing/v2/Messaging/Countries/{IsoCode} | GET /v1/pricing?country={iso2} | smsroute returns single flat rate per country; Twilio returns per-carrier breakdown. Use GET /v1/pricing/lookup?to=+NUMBER for exact per-number quote. |
GET /Accounts/{SID}/IncomingPhoneNumbers.json | GET /v1/phone-numbers | Inventory of rented long codes for two-way SMS. |
| Webhook: form-encoded POST to StatusCallback | Webhook: JSON POST to status_callback | HMAC-SHA256 signature on both, different header names. |
The 12-line code diff — Twilio Python SDK to smsroute
Using httpx directly (minimum-dependency migration)
- from twilio.rest import Client
- client = Client(account_sid=os.environ["TWILIO_SID"],
- auth_token=os.environ["TWILIO_AUTH"])
- message = client.messages.create(
- to="+5491123456789",
- from_="smsroute",
- body="Your verification code is 384921",
- status_callback="https://api.example.com/webhooks/status"
- )
- print(message.sid, message.status)
+ import httpx, os
+ r = httpx.post(
+ "https://api.smsroute.cc/v1/messages",
+ headers={"Authorization": f"Bearer {os.environ['SMSROUTE_API_KEY']}"},
+ json={
+ "to": "+5491123456789",
+ "from": "smsroute",
+ "body": "Your verification code is 384921",
+ "status_callback": "https://api.example.com/webhooks/status"
+ },
+ )
+ data = r.json()
+ print(data["id"], data["status"])
Using the official smsroute-python client (idiomatic migration)
- from twilio.rest import Client
- client = Client(os.environ["TWILIO_SID"], os.environ["TWILIO_AUTH"])
+ from smsroute import Client
+ client = Client(api_key=os.environ["SMSROUTE_API_KEY"])
message = client.messages.create(
to="+5491123456789",
- from_="smsroute",
+ from_="smsroute", # unchanged
body="Your verification code is 384921",
status_callback="https://api.example.com/webhooks/status"
)
- print(message.sid, message.status)
+ print(message.id, message.status) # .sid → .id
That's the whole thing for the send path. The surface is deliberately kept close to Twilio's so teams familiar with the Twilio SDK don't have to re-learn a new idiom.
Webhook payload translation
Delivery receipts are where Twilio and smsroute differ most. The functional content is identical — message ID, status, destination, timestamp, carrier — but field names and serialization format differ:
| Twilio field (form-encoded) | smsroute field (JSON) | Notes |
|---|---|---|
MessageSid | message_id | Twilio: SMxxx...; smsroute: msg_01HZX... (ULID) |
MessageStatus | status | Values: Twilio queued/sending/sent/delivered/undelivered/failed; smsroute accepted/sent/delivered/undelivered/failed |
To | to | Both E.164 format |
From | from | Alphanumeric sender ID or E.164 long code |
Body | body | Not included in smsroute status callbacks by default — use include_body=true on send if you need it back on the webhook |
NumSegments | segments | Integer count of SMS segments |
Price | price_usd | String decimal; smsroute always USD |
ErrorCode | error_code | Twilio numeric (30003, 30005); smsroute string (invalid_destination, carrier_unreachable) |
ErrorMessage | error_message | Human-readable explanation |
X-Twilio-Signature | X-SmsRoute-Signature | Both HMAC-SHA256; different header name. smsroute includes a timestamp: t=1713724912,v1=abc123... |
| Content-Type: form-urlencoded | Content-Type: application/json | The biggest functional difference — parsing changes |
Webhook handler — before
@app.post("/webhooks/twilio-status")
async def handle_status(request: Request):
form = await request.form()
msg_sid = form["MessageSid"]
status = form["MessageStatus"]
if status == "delivered":
mark_delivered(msg_sid)
elif status in ("failed", "undelivered"):
mark_failed(msg_sid, form.get("ErrorCode"))
Webhook handler — after (native smsroute)
@app.post("/webhooks/smsroute-status")
async def handle_status(request: Request):
payload = await request.json()
msg_id = payload["message_id"]
status = payload["status"]
if status == "delivered":
mark_delivered(msg_id)
elif status in ("failed", "undelivered"):
mark_failed(msg_id, payload.get("error_code"))
Webhook handler — after (using twilio-compat shim)
# No handler changes. Install the middleware in your request chain:
from twilio_compat import translate_smsroute_to_twilio
app.add_middleware(translate_smsroute_to_twilio)
# Your existing /webhooks/twilio-status handler keeps working unchanged.
Error code mapping
Twilio uses numeric error codes in the 30000-range for SMS errors; smsroute uses descriptive strings. The semantic mapping is direct:
| Twilio code | smsroute code | HTTP | Meaning |
|---|---|---|---|
| 30003 | invalid_destination | 400 | Destination phone number is invalid or unreachable |
| 30004 | forbidden_destination | 403 | Destination country is not supported or currently suspended |
| 30005 | invalid_sender_id | 400 | Sender ID violates destination country's rules |
| 30006 | carrier_unreachable | 503 | Carrier is temporarily not accepting traffic |
| 30007 | body_too_long | 400 | Body exceeds segmentation limits |
| 30008 | spam_filtered | — | Carrier flagged as spam (webhook error_code only) |
| 30010 | rate_limited | 429 | Exceeded per-key throughput; respect Retry-After |
| 20003 | unauthorized | 401 | Missing or invalid API key |
| 20403 | insufficient_balance | 402 | Account balance below destination's price; top up |
| 20500 | internal_error | 500 | Our problem; safe to retry with idempotency key |
Full mapping table at github.com/smsroute/twilio-compat/blob/main/error_codes.md, kept in sync with Twilio's published error-code registry.
Idempotency handling — a feature Twilio doesn't have
One place where smsroute actually has a feature Twilio lacks: idempotency keys on POST /v1/messages. Pass an Idempotency-Key header (any unique string, typically UUIDv4) and smsroute dedups retries within 24 hours:
r = httpx.post(
"https://api.smsroute.cc/v1/messages",
headers={
"Authorization": f"Bearer {KEY}",
"Idempotency-Key": "8f1e0b82-7c59-4a8c-b5e9-2c4f3d9a1e2f",
},
json={"to": "+5491123456789", "from": "smsroute", "body": "..."},
)
Safely retry on transient network failures without risk of duplicate sends. Twilio has no equivalent — migration users frequently add idempotency to their retry logic during the switch. It's strictly additive: your pre-migration Twilio code doesn't break without it, it just can't take advantage of it.
Cost comparison at realistic volume
The concrete ROI. A SaaS sending 50,000 OTP SMS/month split across 10 international countries (excluding US) — a typical transactional workload for a consumer fintech, exchange, or notification service:
| Metric | Twilio | smsroute | Delta |
|---|---|---|---|
| Blended per-SMS rate | ~$0.0476 | ~$0.0136 | 71% lower |
| Monthly SMS cost (50k msgs) | $2,380 | $680 | -$1,700/mo |
| Annual SMS cost | $28,560 | $8,160 | -$20,400/yr |
| Migration engineering time | — | 4-8 hours | — |
| Break-even (hours of saved cost to recover migration time) | — | ~13 hrs | Break-even week 1 |
| 3-year cumulative savings | $85,680 spent | $24,480 spent | $61,200 saved |
Parallel-send validation pattern — the shadow-traffic approach
The standard way to de-risk the migration is dual-write for 24-48 hours. A simple middleware pattern:
import random, os, httpx
from twilio.rest import Client as TwilioClient
twilio = TwilioClient(os.environ["TWILIO_SID"], os.environ["TWILIO_AUTH"])
SHADOW_PCT = float(os.environ.get("SMSROUTE_SHADOW_PCT", "0.10")) # 10% shadow
def send_sms(to: str, body: str, **kwargs) -> dict:
# Primary: Twilio (users get the Twilio response)
msg = twilio.messages.create(to=to, from_="smsroute", body=body, **kwargs)
result = {"id": msg.sid, "status": msg.status, "provider": "twilio"}
# Shadow: smsroute (discarded, logged for comparison)
if random.random() < SHADOW_PCT:
try:
r = httpx.post(
"https://api.smsroute.cc/v1/messages",
headers={"Authorization": f"Bearer {os.environ['SMSROUTE_API_KEY']}"},
json={"to": to, "from": "smsroute", "body": body, **kwargs},
timeout=10,
)
log.info("shadow_sms", twilio_id=msg.sid, smsroute=r.json())
except Exception as e:
log.warning("shadow_sms_failed", error=str(e))
return result
After 24-48 hours of shadow logs, query for:
- Delivery-rate parity (should be within 1-2 percentage points across countries)
- Latency comparison (smsroute should be equal or faster on most non-US routes)
- Error-rate parity (both should be very low on well-formed inputs)
- Edge cases (specific destination carriers where one provider outperforms)
Once the comparison is clean, flip SHADOW_PCT to 1.0 and reverse primary/shadow roles — smsroute becomes primary, Twilio becomes shadow (or drops out entirely).
Frequently asked questions
How long does a typical Twilio to smsroute migration take?
4-8 hours of engineering work for a standard codebase using the Twilio Python or Node.js SDK. The actual code diff is 12-30 lines. Most of the time is spent on webhook URL updates, validating the first 100-1000 test sends, and updating CI/staging secrets. Teams running Twilio-specific products (Flex, Studio, Verify) will need more work — those aren't 1:1 replaceable.
Can I run Twilio and smsroute in parallel during migration?
Yes — this is the recommended validation pattern. Route 10% of outbound SMS through smsroute for 24-48 hours while 90% continues on Twilio. Compare delivery receipts to catch any edge cases specific to your destination countries.
What is the twilio-compat middleware?
A 30-line open-source middleware at github.com/smsroute/twilio-compat that translates smsroute's delivery-receipt webhook payload into Twilio's webhook shape. Install it in front of your existing handler and smsroute webhooks arrive with Twilio's field names — no handler changes needed. Adds <5ms latency.
Do I need to port my Twilio phone numbers?
Depends on which numbers. Alphanumeric sender IDs are free and work day one. Long codes are portable via RespOrg transfer (7-10 business days). Shortcodes require 6-12 weeks and CTIA re-approval — most teams keep shortcodes on Twilio during migration. US A2P 10DLC campaigns must be re-registered.
Can I keep my Twilio error-handling logic?
Mostly yes. Twilio uses numeric error codes; smsroute uses descriptive strings. Semantic mapping is clean and documented in the table above and at github.com/smsroute/twilio-compat.
What happens to my existing Twilio usage data?
It stays on Twilio. smsroute does not import historical message logs. Export from Twilio's Messaging Logs before cancellation if you need the historical data for compliance. Going forward, smsroute's /v1/messages/{id} endpoint and webhook delivery receipts feed your own logging infrastructure.
Is there a cost during migration?
Minimal. During parallel-send validation you pay both providers for the 10% overlap — ~$3-8 total on 50k/month volume. No migration fees from either provider. smsroute's sandbox is free.
Does smsroute support all the same destination countries as Twilio?
smsroute covers 149 countries — effectively the same international coverage as Twilio. For the top 50 destinations that account for 95%+ of most teams' SMS traffic, smsroute and Twilio route to the same tier-1 carriers.
Related pages
- smsroute vs Twilio — the honest comparison (TL;DR of when each wins)
- REST API reference · 149-country pricing grid
How long does a typical Twilio to smsroute migration take?
4-8 hours of engineering work for a standard codebase using the Twilio Python or Node.js SDK. The actual code diff is 12-30 lines. Most of the time is spent on webhook URL updates in your provider panel, validating the first 100-1000 test sends, and updating CI/staging secrets. Teams running Twilio-specific products (Flex, Studio, Verify) will need more work — those are not 1:1 replaceable and may stay on Twilio while SMS moves to smsroute.
Can I run Twilio and smsroute in parallel during migration?
Yes — this is the recommended validation pattern. Route 10% of outbound SMS through smsroute for 24-48 hours while 90% continues on Twilio. Compare delivery receipts between the two providers to catch any edge cases specific to your destination countries. Once parallel-send passes cleanly, flip to 100% smsroute and keep Twilio credentials in place for a week as emergency rollback.
What is the twilio-compat middleware?
twilio-compat is a 30-line open-source middleware at github.com/smsroute/twilio-compat that translates smsroute's delivery-receipt webhook payload into Twilio's webhook shape. If you don't want to modify your existing Twilio webhook handler during the migration, install twilio-compat in front of your handler and smsroute webhooks will arrive with Twilio's field names (MessageSid, MessageStatus, To, From, etc.). Adds <5ms latency.
Do I need to port my Twilio phone numbers?
It depends on which numbers. Alphanumeric sender IDs are portable for free — they are per-send configuration, not account-bound. Long codes (regular phone numbers) can be ported via RespOrg transfer, typically 7-10 business days and some carrier fees. Shortcodes are technically portable but require 6-12 weeks and CTIA re-approval — most teams keep shortcodes on Twilio during migration. US A2P 10DLC campaigns must be re-registered under smsroute's TCR relationship.
Can I keep my Twilio error-handling logic?
Mostly yes. Twilio uses numeric error codes (30003, 30005, 30007, etc.) documented in their error-code table. smsroute uses descriptive string codes (invalid_destination, insufficient_balance, rate_limited). The semantic mapping is clean: most Twilio numeric codes have a direct smsroute string equivalent. We maintain a mapping table at github.com/smsroute/twilio-compat/blob/main/error_codes.md so you can translate existing error-handling conditionals. For retry logic, both providers return the same HTTP status class for the same failure categories (400 for permanent, 429 for rate limit, 500/503 for transient).
What happens to my existing Twilio usage data?
It stays on Twilio. smsroute does not import historical message logs or billing data from Twilio. If you need to retain message logs for compliance or analytics, export from Twilio's Messaging Logs before cancellation (retention varies by Twilio plan, typically 30 days for standard accounts). Going forward, smsroute provides /v1/messages/{id} endpoint for retrieval of individual messages and webhook delivery receipts that your own logging infrastructure can persist.
Is there a cost during migration?
Minimal. During parallel-send validation you pay both providers for the overlap — typically 24-48 hours of ~10% double-sending. On 50k messages/month volume, that overlap costs ~$3-8 total. There is no migration fee from either provider. smsroute's sandbox API is free (no balance consumed) so all pre-production testing costs zero.
Does smsroute support all the same destination countries as Twilio?
smsroute covers 149 countries — effectively the same international coverage as Twilio for SMS. There are a handful of small Pacific and African markets where Twilio has direct connections smsroute routes through aggregator partners, which shows up as slightly longer latency (~300-600ms vs ~150-300ms). For the top 50 destination countries that account for 95%+ of most teams' SMS traffic, smsroute and Twilio route to the same tier-1 carriers. See our full country list at /send-sms-to.
How long does a typical Twilio to smsroute migration take?
4-8 hours of engineering work for a standard codebase using the Twilio Python or Node.js SDK. The actual code diff is 12-30 lines. Most of the time is spent on webhook URL updates in your provider panel, validating the first 100-1000 test sends, and updating CI/staging secrets. Teams running Twilio-specific products (Flex, Studio, Verify) will need more work — those are not 1:1 replaceable and may stay on Twilio while SMS moves to smsroute.
Can I run Twilio and smsroute in parallel during migration?
Yes — this is the recommended validation pattern. Route 10% of outbound SMS through smsroute for 24-48 hours while 90% continues on Twilio. Compare delivery receipts between the two providers to catch any edge cases specific to your destination countries. Once parallel-send passes cleanly, flip to 100% smsroute and keep Twilio credentials in place for a week as emergency rollback.
What is the twilio-compat middleware?
twilio-compat is a 30-line open-source middleware at github.com/smsroute/twilio-compat that translates smsroute's delivery-receipt webhook payload into Twilio's webhook shape. If you don't want to modify your existing Twilio webhook handler during the migration, install twilio-compat in front of your handler and smsroute webhooks will arrive with Twilio's field names (MessageSid, MessageStatus, To, From, etc.). Adds <5ms latency.
Do I need to port my Twilio phone numbers?
It depends on which numbers. Alphanumeric sender IDs are portable for free — they are per-send configuration, not account-bound. Long codes (regular phone numbers) can be ported via RespOrg transfer, typically 7-10 business days and some carrier fees. Shortcodes are technically portable but require 6-12 weeks and CTIA re-approval — most teams keep shortcodes on Twilio during migration. US A2P 10DLC campaigns must be re-registered under smsroute's TCR relationship.
Can I keep my Twilio error-handling logic?
Mostly yes. Twilio uses numeric error codes (30003, 30005, 30007, etc.) documented in their error-code table. smsroute uses descriptive string codes (invalid_destination, insufficient_balance, rate_limited). The semantic mapping is clean: most Twilio numeric codes have a direct smsroute string equivalent. We maintain a mapping table at github.com/smsroute/twilio-compat/blob/main/error_codes.md so you can translate existing error-handling conditionals. For retry logic, both providers return the same HTTP status class for the same failure categories (400 for permanent, 429 for rate limit, 500/503 for transient).
What happens to my existing Twilio usage data?
It stays on Twilio. smsroute does not import historical message logs or billing data from Twilio. If you need to retain message logs for compliance or analytics, export from Twilio's Messaging Logs before cancellation (retention varies by Twilio plan, typically 30 days for standard accounts). Going forward, smsroute provides /v1/messages/{id} endpoint for retrieval of individual messages and webhook delivery receipts that your own logging infrastructure can persist.
Is there a cost during migration?
Minimal. During parallel-send validation you pay both providers for the overlap — typically 24-48 hours of ~10% double-sending. On 50k messages/month volume, that overlap costs ~$3-8 total. There is no migration fee from either provider. smsroute's sandbox API is free (no balance consumed) so all pre-production testing costs zero.
Does smsroute support all the same destination countries as Twilio?
smsroute covers 149 countries — effectively the same international coverage as Twilio for SMS. There are a handful of small Pacific and African markets where Twilio has direct connections smsroute routes through aggregator partners, which shows up as slightly longer latency (~300-600ms vs ~150-300ms). For the top 50 destination countries that account for 95%+ of most teams' SMS traffic, smsroute and Twilio route to the same tier-1 carriers. See our full country list at /send-sms-to.
How long does a typical Twilio to smsroute migration take?
4-8 hours of engineering work for a standard codebase using the Twilio Python or Node.js SDK. The actual code diff is 12-30 lines. Most of the time is spent on webhook URL updates in your provider panel, validating the first 100-1000 test sends, and updating CI/staging secrets. Teams running Twilio-specific products (Flex, Studio, Verify) will need more work — those are not 1:1 replaceable and may stay on Twilio while SMS moves to smsroute.
Can I run Twilio and smsroute in parallel during migration?
Yes — this is the recommended validation pattern. Route 10% of outbound SMS through smsroute for 24-48 hours while 90% continues on Twilio. Compare delivery receipts between the two providers to catch any edge cases specific to your destination countries. Once parallel-send passes cleanly, flip to 100% smsroute and keep Twilio credentials in place for a week as emergency rollback.
What is the twilio-compat middleware?
twilio-compat is a 30-line open-source middleware at github.com/smsroute/twilio-compat that translates smsroute's delivery-receipt webhook payload into Twilio's webhook shape. If you don't want to modify your existing Twilio webhook handler during the migration, install twilio-compat in front of your handler and smsroute webhooks will arrive with Twilio's field names (MessageSid, MessageStatus, To, From, etc.). Adds <5ms latency.
Do I need to port my Twilio phone numbers?
It depends on which numbers. Alphanumeric sender IDs are portable for free — they are per-send configuration, not account-bound. Long codes (regular phone numbers) can be ported via RespOrg transfer, typically 7-10 business days and some carrier fees. Shortcodes are technically portable but require 6-12 weeks and CTIA re-approval — most teams keep shortcodes on Twilio during migration. US A2P 10DLC campaigns must be re-registered under smsroute's TCR relationship.
Can I keep my Twilio error-handling logic?
Mostly yes. Twilio uses numeric error codes (30003, 30005, 30007, etc.) documented in their error-code table. smsroute uses descriptive string codes (invalid_destination, insufficient_balance, rate_limited). The semantic mapping is clean: most Twilio numeric codes have a direct smsroute string equivalent. We maintain a mapping table at github.com/smsroute/twilio-compat/blob/main/error_codes.md so you can translate existing error-handling conditionals. For retry logic, both providers return the same HTTP status class for the same failure categories (400 for permanent, 429 for rate limit, 500/503 for transient).
What happens to my existing Twilio usage data?
It stays on Twilio. smsroute does not import historical message logs or billing data from Twilio. If you need to retain message logs for compliance or analytics, export from Twilio's Messaging Logs before cancellation (retention varies by Twilio plan, typically 30 days for standard accounts). Going forward, smsroute provides /v1/messages/{id} endpoint for retrieval of individual messages and webhook delivery receipts that your own logging infrastructure can persist.
Is there a cost during migration?
Minimal. During parallel-send validation you pay both providers for the overlap — typically 24-48 hours of ~10% double-sending. On 50k messages/month volume, that overlap costs ~$3-8 total. There is no migration fee from either provider. smsroute's sandbox API is free (no balance consumed) so all pre-production testing costs zero.
Does smsroute support all the same destination countries as Twilio?
smsroute covers 149 countries — effectively the same international coverage as Twilio for SMS. There are a handful of small Pacific and African markets where Twilio has direct connections smsroute routes through aggregator partners, which shows up as slightly longer latency (~300-600ms vs ~150-300ms). For the top 50 destination countries that account for 95%+ of most teams' SMS traffic, smsroute and Twilio route to the same tier-1 carriers. See our full country list at /send-sms-to.
How long does a typical Twilio to smsroute migration take?
4-8 hours of engineering work for a standard codebase using the Twilio Python or Node.js SDK. The actual code diff is 12-30 lines. Most of the time is spent on webhook URL updates in your provider panel, validating the first 100-1000 test sends, and updating CI/staging secrets. Teams running Twilio-specific products (Flex, Studio, Verify) will need more work — those are not 1:1 replaceable and may stay on Twilio while SMS moves to smsroute.
Can I run Twilio and smsroute in parallel during migration?
Yes — this is the recommended validation pattern. Route 10% of outbound SMS through smsroute for 24-48 hours while 90% continues on Twilio. Compare delivery receipts between the two providers to catch any edge cases specific to your destination countries. Once parallel-send passes cleanly, flip to 100% smsroute and keep Twilio credentials in place for a week as emergency rollback.
What is the twilio-compat middleware?
twilio-compat is a 30-line open-source middleware at github.com/smsroute/twilio-compat that translates smsroute's delivery-receipt webhook payload into Twilio's webhook shape. If you don't want to modify your existing Twilio webhook handler during the migration, install twilio-compat in front of your handler and smsroute webhooks will arrive with Twilio's field names (MessageSid, MessageStatus, To, From, etc.). Adds <5ms latency.
Do I need to port my Twilio phone numbers?
It depends on which numbers. Alphanumeric sender IDs are portable for free — they are per-send configuration, not account-bound. Long codes (regular phone numbers) can be ported via RespOrg transfer, typically 7-10 business days and some carrier fees. Shortcodes are technically portable but require 6-12 weeks and CTIA re-approval — most teams keep shortcodes on Twilio during migration. US A2P 10DLC campaigns must be re-registered under smsroute's TCR relationship.
Can I keep my Twilio error-handling logic?
Mostly yes. Twilio uses numeric error codes (30003, 30005, 30007, etc.) documented in their error-code table. smsroute uses descriptive string codes (invalid_destination, insufficient_balance, rate_limited). The semantic mapping is clean: most Twilio numeric codes have a direct smsroute string equivalent. We maintain a mapping table at github.com/smsroute/twilio-compat/blob/main/error_codes.md so you can translate existing error-handling conditionals. For retry logic, both providers return the same HTTP status class for the same failure categories (400 for permanent, 429 for rate limit, 500/503 for transient).
What happens to my existing Twilio usage data?
It stays on Twilio. smsroute does not import historical message logs or billing data from Twilio. If you need to retain message logs for compliance or analytics, export from Twilio's Messaging Logs before cancellation (retention varies by Twilio plan, typically 30 days for standard accounts). Going forward, smsroute provides /v1/messages/{id} endpoint for retrieval of individual messages and webhook delivery receipts that your own logging infrastructure can persist.
Is there a cost during migration?
Minimal. During parallel-send validation you pay both providers for the overlap — typically 24-48 hours of ~10% double-sending. On 50k messages/month volume, that overlap costs ~$3-8 total. There is no migration fee from either provider. smsroute's sandbox API is free (no balance consumed) so all pre-production testing costs zero.
Does smsroute support all the same destination countries as Twilio?
smsroute covers 149 countries — effectively the same international coverage as Twilio for SMS. There are a handful of small Pacific and African markets where Twilio has direct connections smsroute routes through aggregator partners, which shows up as slightly longer latency (~300-600ms vs ~150-300ms). For the top 50 destination countries that account for 95%+ of most teams' SMS traffic, smsroute and Twilio route to the same tier-1 carriers. See our full country list at /send-sms-to.