Why Use SMSRoute for Node.js SMS Delivery?
SMSRoute routes messages through direct carrier interconnects across 149 countries. Typical delivery times stay under 2 seconds for tier-1 markets — the US, UK, Germany, India, and Brazil. You pay per message with transparent real-time pricing starting at $0.004 per SMS, with no monthly minimums or hidden fees.
The API supports alphanumeric sender IDs (where local regulation permits), two-way SMS via dedicated long codes, and HMAC-signed delivery receipts. Fund your account with cryptocurrency — BTC, ETH, USDT, LTC, XMR, or SOL — and start sending without credit checks or KYC delays. For production systems, you get 99.9% API uptime and automated failover across multiple data centres.
How to Send SMS with Node.js — Step by Step
Install axios, or use Node's built-in fetch (available from Node 18 onwards). Create an HTTP client with your API key as a Bearer token, then POST structured JSON to the send endpoint. Here is a complete CommonJS example:
const axios = require('axios');
require('dotenv').config();
const client = axios.create({
baseURL: 'https://api.smsroute.cc/v1',
headers: { 'Authorization': `Bearer ${process.env.SMSROUTE_API_KEY}` }
});
async function sendSms() {
try {
const { data } = await client.post('/messages', {
to: '+1234567890',
from: 'MyApp',
body: 'Hello from SMSRoute!'
});
console.log('Message ID:', data.id, 'Status:', data.status);
} catch (err) {
console.error('Send error:', err.response?.data || err.message);
}
}
sendSms();
The same logic in ESM uses import instead of require:
import axios from 'axios';
import dotenv from 'dotenv';
dotenv.config();
const client = axios.create({
baseURL: 'https://api.smsroute.cc/v1',
headers: { 'Authorization': `Bearer ${process.env.SMSROUTE_API_KEY}` }
});
async function sendSms() {
const { data } = await client.post('/messages', {
to: '+1234567890',
from: 'MyApp',
body: 'Hello from SMSRoute!'
});
console.log('Message ID:', data.id);
}
sendSms();
The axios.create() factory returns a pre-configured client with your base URL and auth header
baked into every request. This keeps your code DRY and avoids accidentally sending unauthenticated requests.
Store your API key in an environment variable (SMSROUTE_API_KEY) — never hard-code it.
Send Bulk SMS from Node.js
The /messages/bulk endpoint accepts an array of up to 10,000 messages in a single POST. Each
message object can carry a different recipient, sender ID, and body. The response returns an array of message
objects with unique IDs that you can store and match against delivery receipts later.
const axios = require('axios');
require('dotenv').config();
const client = axios.create({
baseURL: 'https://api.smsroute.cc/v1',
headers: { 'Authorization': `Bearer ${process.env.SMSROUTE_API_KEY}` }
});
async function sendBulk() {
const { data } = await client.post('/messages/bulk', {
messages: [
{ to: '+1234567890', from: 'MyApp', body: 'Order confirmed!' },
{ to: '+0987654321', from: 'MyApp', body: 'Your appointment is tomorrow.' },
{ to: '+5555555555', from: 'MyApp', body: 'Welcome aboard!' }
]
});
console.log('Bulk result:', data);
}
sendBulk();
Each returned message object includes id, status (typically "accepted"),
price_usd, and a created_at timestamp. Store these IDs in your database so you can
reconcile them against delivery receipt webhooks later. For campaigns exceeding 10,000 recipients, batch your
requests into chunks of 1,000–5,000 to avoid timeouts and keep memory usage predictable.
Handle Delivery Receipts with Express Webhooks
Instead of polling the API for delivery status, configure a webhook URL in the SMSRoute dashboard. SMSRoute
sends a POST request whenever a message is delivered, undelivered, or fails. Each request includes an
X-Smsroute-Signature header for verification. You must preserve the raw request body (as a Buffer)
before any JSON body-parser middleware runs.
const express = require('express');
const crypto = require('crypto');
require('dotenv').config();
const app = express();
const webhookSecret = process.env.SMSROUTE_WEBHOOK_SECRET;
app.use(express.raw({ type: 'application/json' }));
app.post('/webhooks/smsroute', (req, res) => {
const signature = req.headers['x-smsroute-signature'];
if (!signature) return res.status(403).send('Forbidden');
const expected = crypto
.createHmac('sha256', webhookSecret)
.update(req.body)
.digest('hex');
try {
if (!crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected))) {
return res.status(403).send('Forbidden');
}
} catch { return res.status(403).send('Forbidden'); }
const event = JSON.parse(req.body.toString());
console.log('Delivery event:', event.data.status);
// event.data = { id, to, status, delivered_at, ... }
res.json({ success: true });
});
app.listen(3000);
Deploy this endpoint (e.g. https://yourapp.com/webhooks/smsroute) and register it in the SMSRoute
dashboard. The payload includes the message ID, recipient phone, current delivery status, and a timestamp. A
common pitfall: if your framework parses JSON before this middleware, req.body will be an object
instead of a Buffer and the HMAC verification will fail. Always register express.raw() before any
other body parser.
Implement Retry Logic and Idempotency Keys
Network hiccups and rate limits (HTTP 429) are inevitable in production. Rather than wrapping every API call in
retry logic manually, use an axios response interceptor. It centralises retry decisions — retry on 5xx and 429
only, never on 4xx client errors. Combine this with an Idempotency-Key header to guarantee that
retries do not produce duplicate SMS charges.
const axios = require('axios');
const { randomUUID } = require('crypto');
require('dotenv').config();
const client = axios.create({
baseURL: 'https://api.smsroute.cc/v1',
headers: { 'Authorization': `Bearer ${process.env.SMSROUTE_API_KEY}` },
timeout: 10000
});
let retries = 0;
client.interceptors.response.use(
r => r,
async err => {
const cfg = err.config;
if (!cfg || retries >= 3) return Promise.reject(err);
if (err.response?.status >= 500 || err.response?.status === 429) {
retries++;
const delay = err.response?.headers['retry-after']
? parseInt(err.response.headers['retry-after']) * 1000
: Math.pow(2, retries) * 1000;
await new Promise(r => setTimeout(r, delay));
return client(cfg);
}
return Promise.reject(err);
}
);
async function sendWithIdempotency() {
const key = randomUUID();
const { data } = await client.post('/messages', {
to: '+1234567890',
from: 'MyApp',
body: 'Idempotent SMS'
}, { headers: { 'Idempotency-Key': key } });
console.log('Sent:', data.id);
}
The Idempotency-Key header tells SMSRoute to return the same response for repeated requests with
the same key, preventing duplicate SMS. Generate the key with crypto.randomUUID() and store it
alongside the message ID in your database for auditing.
TypeScript Types for the Node.js SMS API
SMSRoute does not publish an npm package with built-in TypeScript types, but defining your own interfaces is straightforward. This gives you full control and editor autocomplete without depending on a third-party type package.
// types/smsroute.ts
export interface SmsRouteMessage {
id: string;
status: 'accepted' | 'delivered' | 'undelivered' | 'failed';
to: string;
from: string;
body: string;
segments: number;
price_usd: number;
created_at: string;
}
export interface SmsRouteError {
code: string;
message: string;
status?: number;
}
Create a typed wrapper that returns proper response types without casting at every call site:
// services/smsroute.ts
import axios from 'axios';
import type { SmsRouteMessage, SmsRouteError } from '../types/smsroute';
export class SmsRouteClient {
private client = axios.create({
baseURL: 'https://api.smsroute.cc/v1',
headers: { 'Authorization': `Bearer ${process.env.SMSROUTE_API_KEY!}` },
timeout: 10000
});
async send(to: string, from: string, body: string, idempotencyKey?: string) {
const headers: Record<string, string> = {};
if (idempotencyKey) headers['Idempotency-Key'] = idempotencyKey;
const { data } = await this.client.post('/messages', { to, from, body }, { headers });
return data as SmsRouteMessage;
}
async sendBulk(messages: Array<{ to: string; from: string; body: string }>) {
const { data } = await this.client.post('/messages/bulk', { messages });
return data.messages as SmsRouteMessage[];
}
}
Test Your Integration in Sandbox Mode
Use API keys prefixed with sk_test_ to send free test messages. The sandbox supports magic phone
numbers that return specific outcomes:
- +1234567890 — Always succeeds with status "delivered"
- +9999999999 — Always fails with status "failed"
- +0000000000 — Triggers a rate limit error (429)
Write tests that exercise every code path — successful delivery, failed delivery, and rate-limited responses —
before switching to a live sk_live_ key. This confirms your retry logic, error handlers, and
webhook processing all work correctly in production.
const axios = require('axios');
require('dotenv').config();
async function testSandbox() {
const client = axios.create({
baseURL: 'https://api.smsroute.cc/v1',
headers: { 'Authorization': `Bearer ${process.env.SMSROUTE_API_KEY}` }
});
try {
const { data } = await client.post('/messages', {
to: '+1234567890',
from: 'TestApp',
body: 'Sandbox test'
});
console.log('PASS:', data.status); // "delivered"
} catch (err) {
console.error('FAIL:', err.response?.data || err.message);
}
}
Frequently Asked Questions
Do I need an SMSRoute npm package to send SMS from Node.js?
No. SMSRoute uses a standard REST API. Install axios (or use Node's built-in fetch) and make HTTP
requests directly to https://api.smsroute.cc/v1. No proprietary SDK required.
Can I use Node.js fetch instead of axios with the SMS API?
Yes. Node.js 18+ includes a global fetch function compatible with SMSRoute. Axios provides interceptors, automatic retries, and timeout handling, but fetch works for simple use cases.
How do I verify SMS webhook signatures in Node.js?
Extract the X-Smsroute-Signature header, compute HMAC-SHA256 of the raw request body using
your webhook secret, and compare using crypto.timingSafeEqual to prevent timing attacks.
What is an idempotency key and why do I need one?
An idempotency key is a unique string sent in the Idempotency-Key header. If a request times
out and you retry with the same key, SMSRoute returns the original response and does not send a duplicate
SMS.
How do I handle rate limits when sending SMS from Node.js?
On 429 (rate limit) or 5xx errors, read the Retry-After header and use an axios interceptor
to retry with exponential backoff. Never retry on 4xx client errors.
What does the sk_test_ API key prefix do?
Sandbox keys (sk_test_) let you test integration for free without sending real SMS or
charging your account. Use magic numbers like +1234567890 to simulate different delivery
outcomes.
How many countries does SMSRoute support for Node.js SMS delivery?
SMSRoute routes messages to 149 countries through direct carrier interconnects, with typical delivery times under 2 seconds for tier-1 markets like the US, UK, Germany, and India.
Can I send bulk SMS from Node.js with SMSRoute?
Yes. Use the /messages/bulk endpoint to send up to 10,000 messages in a single API request.
Each message can have a different recipient, sender ID, and body.
Related Guides and Resources
Explore more integration patterns and product documentation:
- SMS pricing and country coverage — per-country rates and route quality metrics
- Full REST API reference — all endpoints, parameters, and response schemas
- Python SMS integration guide — similar patterns for Django, Flask, and async Python
- Two-factor authentication with SMS — OTP delivery best practices and timing
- Migrating from Twilio to SMSRoute — endpoint mapping and switchover checklist