Enviar un mensaje de WhatsApp por API con curl: ejemplos listos
Para enviar un mensaje de WhatsApp por API con curl, haz un POST a /api/v1/{instanceId}/send/text con los encabezados x-api-key y x-api-secret y un JSON con number (con código de país, solo dígitos) y text. La respuesta trae messageId y status. Esta guía va del primer envío a una lista con intervalo, y cada comando se puede copiar y ejecutar en la terminal. Para Node, Python y PHP, mira las guías de Node.js en 5 minutos, Python y PHP con cURL.
Publicado el · 4 min de lectura
Este artículo también está disponible en: Português · English
Qué necesitas antes de empezar
Una cuenta en ZapZap, una instancia conectada por código QR, la clave y el secreto de la API (en el panel, en Configuración y API) y el ID de la instancia. La autenticación usa solo dos encabezados, sin OAuth. Guarda los tres datos en variables de entorno para no pegar secretos en comandos ni dejarlos en el historial de la terminal.
El segundo comando imprime solo el código HTTP: 200 indica que la clave y el secreto están bien; 401 indica que uno de ellos está mal.
Variáveis e teste de credencial
# Keep credentials out of the command and out of the shell history
export ZAPZAP_INSTANCE="your-instance-id"
export ZAPZAP_API_KEY="your-api-key"
export ZAPZAP_API_SECRET="your-api-secret"
# Quick check: a 401 here means key or secret is wrong, not the instance
curl -s -o /dev/null -w "%{http_code}\n" "https://api.zapzapapi.com/api/v1/account" \
-H "x-api-key: $ZAPZAP_API_KEY" -H "x-api-secret: $ZAPZAP_API_SECRET"El primer envío: un mensaje de texto
El número va con código de país y solo dígitos, sin +, espacios ni guiones: 5511999999999 para un celular de São Paulo. El cuerpo mínimo tiene number y text. La respuesta síncrona trae messageId y status, que guardan el rastro del mensaje en tu sistema.
Por defecto, la API muestra el 'escribiendo...' proporcional al largo del texto antes de enviar, lo que humaniza el envío. Si necesitas una respuesta más rápida, envía digitando como false; para retrasar el envío en milisegundos, usa delay.
curl: texto
curl -X POST "https://api.zapzapapi.com/api/v1/$ZAPZAP_INSTANCE/send/text" \
-H "Content-Type: application/json" \
-H "x-api-key: $ZAPZAP_API_KEY" \
-H "x-api-secret: $ZAPZAP_API_SECRET" \
-d '{"number":"5511999999999","text":"Hello! Your order left for delivery."}'
# Expected answer: {"messageId":"ABCDEF123456","status":"sent"}Enviar imagen y PDF
El endpoint /send/media recibe el tipo (image, video, audio, document, sticker y otros) y la URL pública del archivo en el campo file. En imagen y video, el campo text pasa a ser el pie. En documento, docName es obligatorio y pasa a ser el nombre del archivo que ve el cliente.
Si la URL no es pública, el envío falla: el archivo debe ser accesible sin login. Para archivos privados, genera un enlace temporal firmado en tu almacenamiento.
curl: mídia
# Image with caption (the file must be a public URL)
curl -X POST "https://api.zapzapapi.com/api/v1/$ZAPZAP_INSTANCE/send/media" \
-H "Content-Type: application/json" \
-H "x-api-key: $ZAPZAP_API_KEY" -H "x-api-secret: $ZAPZAP_API_SECRET" \
-d '{"number":"5511999999999","type":"image","file":"https://example.com/promo.jpg","text":"This week offer"}'
# PDF: type document, and docName is required
curl -X POST "https://api.zapzapapi.com/api/v1/$ZAPZAP_INSTANCE/send/media" \
-H "Content-Type: application/json" \
-H "x-api-key: $ZAPZAP_API_KEY" -H "x-api-secret: $ZAPZAP_API_SECRET" \
-d '{"number":"5511999999999","type":"document","file":"https://example.com/quote.pdf","docName":"quote.pdf"}'Recibir la respuesta del cliente con webhook
Enviar es la mitad del camino. Registra una URL HTTPS pública en el endpoint de webhook de la instancia y la API empieza a hacer POST en ella en cada evento, como mensaje recibido y cambio de estado. Usa excludeMessages para no recibir de vuelta los mensajes que tú mismo enviaste.
Durante el desarrollo, una URL local no recibe nada. Mira por qué el webhook no llega a localhost y, para producción, idempotencia y reintento en el webhook.
curl: webhook
# Receive replies: register your HTTPS URL on the instance
curl -X PUT "https://api.zapzapapi.com/api/v1/instances/$ZAPZAP_INSTANCE/webhook" \
-H "Content-Type: application/json" \
-H "x-api-key: $ZAPZAP_API_KEY" -H "x-api-secret: $ZAPZAP_API_SECRET" \
-d '{"webhook_url":"https://yoursite.com/webhook","events":"messages,status","excludeMessages":true}'Una lista pequeña, al ritmo correcto
El script de abajo lee un CSV (número y nombre) y envía un mensaje a la vez, con pausa aleatoria de 8 a 20 segundos entre ellos. Nunca en paralelo. Es el patrón de menor riesgo para una lista de personas que ya pidieron recibir tus mensajes, y sirve para recordatorios y confirmaciones, no para campañas en frío.
Ningún intervalo elimina el riesgo de bloqueo, porque ZapZap usa un canal no oficial. Para volúmenes mayores, lee las guías de envío masivo y antibloqueo.
bash: lista com intervalo
# Small list, human pace: one message every 8 to 20 seconds, never in parallel
while IFS=, read -r number name; do
curl -s -X POST "https://api.zapzapapi.com/api/v1/$ZAPZAP_INSTANCE/send/text" \
-H "Content-Type: application/json" \
-H "x-api-key: $ZAPZAP_API_KEY" -H "x-api-secret: $ZAPZAP_API_SECRET" \
-d "{\"number\":\"$number\",\"text\":\"Hi $name, your appointment is tomorrow. Reply 1 to confirm.\"}" \
-w " -> HTTP %{http_code}\n"
sleep $(( (RANDOM % 13) + 8 ))
done < contacts.csvErrores más comunes y cómo leer la respuesta
401: clave o secreto errados, o un encabezado con el nombre cambiado. 404: el ID de la instancia está mal o se pegó dos veces en la URL. 400: cuerpo inválido, como number con letras o un campo obligatorio ausente. 429 y 5xx: errores transitorios, vale reintentar con esperas crecientes; 400, 401 y 403 no mejoran repitiendo.
Si la API respondió 'sent' y el cliente no lo recibió, el problema suele estar después del envío. Mira WhatsApp enviado y el cliente no lo recibe. La referencia completa de campos está en enviar mensaje y en la referencia de la API.
Resumen rápido
- Un POST a /send/text con x-api-key, x-api-secret, number (código de país, solo dígitos) y text envía el mensaje.
- Credenciales en variables de entorno, nunca pegadas en el comando.
- Multimedia exige URL pública en el campo file; un documento exige docName.
- Lista pequeña: un mensaje a la vez, con pausa aleatoria, solo para quien pidió recibir.
Sigue leyendo
Lee también
Preguntas frecuentes
¿Cómo enviar un mensaje de WhatsApp por API con curl?
Haz un POST a https://api.zapzapapi.com/api/v1/{instanceId}/send/text con los encabezados x-api-key, x-api-secret y Content-Type application/json, y el cuerpo {number, text}. El number lleva código de país y solo dígitos.
¿El número necesita + o espacios?
No. Usa solo dígitos con código de país, como 5511999999999. Para limpiar listas enteras, usa el formateador de números en lote del área de herramientas.
¿Puedo enviar imagen y PDF con curl?
Sí, por el endpoint /send/media, con type image o document y la URL pública del archivo en file. En el documento, informa docName.
¿Cómo recibo la respuesta del cliente?
Registra la URL HTTPS de tu servidor con PUT a /api/v1/instances/{id}/webhook. La API hace POST en ella en cada evento de mensaje y de estado.
¿Es seguro disparar una lista entera con un bucle?
Solo una lista pequeña, de quien pidió recibir, con pausa entre mensajes. El canal es no oficial y ningún intervalo elimina el riesgo de bloqueo. Para volumen, sigue las guías de antibloqueo.
Ejecuta tu primer curl hoy
Crea la cuenta, conecta el número con un código QR y copia el comando de la guía. R$15 por número activo al mes, sin cobro por mensaje.
