---
title: "Enviar SMS por API: ejemplos en Python, Node.js, PHP y cURL"
description: "Envíe SMS por API en Python, Node.js, PHP y cURL: envíos individuales y masivos, Unicode y árabe, conteo de segmentos, webhooks de entrega y errores."
canonical: "https://omnichannels.ai/es/blog/send-sms-api-examples/"
language: "es"
updated: "2026-10-16"
publisher: "BroadNet Technologies"
brand: "OmniChannels.ai"
category: "integration"
primaryQuery: "enviar sms api python"
keywords: ["enviar sms python", "ejemplo api sms node.js", "api sms php", "api sms curl", "api sms unicode árabe", "webhook de informe de entrega sms", "ejemplo api sms masivos"]
published: "2026-10-16"
author: "OmniChannels.ai Editorial Team"
reviewer: "BroadNet Messaging Solutions Team"
readingMinutes: 10
answer: "Para enviar un SMS con la API de OmniChannels.ai, haga un POST autenticado a https://api.broadnet.me/sms/sendSms con un cuerpo JSON que contenga senderId, destinationNumber, message y messageType. Use TEXT para texto GSM-7 y UNICODE para árabe o emojis. Una respuesta 200 devuelve el ID del mensaje como texto sin formato, y el estado final de entrega llega después a su webhook."
takeaways: ["POST /sms/sendSms necesita cuatro campos: senderId, destinationNumber, message y messageType; el cuerpo de la respuesta es el ID del mensaje como texto sin formato, no JSON.", "Establezca messageType en UNICODE para árabe, emojis o cualquier carácter fuera de GSM-7; un solo carácter así convierte todo el mensaje en Unicode y acorta cada segmento.", "POST /sms/identify cuenta los caracteres y los segmentos antes de enviar, para que pueda prever cuántos segmentos usa un mensaje.", "La entrega es asíncrona: un 200 significa aceptado, y DELIVERED, FAILED o REJECTED llega después a su webhook.", "La API no tiene clave de idempotencia, así que registre cada envío antes de hacerlo y compruebe el estado tras un tiempo de espera agotado en lugar de reenviar."]
faq: 
  - q: "¿Cómo envío un SMS con Python?"
    a: "Use la librería requests para hacer un POST JSON a https://api.broadnet.me/sms/sendSms con una cabecera Authorization: Bearer y los campos senderId, destinationNumber, message y messageType. Lea el token de una variable de entorno. El cuerpo de una respuesta 200 es el ID del mensaje como texto sin formato, así que use r.text en lugar de r.json()."
  - q: "¿Qué formato de número de teléfono espera la API de SMS?"
    a: "Prefijo del país más número nacional, solo dígitos, sin signo más, sin prefijo 00, sin espacios ni cero inicial. Por ejemplo, 447700900123 es válido, mientras que +44 7700 900123 y 07700900123 no lo son. Guarde los números en formato E.164 en su propia base de datos y quite el signo más al construir la petición."
  - q: "¿Cómo envío SMS en árabe por la API?"
    a: "Establezca messageType en UNICODE y envíe el texto árabe en el campo message en UTF-8. Los mensajes Unicode admiten 70 caracteres en un segmento único y 67 por segmento al dividirse, frente a 160 y 153 en GSM-7. Pruebe en teléfonos reales y llame antes a /sms/identify para ver el número de segmentos."
  - q: "¿Cómo obtengo los informes de entrega?"
    a: "Los estados se envían a la URL del webhook configurada en su cuenta. Los mensajes pasan por SUBMITTED y SENT hasta DELIVERED, FAILED o REJECTED. Su endpoint debe guardar el evento sin procesar, responder 2xx con rapidez y procesar de forma asíncrona. Si se perdieron eventos, POST /sms/resendDlr puede volver a enviar los informes de entrega de un intervalo de fechas."
  - q: "¿Cómo envío SMS a muchos números a la vez?"
    a: "Use POST /sms/sendbulk, una petición multipart con un destinationNumberFile (CSV o TXT) y una cadena JSON bulkRequest que contenga senderId, message y messageType. La respuesta indica cuántos números eran válidos, no válidos o duplicados, y devuelve un bulkSessionId que puede guardar con su campaña."
  - q: "¿Necesito registrar un sender ID?"
    a: "En muchos países, sí. Algunos exigen un sender ID alfanumérico aprobado y otros requieren identificadores del regulador o de plantilla, que la API acepta en campos opcionales como peId y templateId. Los requisitos dependen de cada país, así que consulte la página de sender ID de su destino; nuestro equipo puede encargarse del registro con usted."
sources: 
  - title: "3GPP TS 23.040: Technical realization of the Short Message Service (SMS)"
    url: "https://www.3gpp.org/dynareport?code=23040"
  - title: "ITU-T E.164: The international public telecommunication numbering plan"
    url: "https://www.itu.int/rec/T-REC-E.164"
  - title: "RFC 9110: HTTP Semantics"
    url: "https://www.rfc-editor.org/rfc/rfc9110"
---

# Enviar SMS por API: ejemplos en Python, Node.js, PHP y cURL

Canonical: https://omnichannels.ai/es/blog/send-sms-api-examples/
Language: es
Publisher: OmniChannels.ai by BroadNet Technologies

Esta guía está dirigida a los desarrolladores que necesitan enviar SMS desde una aplicación y quieren código funcional en Python, Node.js, PHP o cURL. Abarca un envío individual, Unicode y árabe, el conteo de segmentos, la carga masiva, los webhooks de estado de entrega y la gestión de errores, usando únicamente endpoints y campos de nuestra [referencia de la API de SMS](/developers/sms/) y de la especificación OpenAPI.

## ¿Cómo envío un SMS con la API?

Haga un `POST` autenticado a `/sms/sendSms` con un cuerpo JSON. La respuesta es `200 OK` con el ID del mensaje como texto sin formato.

| Elemento | Valor |
|---|---|
| URL base | `https://api.broadnet.me` (sin prefijo `/v1`) |
| Endpoint | `POST /sms/sendSms` |
| Autenticación | `Authorization: Bearer $BROADNET_TOKEN` |
| Tipo de contenido | `application/json` |
| Campos obligatorios | `senderId`, `destinationNumber`, `message`, `messageType` |

Las credenciales de prueba se emiten tras una breve solicitud de acceso, y los envíos de prueba van a sus propios números verificados. No hay alta autoservicio instantánea. Consulte el [inicio rápido](/developers/quickstart/).

```bash
curl -X POST https://api.broadnet.me/sms/sendSms \
  -H "Authorization: Bearer $BROADNET_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "senderId": "OmniDemo",
    "destinationNumber": "447700900123",
    "message": "Your OmniDemo code is 482913",
    "messageType": "TEXT"
  }'
```

```js
// Node.js 18+ (fetch integrado)
const res = await fetch("https://api.broadnet.me/sms/sendSms", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.BROADNET_TOKEN}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    senderId: "OmniDemo",
    destinationNumber: "447700900123",
    message: "Your OmniDemo code is 482913",
    messageType: "TEXT",
  }),
});
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
const messageId = await res.text(); // ID del mensaje en texto sin formato
```

```python
import os
import requests

r = requests.post(
    "https://api.broadnet.me/sms/sendSms",
    headers={"Authorization": f"Bearer {os.environ['BROADNET_TOKEN']}"},
    json={
        "senderId": "OmniDemo",
        "destinationNumber": "447700900123",
        "message": "Your OmniDemo code is 482913",
        "messageType": "TEXT",
    },
    timeout=15,
)
r.raise_for_status()
message_id = r.text  # ID del mensaje en texto sin formato, no JSON
```

```php
<?php
$payload = json_encode([
    "senderId" => "OmniDemo",
    "destinationNumber" => "447700900123",
    "message" => "Your OmniDemo code is 482913",
    "messageType" => "TEXT",
]);

$ch = curl_init("https://api.broadnet.me/sms/sendSms");
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_TIMEOUT => 15,
    CURLOPT_HTTPHEADER => [
        "Authorization: Bearer " . getenv("BROADNET_TOKEN"),
        "Content-Type: application/json",
    ],
    CURLOPT_POSTFIELDS => $payload,
]);
$messageId = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);
echo $status . " " . $messageId . PHP_EOL;
```

El cuerpo de una respuesta correcta tiene este aspecto:

```text
2603121407537573731
```

Guarde ese ID junto a su propio registro, como un pedido o un intento de inicio de sesión. Es lo que le permite relacionar después los estados de entrega.

## ¿Cómo debo dar formato a los números de teléfono?

Use el prefijo del país más el número nacional, solo dígitos. El formato sigue el plan internacional de numeración de la ITU-T E.164, sin el signo más.

| Entrada | ¿Válida? | Motivo |
|---|---|---|
| `447700900123` | Sí | Prefijo de país 44 más número nacional |
| `+44 7700 900123` | No | Quite el signo más y los espacios |
| `07700900123` | No | Sustituya el 0 inicial por el prefijo del país |

Guarde los números en formato E.164 (`+447700900123`) en su base de datos y quite el `+` al construir la petición. Nuestro formateador gratuito de números de teléfono ayuda a hacer una comprobación rápida.

## ¿Cómo envío árabe, emojis u otro texto Unicode?

Establezca `messageType` en `UNICODE`. Cualquier carácter fuera del alfabeto GSM-7, como una letra árabe, un emoji, unas comillas tipográficas o una raya, convierte todo el mensaje en Unicode.

```python
r = requests.post(
    "https://api.broadnet.me/sms/sendSms",
    headers={"Authorization": f"Bearer {os.environ['BROADNET_TOKEN']}"},
    json={
        "senderId": "OmniDemo",
        "destinationNumber": "971500000000",
        "message": "رمز التحقق الخاص بك هو 482913",
        "messageType": "UNICODE",
        "campaignName": "login-otp",
    },
    timeout=15,
)
```

En PHP, use `JSON_UNESCAPED_UNICODE` al llamar a `json_encode`. La codificación determina el tamaño del segmento, tal como se define para el SMS en 3GPP TS 23.040:

| `messageType` | SMS único | Por segmento al dividir | Uso |
|---|---|---|---|
| `TEXT` | 160 caracteres | 153 caracteres | Texto que usa solo el alfabeto GSM-7 |
| `UNICODE` | 70 caracteres | 67 caracteres | Árabe, cirílico, chino, emojis |

La mayoría de los operadores facturan por segmento, de modo que un mensaje largo en árabe cuesta más que uno de la misma longitud en alfabeto latino. Lea nuestra [guía de SMS en Unicode y árabe](/es/blog/sms-character-limits-unicode-arabic/) y pruebe el contador de caracteres SMS.

## ¿Cómo cuento los segmentos antes de enviar?

Llame a `POST /sms/identify` con el mensaje. Devuelve la codificación detectada, el número de caracteres, el número de segmentos y el límite por segmento.

```bash
curl -X POST https://api.broadnet.me/sms/identify \
  -H "Authorization: Bearer $BROADNET_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "message": "Your OmniDemo code is 482913" }'
```

```json
{
  "message": "Your OmniDemo code is 482913",
  "messageType": "TEXT",
  "smscount": 1,
  "charCount": 28,
  "charLimit": 160
}
```

Puede pasar `smsParts` (a partir de `smscount`), `charCount` y `charLimit` en una petición de envío. El texto GSM-7 puede aparecer etiquetado como `GSM-7` en la respuesta; envíelo como `TEXT`.

## ¿Cómo envío a muchos números?

Use `POST /sms/sendbulk`, una petición `multipart/form-data` con un archivo de destinatarios y una cadena JSON de ajustes.

```bash
curl -X POST https://api.broadnet.me/sms/sendbulk \
  -H "Authorization: Bearer $BROADNET_TOKEN" \
  -F destinationNumberFile=@recipients.csv \
  -F 'bulkRequest={"senderId":"OmniDemo","message":"Your statement is ready in the app.","messageType":"TEXT","campaignName":"oct-statements","allowDuplicate":false}'
```

```python
import json
import os
import requests

bulk_request = {
    "senderId": "OmniDemo",
    "message": "Your statement is ready in the app.",
    "messageType": "TEXT",
    "campaignName": "oct-statements",
    "allowDuplicate": False,
}

with open("recipients.csv", "rb") as f:
    r = requests.post(
        "https://api.broadnet.me/sms/sendbulk",
        headers={"Authorization": f"Bearer {os.environ['BROADNET_TOKEN']}"},
        files={"destinationNumberFile": ("recipients.csv", f, "text/csv")},
        data={"bulkRequest": json.dumps(bulk_request)},
        timeout=60,
    )
print(r.json())
```

La respuesta incluye `bulkSessionId` y un objeto `bulkListInfo` con `total`, `validCount`, `invalidCount` y `duplicate`. Guarde `bulkSessionId` con la campaña. Los trabajos pueden listarse, pausarse, reanudarse y abortarse con los endpoints de la referencia. También puede comprobar los números antes de un envío grande con `POST /sms/lookup`; consulte nuestra [guía de HLR lookup](/es/blog/hlr-lookup-number-validation/).

## ¿Cómo recibo el estado de entrega?

Los estados de entrega se envían a la URL del webhook configurada en su cuenta. Un mensaje pasa por `SUBMITTED` y `SENT` hasta `DELIVERED`, o termina como `FAILED` o `REJECTED`. Usted facilita a nuestro equipo la URL HTTPS pública, y los revendedores pueden tener un webhook por subcuenta.

Los campos de la carga varían según el canal y la especificación de la API no define un único esquema de webhook, así que registre primero el cuerpo sin procesar y asigne los campos a partir de un evento real de su cuenta. Un receptor mínimo guarda el evento y responde con rapidez:

```js
import express from "express";

const app = express();

app.post("/webhooks/omnichannels", express.json(), async (req, res) => {
  await saveStatusEvent(req.body); // su almacenamiento; procese de forma asíncrona
  res.sendStatus(200);             // responda rápido
});

app.listen(3000);
```

Haga que el procesamiento sea idempotente, porque el mismo estado puede llegar dos veces, y no deje nunca que un `SENT` tardío sobrescriba un `DELIVERED` guardado. Si su endpoint estuvo caído, `POST /sms/resendDlr` vuelve a enviar los informes de un intervalo de fechas:

```bash
curl -X POST https://api.broadnet.me/sms/resendDlr \
  -H "Authorization: Bearer $BROADNET_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "startDate": "2026-10-09",
    "endDate": "2026-10-10",
    "senderId": "OmniDemo",
    "messageStatus": "DELIVERED"
  }'
```

Encontrará más información en la [guía de webhooks](/developers/webhooks/) y en estados de entrega de mensajes explicados.

## ¿Cómo gestiono los errores y evito los duplicados?

Gestione primero el estado HTTP y después el cuerpo del error. Reintente solo los errores de red, `429` y `5xx`.

| Código | Significado | Acción |
|---|---|---|
| `200` | Aceptado | Guarde el ID |
| `400` | Error de validación o campo ausente | Corrija la petición |
| `401` / `403` | Token incorrecto u operación no permitida | Corrija; no reintente |
| `429` | Límite de frecuencia superado | Espere según `Retry-After` |
| `500`, `503`, `504` | Problema del servidor o del servicio previo | Reintente con espera creciente y jitter |

Los errores que genera la API usan un cuerpo JSON de cinco campos con `status`, `error`, `message`, `errorCode` y `requestUri`. El límite REST documentado por defecto es de 200 peticiones por segundo sostenidas; los límites de su cuenta se confirman junto con sus credenciales.

La API no acepta ninguna clave de idempotencia. Una petición puede tener éxito aunque se pierda su respuesta, y un reintento a ciegas envía entonces el mensaje dos veces. Escriba una fila pendiente con su propio ID de operación antes de llamar, guarde el ID de mensaje devuelto y, tras un tiempo de espera agotado, compruebe si hay un estado por webhook antes de volver a enviar. En los envíos masivos, establezca `allowDuplicate` en `false`. Los detalles están en [Errores y límites](/developers/errors-and-limits/).

## Ejemplo práctico: códigos de inicio de sesión para una app regional

"Souq Rafiq" (ficticia), un comercio de los Emiratos Árabes Unidos, envía códigos de inicio de sesión por SMS.

1. El servicio de autenticación genera un código de seis dígitos, guarda un hash con una caducidad de cinco minutos y escribe una fila de envío `pending`.
2. Llama a `/sms/sendSms` con `messageType=UNICODE` para los usuarios en árabe y `TEXT` para los usuarios en inglés.
3. Con un `200`, guarda el ID del mensaje.
4. El gestor del webhook marca la fila como `DELIVERED`, `FAILED` o `REJECTED`.
5. Si el estado no es `DELIVERED` tras una breve espera, la app ofrece un reenvío y, si está configurado, otro canal. Consulte el respaldo entre canales.
6. Los límites de solicitudes de código por número y por IP protegen contra los abusos; consulte la [prevención del SMS pumping](/es/blog/sms-pumping-fraud-prevention/).

## ¿Cuáles son los errores habituales?

- **Llamar a `r.json()` sobre la respuesta del envío.** Es texto sin formato.
- **Nombres de campo incorrectos.** Use `senderId`, `destinationNumber` y `message`; campos como `to`, `from` o `text` no forman parte de este endpoint.
- **Signos más y ceros iniciales** en los números.
- **Olvidar `UNICODE`** para el árabe, lo que desfigura el texto.
- **Sender ID sin registrar.** Algunos países los bloquean o los sustituyen; consulte las normas de sender ID de su destino y pídanos que nos encarguemos del registro.
- **Tratar un `200` como entregado.**
- **El token en una app móvil o en un navegador,** donde cualquiera puede extraerlo.
- **Límites:** esta guía muestra la petición habitual. Los campos opcionales, como los formatos de programación, deben confirmarse con nuestro equipo.

## Cómo le ayuda OmniChannels.ai

OmniChannels.ai envía SMS A2P mediante la conectividad directa con operadores de BroadNet, con SMS bidireccional, sender ID, Unicode y árabe, HLR lookup y SMPP 3.4 para grandes volúmenes. La API REST en `https://api.broadnet.me` usa un token Bearer y abarca también WhatsApp, RCS y otros canales, con los estados de entrega normalizados en un único formato de webhook. Nuestro equipo prepara con usted los registros de sender ID. Consulte la [referencia de la API de SMS](/developers/sms/) o el canal SMS.
