Referencia de la API de SMS masivos

La API REST de BulkSMSOnline envía mensajes de texto a 227 países desde tu propio software, con los mismos precios, reglas de segmentos, programación y reembolsos que la consola. Las solicitudes y respuestas son JSON sobre HTTPS, y cada estado de entrega final puede enviarse a tu servidor como un webhook firmado.

URL basehttps://bulksmsonline.cc/api/v1

Resumen

Usa la API cuando los mensajes son activados por tus propios sistemas: actualizaciones de pedidos de una tienda, códigos de un formulario de inicio de sesión, recordatorios de una herramienta de reservas. Una solicitud a POST /v1/messages con hasta 10,000 números crea una campaña, carga el saldo y pone en cola cada mensaje. Las campañas creadas por la API también aparecen en la consola con sus informes de entrega.

  • Todos los montos son en dólares estadounidenses como cadenas con cinco decimales, por ejemplo "0.00534".
  • Los números de teléfono son internacionales, con o sin el + inicial; se eliminan espacios y guiones.
  • Las horas son ISO 8601 en UTC, por ejemplo 2026-12-01T09:00:00Z.
  • Los IDs de mensaje y campaña se ven como MSG-XXXX-XXXX-XXXX y CMP-XXXX-XXXX-XXXX.

Autenticación

Crea una clave API en API en la consola (las claves se activan después de tu primera recarga). Envíala en el encabezado Authorization de cada solicitud. Una clave se ve como bso_api_ seguido de 36 caracteres; se muestra una vez, así que guárdala como secreto en tu servidor. Crear una nueva clave reemplaza la anterior de inmediato.

Authorization: Bearer bso_api_0123456789abcdef0123456789abcdef0123
Content-Type: application/json

Límites

Solicitudes300 por minuto por clave; más responde 429 rate_limited
Números por solicitud10,000
Texto del mensaje2,000 caracteres como están escritos, hasta 10 segmentos
Programaciónsend_at desde 5 minutos hasta 60 días adelante
Ritmo de envío400 mensajes por minuto por cola
Reintentos de webhook9 intentos en aproximadamente 22 horas

Enviar mensajes

POST/v1/messages

Envía un mensaje de texto a uno o más números. El costo total se deduce del saldo cuando se acepta la solicitud; cada mensaje que no se pueda entregar se reembolsa después. La respuesta tiene estado 201 y la campaña con cada mensaje.

CampoDescripción
toobligatorioUn número o un array de hasta 10,000 números en formato internacional. Los números repetidos se envían una vez; los ilegibles se listan en skipped.
textobligatorioEl mensaje, hasta 2,000 caracteres y 10 segmentos. El texto GSM-7 tiene 160 caracteres por SMS, Unicode 70.
send_atopcionalHora ISO 8601 para programar el envío, de 5 minutos a 60 días adelante. Omítelo para enviar ahora.
opt_outopcionaltrue añade tu línea de exclusión voluntaria de la configuración de la consola en una nueva línea al final.
client_refopcionalTu propia referencia, hasta 64 letras, dígitos y _ . : -. Se devuelve en informes y webhooks.
curl -X POST https://bulksmsonline.cc/api/v1/messages \
  -H "Authorization: Bearer $BSO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"to":["+447700900412","+33612345678"],"text":"Order 4521 has shipped. Track it at shop.example/t/4521","client_ref":"order-4521"}'
HTTP/1.1 201 Created
{
  "id": "CMP-7KQ2-M9XA-4HTD",
  "status": "sending",
  "channel": "api",
  "recipients": 2,
  "segments": 1,
  "encoding": "GSM-7",
  "cost": "0.02158",
  "refunded": "0.00000",
  "client_ref": "order-4521",
  "send_at": null,
  "counts": { "queued": 2, "submitted": 0, "delivered": 0, "failed": 0, "canceled": 0 },
  "skipped": [],
  "balance": "118.43120",
  "messages": [
    { "id": "MSG-3RTW-81KD-ZQ0P", "to": "+447700900412", "country": "GB", "segments": 1, "price": "0.00834", "status": "queued" },
    { "id": "MSG-5HNA-4C2E-7YVB", "to": "+33612345678", "country": "FR", "segments": 1, "price": "0.01324", "status": "queued" }
  ]
}

Cotizar un envío

POST/v1/quote

Toma el mismo cuerpo que /v1/messages y devuelve el costo exacto, segmentos y codificación sin enviar ni cobrar nada. Úsalo para mostrar un precio a tus usuarios antes de que confirmen.

{
  "recipients": 2,
  "skipped": [],
  "segments": 1,
  "encoding": "GSM-7",
  "cost": "0.02158",
  "balance": "118.45278",
  "countries": [ { "country": "GB", "recipients": 1, "cost": "0.00834" }, { "country": "FR", "recipients": 1, "cost": "0.01324" } ]
}

Saldo

GET/v1/balance

{ "balance": "118.43120", "currency": "USD" }

Tarifas

GET/v1/rates

GET/v1/rates/{country}

El precio de un segmento SMS para cada destino, o para un código de país ISO de dos letras. Estos dos endpoints son públicos y no requieren clave.

{ "country": "DE", "name": "Germany", "calling_code": "+49", "price": "0.01544", "currency": "USD", "per": "segment" }

Estado del mensaje

GET/v1/messages/{id}

{
  "id": "MSG-3RTW-81KD-ZQ0P",
  "campaign_id": "CMP-7KQ2-M9XA-4HTD",
  "to": "+447700900412",
  "country": "GB",
  "segments": 1,
  "price": "0.00834",
  "status": "delivered",
  "error": null,
  "created_at": "2026-11-07T09:00:02Z",
  "submitted_at": "2026-11-07T09:00:03Z",
  "final_at": "2026-11-07T09:00:08Z"
}

Campañas

GET/v1/campaigns/{id}

GET/v1/campaigns/{id}/messages?offset=0&limit=100

El primero devuelve la campaña con sus conteos por estado; el segundo pagina sus mensajes, hasta 1,000 por página. Las campañas enviadas desde la consola se pueden leer de la misma manera con el id mostrado en su página.

Cancelar un envío programado

POST/v1/campaigns/{id}/cancel

Detiene una campaña que se creó con send_at y aún no ha comenzado. Cada mensaje se cancela y el costo total vuelve al saldo. Una campaña que ya se está enviando responde 409 not_scheduled.

Webhooks de entrega

Añade una URL HTTPS en API en la consola para recibir cada estado final (entregado o fallido) como una solicitud POST. Responde con cualquier estado 2xx en 8 segundos. Otras respuestas y timeouts se reintentan después de 20 segundos, 1, 5 y 15 minutos, luego 1, 3, 6 y 12 horas, 9 intentos en total. El mismo evento puede llegar dos veces, así que usa event_id para procesarlo una vez.

POST https://your-server.example/sms-status
Content-Type: application/json
X-BulkSMSOnline-Signature: t=1794042008,v1=5f0c2a...e91b

{
  "type": "message.status",
  "event_id": "ev_8f1c22d0a4b97e63c1f0",
  "sent_at": "2026-11-07T09:00:09Z",
  "data": {
    "message_id": "MSG-3RTW-81KD-ZQ0P",
    "campaign_id": "CMP-7KQ2-M9XA-4HTD",
    "to": "+447700900412",
    "country": "GB",
    "status": "delivered",
    "error": null,
    "segments": 1,
    "price": "0.00834",
    "currency": "USD",
    "client_ref": "order-4521",
    "final_at": "2026-11-07T09:00:08Z"
  }
}

Verificar la firma

El encabezado contiene el tiempo Unix t y la firma v1 = HMAC-SHA256 de la cadena "t.raw_body" con tu secreto de webhook, en hex. Calcúlalo sobre el cuerpo sin procesar antes de analizar el JSON, compara en tiempo constante y rechaza eventos de más de cinco minutos.

import crypto from "node:crypto";

export function verify(rawBody, header, secret) {
  const m = /^t=(\d+),v1=([0-9a-f]{64})$/.exec(header || "");
  if (!m || Math.abs(Date.now() / 1000 - Number(m[1])) > 300) return false;
  const want = crypto.createHmac("sha256", secret).update(m[1] + "." + rawBody).digest("hex");
  return crypto.timingSafeEqual(Buffer.from(want), Buffer.from(m[2]));
}

Estados

queuedAceptado y esperando salir de la cola
submittedEntregado a la red de destino
deliveredConfirmado por el dispositivo (final)
failedNo se pudo entregar; el precio se reembolsa (final)
canceledParte de una campaña programada cancelada antes del envío (final)

El estado de la campaña es scheduled, sending, complete o canceled.

Errores

Los errores responden con un estado HTTP y un cuerpo JSON: { "error": { "code": "insufficient_balance", "message": "..." } }. No se cobra nada cuando una solicitud responde con un error.

HTTPCódigoSignificado
400invalid_requestFalta un campo o no es válido; el mensaje indica cuál.
400invalid_jsonEl cuerpo no es JSON.
400no_valid_numbersNo se pudieron leer ninguno de los números.
401unauthorizedLa clave falta, está malformada o ha sido reemplazada.
402insufficient_balanceEl saldo no cubre el costo; recarga y envía de nuevo.
404not_foundID o endpoint desconocido.
409not_scheduledSolo se puede cancelar una campaña programada que no haya comenzado.
413too_many_recipientsMás de 10,000 números en una solicitud.
429rate_limitedMás de 300 solicitudes en un minuto.
500server_errorReintenta la solicitud; no se cobró nada.

Obtén una clave API en la consola

Crea una cuenta, añade tu primera recarga y genera una clave en API. Tu primera solicitud puede enviarse un minuto después.

Crear cuenta
Referencia de la API de Bulk SMS: Endpoints REST y Webhooks | BulkSMSOnline