Tienes una app, un backend o un script que necesita notificar usuarios por SMS. No quieres depender de una plataforma con interfaz de clicks. Quieres código que funcione, en tu stack, desde hoy.
Este tutorial te muestra exactamente cómo enviar SMS desde Python y cómo integrar la API SMS Node.js de SMS Masivos, con ejemplos funcionales que puedes copiar, adaptar y poner en producción en minutos. Si tu stack es PHP, tenemos un tutorial completo para PHP también disponible.
Lo que vas a poder hacer al terminar:
- Enviar un SMS a cualquier número desde código
- Probar en modo sandbox sin afectar producción
- Integrar verificación OTP (2FA) si lo necesitas
- Manejar errores y reintentos de forma robusta
Requisitos previos
Antes de arrancar necesitas:
- Una cuenta en SMS Masivos: Créala aquí. El registro toma menos de 5 minutos.
- Tu API key: La encuentras en el panel bajo Configuración > API. Es un string único vinculado a tu cuenta.
- Python 3.8+ o Node.js 18+ instalado en tu entorno.
No necesitas configurar OAuth, webhooks ni aprobaciones de terceros. La autenticación es un header con tu API key.
Enviar SMS desde Python
Instalar dependencias
Solo necesitas requests. Si ya lo tienes instalado, sáltate este paso.
pip install requestsCódigo completo: enviar un SMS
import requests
API_KEY = "tu_api_key_aqui"
BASE_URL = "https://api.smsmasivos.com.mx"
def send_sms(numbers: str, message: str, country_code: str = "52",
sandbox: int = 0, sender: str = None) -> dict:
payload = {
"numbers": numbers,
"message": message,
"country_code": country_code,
"sandbox": sandbox
}
if sender:
payload["sender"] = sender
response = requests.post(
f"{BASE_URL}/sms/send",
headers={"apikey": API_KEY, "Content-Type": "application/json"},
json=payload
)
response.raise_for_status()
return response.json()
# Envío en modo sandbox
result = send_sms(
numbers="5512345678",
message="Tu pedido #1234 fue confirmado. Entrega estimada: mañana entre 10am y 2pm.",
sandbox=1
)
print(result)Envío a múltiples destinatarios
Pasa los números separados por coma en un solo string:
result = send_sms(
numbers="5512345678,5587654321,5534567890",
message="Recordatorio: Tu cita es mañana a las 3pm. Responde CANCELAR para reagendar.",
sandbox=1
)Respuesta esperada
Una respuesta exitosa devuelve status 200 con un JSON de confirmación. En caso de error (número inválido, créditos insuficientes, API key incorrecta) recibirás un 400 con el detalle del problema.
import requests
from requests.exceptions import HTTPError, ConnectionError, Timeout
def send_sms_safe(numbers: str, message: str, country_code: str = "52") -> dict:
response = None
try:
response = requests.post(
f"{BASE_URL}/sms/send",
headers={"apikey": API_KEY, "Content-Type": "application/json"},
json={"numbers": numbers, "message": message,
"country_code": country_code, "sandbox": 0},
timeout=10
)
response.raise_for_status()
return {"success": True, "data": response.json()}
except HTTPError as e:
status = response.status_code if response is not None else None
return {"success": False, "error": str(e), "status": status}
except (ConnectionError, Timeout) as e:
return {"success": False, "error": str(e), "status": None}
except Exception as e:
return {"success": False, "error": str(e)}Retry con exponential backoff
Para producción, agrega reintentos automáticos ante errores transitorios de red:
import time
def send_sms_with_retry(numbers: str, message: str, max_retries: int = 3) -> dict:
delay = 1.0
for attempt in range(max_retries):
result = send_sms_safe(numbers, message)
if result["success"]:
return result
if result.get("status") is not None and result["status"] < 500:
return result
if attempt < max_retries - 1:
time.sleep(delay)
delay *= 2
return resultEnviar SMS desde Node.js
Node.js 18+ (fetch nativo)
Node 18 incluye fetch de forma nativa. No necesitas instalar nada adicional.
const API_KEY = 'tu_api_key_aqui';
const BASE_URL = 'https://api.smsmasivos.com.mx';
async function sendSMS(numbers, message, options = {}) {
const { countryCode = '52', sandbox = 0, sender } = options;
const payload = { numbers, message, country_code: countryCode, sandbox };
if (sender) payload.sender = sender;
const response = await fetch(`${BASE_URL}/sms/send`, {
method: 'POST',
headers: { 'apikey': API_KEY, 'Content-Type': 'application/json' },
body: JSON.stringify(payload)
});
if (!response.ok) {
const error = await response.json();
throw new Error(`SMS API error ${response.status}: ${JSON.stringify(error)}`);
}
return response.json();
}
sendSMS(
'5512345678',
'Tu código de acceso es: 847291. Válido por 10 minutos.',
{ countryCode: '52', sandbox: 1 }
)
.then(result => console.log('Enviado:', result))
.catch(err => console.error('Error:', err.message));Con axios (Node.js y navegador)
Si ya usas axios en tu proyecto:
const axios = require('axios');
const smsClient = axios.create({
baseURL: 'https://api.smsmasivos.com.mx',
headers: { 'apikey': process.env.SMS_API_KEY, 'Content-Type': 'application/json' },
timeout: 10000
});
async function sendSMS(numbers, message, options = {}) {
const { countryCode = '52', sandbox = 0, sender } = options;
const payload = { numbers, message, country_code: countryCode, sandbox };
if (sender) payload.sender = sender;
const { data } = await smsClient.post('/sms/send', payload);
return data;
}Tip de seguridad: guarda tu API key en una variable de entorno (process.env.SMS_API_KEY), nunca hardcodeada en el código.
Caso de uso: verificación OTP en dos pasos
Si tu app necesita 2FA por SMS, la API incluye endpoints dedicados para generar y validar códigos OTP. Los parámetros del endpoint (expiration_date, reset_code) te dan control sobre la vigencia. Necesitarás gestionar la lógica de expiración y los reintentos en tu código.
Puedes leer más sobre cómo funciona la verificación OTP en nuestra guía de códigos de un solo uso y en la página de producto de verificación OTP.
Python: generar y verificar OTP
def send_otp(phone_number: str, country_code: str = "52") -> dict:
response = requests.post(
f"{BASE_URL}/protected/json/phones/verification/start",
headers={"apikey": API_KEY, "Content-Type": "application/json"},
json={"phone_number": phone_number, "country_code": country_code,
"code_length": 6, "code_type": "numeric"}
)
response.raise_for_status()
return response.json()
def verify_otp(phone_number: str, code: str) -> dict:
response = requests.post(
f"{BASE_URL}/protected/json/phones/verification/check",
headers={"apikey": API_KEY, "Content-Type": "application/json"},
json={"phone_number": phone_number, "verification_code": code}
)
response.raise_for_status()
return response.json()
otp_response = send_otp("5512345678")
print("OTP enviado:", otp_response)
user_code = input("Ingresa el código que recibiste: ")
verification = verify_otp("5512345678", user_code)
print("Resultado:", verification)Node.js: generar y verificar OTP
async function sendOTP(phoneNumber, countryCode = '52') {
const response = await fetch(`${BASE_URL}/protected/json/phones/verification/start`, {
method: 'POST',
headers: { 'apikey': API_KEY, 'Content-Type': 'application/json' },
body: JSON.stringify({ phone_number: phoneNumber, country_code: countryCode,
code_length: 6, code_type: 'numeric' })
});
if (!response.ok) throw new Error(`OTP start error ${response.status}`);
return response.json();
}
async function verifyOTP(phoneNumber, code) {
const response = await fetch(`${BASE_URL}/protected/json/phones/verification/check`, {
method: 'POST',
headers: { 'apikey': API_KEY, 'Content-Type': 'application/json' },
body: JSON.stringify({ phone_number: phoneNumber, verification_code: code })
});
if (!response.ok) throw new Error(`OTP verify error ${response.status}`);
return response.json();
}Errores comunes y cómo resolverlos
| Error | Endpoint | Causa probable | Solución |
|---|---|---|---|
400 Bad Request | /sms/send | Parámetro faltante o formato incorrecto | Revisa que numbers, message y country_code estén presentes |
400 número inválido | /sms/send | Número con formato incorrecto | Usa solo dígitos sin espacios, guiones ni +52 |
401 Unauthorized | /credits/consult | API key incorrecta o inactiva | Verifica la key en el panel > Configuración > API |
| Timeout | Cualquier endpoint | Red lenta o API temporalmente lenta | Agrega timeout=10 en requests / axios, implementa retry con backoff |
| Créditos insuficientes | /sms/send | Tu cuenta no tiene saldo | Recarga créditos desde el panel o contacta soporte |
Sandbox siempre primero: antes de enviar a números reales, usa "sandbox": 1. El modo sandbox simula el envío en entorno de pruebas para que puedas verificar que el payload es correcto.
FAQ
¿Cuántos números puedo enviar en una sola llamada?
Puedes pasar múltiples números separados por coma en el campo numbers. Para campañas masivas (miles de contactos) revisa los límites de rate de tu plan.
¿El SMS llega desde un número fijo o un código corto?
Por defecto llega desde un código corto. Si tienes contratado un remitente personalizado, pásalo en el parámetro sender de send_sms() (Python) o en options.sender de sendSMS() (Node.js).
¿Cómo sé si el SMS llegó al destinatario?
Configura un webhook en el panel para recibir notificaciones de eventos de entrega. También puedes consultar el historial en el panel o via /reports/generate (requiere start_date y end_date).
¿Funciona para envíos fuera de México?
Sí, cambia country_code al código del país destino (ej: "1" para EE.UU., "52" para México).
¿Qué longitud máxima tiene un SMS?
160 caracteres por segmento en ASCII estándar. Si tu mensaje incluye caracteres especiales (acentos, ñ) el límite es 70 caracteres por segmento. La API concatena segmentos automáticamente.
Próximos pasos
Con estos bloques de código tienes todo para integrar SMS en tu app sin depender de clicks en un dashboard.
Para entender todos los endpoints disponibles (agendas, webhooks, subcuentas y más), consulta la documentación completa de la API de SMS Masivos. Si tu stack incluye más de un lenguaje, también tenemos el tutorial para PHP con el mismo nivel de detalle.
Crea tu cuenta y obtén tu API key:




