Telegram Bot API
🌐 Español

Cómo enviar mensajes con la API de Telegram: sendMessage paso a paso

· 7 min de lectura

La API de Telegram para enviar mensajes es el método sendMessage de la Bot API: haces una petición a https://api.telegram.org/bot<token>/sendMessage con chat_id y text, y Telegram entrega el mensaje como tu bot. Aquí tienes ejemplos listos para copiar en curl, Python y JavaScript, además de formato, botones y los errores más comunes.

Formato de la petición sendMessage

Puedes usar GET o POST y enviar los parámetros como query string, application/x-www-form-urlencoded, application/json o multipart/form-data. Solo dos son obligatorios:

La respuesta siempre trae ok. Si es true, en result recibes el Message enviado, con su message_id; si es false, llegan error_code y description.

Enviar un mensaje con curl, Python y JavaScript

curl -X POST "https://api.telegram.org/bot123456789:AAEexampleTokenReplaceWithYours/sendMessage" \
  -H "Content-Type: application/json" \
  -d '{"chat_id": 123456789, "text": "Hola desde la Bot API"}'
import requests

TOKEN = "123456789:AAEexampleTokenReplaceWithYours"

resp = requests.post(
    f"https://api.telegram.org/bot{TOKEN}/sendMessage",
    json={"chat_id": 123456789, "text": "Hola desde Python"},
    timeout=10,
)
data = resp.json()
print(data["result"]["message_id"] if data["ok"] else data["description"])
// Node.js 18+ (módulo ES): fetch viene incluido
const TOKEN = "123456789:AAEexampleTokenReplaceWithYours";

const res = await fetch(`https://api.telegram.org/bot${TOKEN}/sendMessage`, {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ chat_id: 123456789, text: "Hola desde JavaScript" }),
});
const data = await res.json();
console.log(data.ok ? data.result.message_id : data.description);

Ejecuta este código en tu servidor, nunca en una página web pública: cualquiera podría leer el token en el JavaScript del navegador.

Formato del texto: parse_mode HTML y MarkdownV2

Con "parse_mode": "HTML" puedes usar <b>, <i>, <u>, <s>, <tg-spoiler>, <a href="…">, <code>, <pre> y <blockquote>, además de sus variantes <strong>, <em>, <ins>, <del> y <blockquote expandable>. Todo <, > o & que no forme parte de una etiqueta debe escribirse como &lt;, &gt; y &amp;; en Python, html.escape() lo hace por ti.

{
  "chat_id": 123456789,
  "parse_mode": "HTML",
  "text": "<b>Pedido #154</b> confirmado\nTotal: <code>1250 MXN</code>\n<a href=\"https://example.com/pedidos/154\">Ver pedido</a>"
}

Con "parse_mode": "MarkdownV2" la sintaxis es más corta (*negrita*, _cursiva_, [enlace](https://example.com)), pero fuera de las entidades hay que escapar con \ estos caracteres: _ * [ ] ( ) ~ ` > # + - = | { } . !. Olvidarlo es la causa más común del error 400 Bad Request: can't parse entities. Una función de escape en Python:

import re

def escape_md2(text: str) -> str:
    return re.sub(r"([_*\[\]()~`>#+\-=|{}.!\\])", r"\\\1", text)

text = "*Total:* " + escape_md2("1,250.00 MXN (pagado!)")

Ojo: si escribes el texto a mano dentro de un JSON, la barra invertida también se escapa, así que \. se convierte en "\\.".

Botones: teclado inline con reply_markup

{
  "chat_id": 123456789,
  "text": "¿Confirmas tu pedido?",
  "reply_markup": {
    "inline_keyboard": [[
      {"text": "Ver en el sitio", "url": "https://example.com/pedidos/154"},
      {"text": "Confirmar", "callback_data": "confirmar:154"}
    ]]
  }
}

Cada fila es un arreglo de botones. Un botón url abre un enlace; uno con callback_data (de 1 a 64 bytes: los acentos y emojis ocupan más de un byte) envía a tu bot una actualización callback_query. Respóndela siempre con answerCallbackQuery, porque hasta entonces el cliente muestra un indicador de progreso. Si envías los parámetros como formulario en lugar de JSON, reply_markup debe ir serializado como cadena JSON.

Enviar a grupos, canales y temas

Mensajes silenciosos, vista previa de enlaces y respuestas

{
  "chat_id": 123456789,
  "text": "Reporte diario: https://example.com/reporte",
  "disable_notification": true,
  "protect_content": true,
  "link_preview_options": {"is_disabled": true},
  "reply_parameters": {"message_id": 42}
}

Errores comunes al enviar mensajes

ErrorCausa y solución
400 Bad Request: chat not foundchat_id incorrecto, el bot no está en el grupo o canal, o el usuario nunca inició el bot.
400 Bad Request: can't parse entitiesEtiqueta HTML mal cerrada o carácter sin escapar en MarkdownV2.
400 Bad Request: message is too longMás de 4096 caracteres: divide el texto.
403 Forbidden: bot was blocked by the userEl usuario bloqueó el bot. No reintentes; márcalo como inactivo.
429 Too Many Requests: retry after 35Superaste los límites. Espera los segundos de parameters.retry_after.

Como referencia: no más de 1 mensaje por segundo en un mismo chat, 20 por minuto en un grupo y unos 30 por segundo en envíos masivos. Un reintento sencillo en Python:

import time
import requests

def send(token, chat_id, text):
    url = f"https://api.telegram.org/bot{token}/sendMessage"
    while True:
        data = requests.post(url, json={"chat_id": chat_id, "text": text}, timeout=10).json()
        if data.get("error_code") != 429:
            return data
        time.sleep(data["parameters"]["retry_after"])

Antes de escribir código, prueba tus parámetros en sendMessage en Bot API Tester (Ctrl+Enter ejecuta la petición) y consulta la referencia del método, en inglés. Si además tu bot debe recibir mensajes en un servidor, sigue con la guía de webhook para bots de Telegram.

Preguntas frecuentes

¿Cuál es el límite de caracteres de sendMessage?

El texto debe tener de 1 a 4096 caracteres después de procesar el formato. Si es más largo, divídelo en varios mensajes.

¿Puedo enviar un mensaje a alguien que nunca habló con mi bot?

No. Los bots no pueden iniciar conversaciones: el usuario debe abrir el bot y pulsar Iniciar (o añadirlo a un grupo) antes de que puedas escribirle.

¿Cómo envío un mensaje sin sonido?

Agrega disable_notification: true a la petición: el mensaje llega con una notificación sin sonido.

¿Qué parse_mode conviene usar, HTML o MarkdownV2?

HTML suele ser más sencillo, porque solo hay que escapar los signos menor que, mayor que y el ampersand. MarkdownV2 exige escapar 18 caracteres especiales con una barra invertida. El modo Markdown clásico se mantiene solo por compatibilidad.

¿Cómo envío un mensaje a un canal de Telegram con la API?

Haz al bot administrador del canal con permiso para publicar y usa como chat_id el @username del canal, si es público, o su id numérico, que empieza por -100.

¿Cómo edito o borro un mensaje que ya envié?

Guarda el message_id que devuelve sendMessage y úsalo con editMessageText o deleteMessage.

Cómo crear un bot en Telegram paso a paso: de BotFather a tu primer mensaje

Aprende a crear un bot de Telegram: /newbot en @BotFather, token, prueba con getMe y tu primer mensaje con curl y Python. Gratis y sin servidor.

Cómo obtener el token de un bot de Telegram (y qué hacer si se filtra)

Cómo obtener el token de tu bot de Telegram con @BotFather, encontrarlo si el bot ya existe, comprobarlo con getMe y revocarlo si se filtró.

Webhook de Telegram: cómo configurar setWebhook para tu bot (Python y n8n)

Configura el webhook de tu bot de Telegram: requisitos HTTPS, setWebhook con secret_token, getWebhookInfo, handler en Python y errores típicos en n8n.

Cómo obtener el chat id de Telegram: usuarios, grupos, canales y temas

Obtén el chat id de Telegram para tu bot con getUpdates: usuarios, grupos, supergrupos (-100…), canales y temas, y qué hacer si result viene vacío.