Understanding SMS Latency
SMS delivery involves multiple hops: your application → MacraSMS API → carrier SMSC → recipient device. Each hop adds latency. Understanding where delays occur helps you optimize effectively:
| Hop | Typical Latency | Optimization Opportunity |
|---|---|---|
| App → MacraSMS API | 50-200ms | Connection pooling, geographic proximity |
| MacraSMS Internal Processing | 10-50ms | Pre-validation, cached routing tables |
| MacraSMS → Carrier SMSC | 100-500ms | Direct routes vs aggregators, load balancing |
| Carrier SMSC → Device | 200ms-30s+ | Network congestion, device status, roaming |
| Total End-to-End | 500ms-60s+ | Cumulative effect of all above factors |
Reality Check
You cannot control carrier-side latency. Focus optimization efforts on the first three hops where you have direct influence. Target sub-second API response times as your primary KPI.
Route Selection Strategy
Choose the right route type based on message priority:
- Direct Routes (Priority): Use for OTPs, transactional alerts, time-sensitive notifications. Higher cost but lowest latency (200-800ms average)
- Grey Routes (Budget): Acceptable for marketing campaigns where 5-30 second delay is tolerable. Lower cost but variable latency
- Smart Routing: Configure automatic fallback from direct to grey routes when direct paths are congested or unavailable
- Carrier-Specific Routes: Some carriers perform better on specific routes. Test and maintain per-carrier route preferences
Best Practice
Tag messages with priority levels in your API calls. Our platform automatically selects optimal routes based on priority tags and real-time network conditions.
Message Segmentation
Long messages are split into segments, each sent separately. Optimize segmentation to reduce overhead:
- Stay Under 160 Characters: Single-segment messages avoid reassembly delays at carrier level
- Use GSM 7-bit Encoding: Supports 160 chars/segment vs 70 chars for Unicode. Avoid emojis and special characters unless necessary
- Pre-Segment Long Messages: If multi-segment is unavoidable, pre-segment client-side to reduce server processing time
- Avoid Concatenated Headers: Some carriers add extra bytes for concatenation. Test actual segment counts before deployment
// Character count impact on segments
const gsm7Message = "Your OTP is 123456. Valid for 5 minutes."; // 42 chars = 1 segment
const unicodeMsg = "Your OTP is 🔐123456. Valid ⏱️5 min."; // 38 chars = 1 segment (Unicode)
const longGsm7 = "Your order #12345 has shipped via courier..."; // 161+ chars = 2+ segments
// Always prefer GSM 7-bit when possible
function getEncoding(message) {
const gsm7Regex = /^[A-Za-z0-9 \r\n@£$¥èéùìòÇØøÅåΔ_ΦΓΛΩΠΨΣΘΞÆæßÉ!\"#¤%&'()*+,\-.\/:;<=>?¡ÄÖÑܧ¿äöñüà^{}\\€~]*$/;
return gsm7Regex.test(message) ? 'GSM7' : 'UCS2';
}
Batch Sizing & Throttling
Bulk sends require careful batching to avoid overwhelming systems:
| Volume | Recommended Batch Size | Delay Between Batches |
|---|---|---|
| < 100 messages | Single request | N/A |
| 100 - 1,000 | 100 per request | 100ms |
| 1,000 - 10,000 | 500 per request | 500ms |
| 10,000 - 100,000 | 1,000 per request | 1s |
| > 100,000 | 2,000 per request | 2s + monitor queue depth |
Rate Limit Warning
Exceeding rate limits triggers HTTP 429 responses that add significant retry overhead. Always implement token bucket or leaky bucket algorithms for outbound throttling.
Connection Pooling
Reuse HTTP connections to eliminate TLS handshake overhead:
- HTTP Keep-Alive: Enable persistent connections with keep-alive headers
- Connection Pool Size: Maintain pool size matching your concurrency requirements (typically 10-50 connections)
- Idle Timeout: Set 30-60 second idle timeout to prevent stale connections
- Max Lifetime: Rotate connections every 5-10 minutes to avoid carrier-side timeouts
- Health Checks: Implement periodic ping tests to detect and remove dead connections
Async Processing Patterns
Never block user-facing requests on SMS sends:
- Queue-Based Architecture: Push messages to Redis/RabbitMQ/Kafka queue immediately after validation
- Worker Processes: Dedicated workers consume queue and make API calls independently
- Immediate Acknowledgment: Return 202 Accepted to client instantly, provide message ID for status tracking
- Webhook Notifications: Use webhooks instead of polling for delivery status updates
- Dead Letter Queue: Route failed messages to DLQ for investigation without blocking main queue
// Example: Async send pattern with immediate acknowledgment
app.post('/send-sms', async (req, res) => {
// Validate and enqueue immediately
const messageId = await messageQueue.enqueue({
to: req.body.to,
message: req.body.message,
senderId: req.body.senderId,
priority: req.body.priority || 'normal'
});
// Return immediately - don't wait for API response
res.status(202).json({
message_id: messageId,
status: 'queued',
estimated_delivery: '< 5 seconds'
});
});
// Separate worker process handles actual API calls
worker.on('message', async (msg) => {
try {
const result = await macraSMS.send(msg);
await updateStatus(msg.id, result);
} catch (error) {
await deadLetterQueue.add(msg, error);
}
});
Monitoring Performance
Track these metrics to identify bottlenecks:
- API Response Time: P50, P95, P99 latencies for /send endpoint
- Queue Depth: Number of pending messages in queue (alert if > 1000)
- Error Rate: Percentage of 4xx/5xx responses (alert if > 1%)
- Throughput: Messages sent per second vs target capacity
- DLR Latency: Time between send and delivery report receipt
- Retry Rate: Frequency of retries indicating transient failures
Carrier-Specific Tips
Kenyan carriers have unique characteristics:
| Carrier | Peak Hours | Optimization Tip |
|---|---|---|
| Safaricom | 8-10AM, 5-7PM | Use dedicated OTP route during peak hours. Standard routes experience 2-3x latency |
| Airtel | 12-2PM, 6-8PM | Batch sizes >500 trigger throttling. Stick to 200-300 per batch during peaks |
| Telkom | 9-11AM, 4-6PM | Smaller network = faster off-peak. Schedule non-urgent sends outside peak windows |
Need Performance Tuning Help?
Our engineering team can audit your integration and recommend carrier-specific optimizations.
Request Performance Audit