Envío
Envía SMS masivos con POST /v1/batches
/v1/batchesEnvía a hasta 10,000 números en una solicitud. El costo total se deduce del saldo de una vez.
Cuerpo de la solicitud
- toarray of stringsopcional
Números para un texto compartido. Usa to con text, o messages.
- textstringopcional
El texto compartido, requerido con to.
- messagesarray of objectsopcional
Un {to, text} por destinatario, para textos personalizados.
- namestringopcional
Una etiqueta para la consola, hasta 100 caracteres.
- referencestringopcional
Tu propio id, hasta 100 caracteres, devuelto en el lote y cada mensaje.
- send_atstringopcional
Hora ISO 8601, de 2 minutos a 45 días adelante.
curl https://bulksmsapi.cc/v1/batches \
-H "Authorization: Bearer $BULKSMSAPI_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: spring-sale-0412" \
-d '{
"name": "Spring sale",
"text": "Spring sale at Northwind: 20% off until Sunday. Opt out: northwind.example/sms",
"to": [
"+14155550123",
"+447400123456",
"+4915123456789"
]
}'const res = await fetch("https://bulksmsapi.cc/v1/batches", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.BULKSMSAPI_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": "spring-sale-0412",
},
body: JSON.stringify({
name: "Spring sale",
text: "Spring sale at Northwind: 20% off until Sunday. Opt out: northwind.example/sms",
to: ["+14155550123", "+447400123456", "+4915123456789"],
}),
});
const batch = await res.json();
console.log(res.status, batch.id, batch.recipients, batch.cost);import json, os, urllib.request
req = urllib.request.Request(
"https://bulksmsapi.cc/v1/batches",
data=json.dumps({
"name": "Spring sale",
"text": "Spring sale at Northwind: 20% off until Sunday. Opt out: northwind.example/sms",
"to": ["+14155550123", "+447400123456", "+4915123456789"],
}).encode(),
headers={
"Authorization": "Bearer " + os.environ["BULKSMSAPI_KEY"],
"Content-Type": "application/json",
"Idempotency-Key": "spring-sale-0412",
"User-Agent": "northwind-app/1.0",
},
method="POST",
)
with urllib.request.urlopen(req) as res:
batch = json.load(res)
print(res.status, batch["id"], batch["recipients"], batch["cost"])<?php
$ch = curl_init("https://bulksmsapi.cc/v1/batches");
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"Authorization: Bearer " . getenv("BULKSMSAPI_KEY"),
"Content-Type: application/json",
"Idempotency-Key: spring-sale-0412",
],
CURLOPT_POSTFIELDS => json_encode([
"name" => "Spring sale",
"text" => "Spring sale at Northwind: 20% off until Sunday. Opt out: northwind.example/sms",
"to" => ["+14155550123", "+447400123456", "+4915123456789"],
]),
]);
$batch = json_decode(curl_exec($ch), true);
echo $batch["id"], " ", $batch["recipients"], " ", $batch["cost"], "\n";package main
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"os"
)
func main() {
body, _ := json.Marshal(map[string]any{
"name": "Spring sale",
"text": "Spring sale at Northwind: 20% off until Sunday. Opt out: northwind.example/sms",
"to": []string{"+14155550123", "+447400123456", "+4915123456789"},
})
req, _ := http.NewRequest("POST", "https://bulksmsapi.cc/v1/batches", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer "+os.Getenv("BULKSMSAPI_KEY"))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Idempotency-Key", "spring-sale-0412")
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
var batch map[string]any
json.NewDecoder(res.Body).Decode(&batch)
fmt.Println(res.StatusCode, batch["id"], batch["recipients"], batch["cost"])
}{
"id": "bat_6TQ2W8M1ZK4N",
"object": "batch",
"name": "Spring sale",
"status": "queued",
"source": "api",
"encoding": "GSM-7",
"recipients": 3,
"max_segments": 1,
"cost": "0.02741",
"refunded": "0.00000",
"currency": "USD",
"counts": { "scheduled": 0, "queued": 3, "sent": 0, "delivered": 0, "failed": 0, "canceled": 0 },
"send_at": null,
"reference": null,
"created_at": "2026-11-07T09:31:40.102Z",
"completed_at": null,
"set_aside": []
}Números que se excluyen
Cada número se limpia y tarifa individualmente. Los números que no se pueden enviar se excluyen del lote, no se cobran, y se listan en set_aside con un motivo:
| reason | Significado |
|---|---|
| invalid_number | No se puede leer como + y 7 a 15 dígitos después de limpiar. |
| duplicate | El mismo número apareció antes en la solicitud. |
| unsupported_country | El número no pertenece a un país con tarifa. |
| too_long | Su texto personalizado necesita más de 10 segmentos. |
| empty_text | Su texto personalizado está vacío. |
| suppressed | El número está en tu lista de no enviar. |
Si no queda ningún número, la solicitud falla con el código del primer motivo (por ejemplo invalid_number) y no se cobra nada.
Texto personalizado por número
Usa messages para dar a cada destinatario su propio texto: nombres, horas, cantidades. Cada texto se cuenta y tarifa individualmente, así que un lote puede mezclar mensajes de uno y dos segmentos.
curl https://bulksmsapi.cc/v1/batches \
-H "Authorization: Bearer $BULKSMSAPI_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Appointment reminders, 14 May",
"send_at": "2026-05-13T16:00:00Z",
"messages": [
{ "to": "+14155550123", "text": "Hi Ana, your dental check-up is tomorrow at 09:30." },
{ "to": "+14155550178", "text": "Hi Sam, your dental check-up is tomorrow at 11:00." }
]
}'Programación
Con send_at el lote y sus mensajes esperan en estado scheduled y pasan a la cola en ese minuto. El costo se deduce cuando se acepta la solicitud, así que un lote programado siempre tiene sus fondos. Un lote programado se puede cancelar hasta que comience.
/v1/batches/{id}/cancelCancela un lote programado: cada mensaje se cancela y el costo total regresa al saldo como una línea de reembolso. Un lote que ha comenzado responde 409 not_cancelable.
curl -X POST https://bulksmsapi.cc/v1/batches/bat_6TQ2W8M1ZK4N/cancel \
-H "Authorization: Bearer $BULKSMSAPI_KEY"Recuperar y listar lotes
/v1/batches/{id}El lote con conteos en vivo por estado. Consúltalo para ver el progreso o usa webhooks.
/v1/batchesLotes de la cuenta, los más recientes primero, con límite (1 a 100) y starting_after (un ID bat_).
Campos del lote
- idstring
bat_ seguido de 12 caracteres.
- statusstring
programado, en cola, enviando, completado o cancelado.
- sourcestring
api o consola.
- encodingstring
GSM-7, o UCS-2 si algún texto requiere Unicode.
- recipientsinteger
Números aceptados en el lote.
- max_segmentsinteger
Mayor conteo de segmentos entre los textos.
- coststring
Total deducido del saldo, USD.
- refundedstring
Total reembolsado hasta ahora por mensajes fallidos o cancelados.
- countsobject
Mensajes por estado: programado, en cola, enviado, entregado, fallido, cancelado.
- send_atstring or null
Hora programada, si aplica.
- completed_atstring or null
Cuando el último mensaje alcanzó un estado final.
Listas más grandes
Divide listas de más de 10,000 números en varias solicitudes, cada una con su propia Idempotency-Key (por ejemplo, el ID de la lista y el número de parte). Los mensajes se entregan a la red en orden a un ritmo de hasta 1,200 por minuto por cuenta, por lo que una campaña de 50,000 números estará en camino en menos de una hora. La consola acepta 50,000 números por campaña desde un pegado o archivo CSV.