Enviar WhatsApp com Node.js em 5 minutos
Dá para enviar uma mensagem de WhatsApp pelo Node.js só com o fetch nativo: sem biblioteca de WhatsApp e sem navegador headless. Leva uns cinco minutos se você já tem um número conectado. Transparência: a ZapZap API é não oficial e conecta um número comum por QR code. Isso significa que está fora dos termos do WhatsApp e que números podem ser limitados ou banidos, então leia a lista do final antes de enviar para gente de verdade.
Publicado em · 4 min de leitura
0. O que você precisa (2 minutos)
1) Uma conta na ZapZap e uma instância conectada por QR code. Contas novas recebem crédito de boas-vindas ao verificar o telefone, conforme a campanha vigente; o início rápido mostra onde clicar. 2) Sua API key, seu API secret e o ID da instância. 3) Node 18 ou mais novo (node -v).
Coloque as credenciais em variáveis de ambiente, nunca no código. A autenticação são dois cabeçalhos em toda requisição, x-api-key e x-api-secret.
Shell
export ZAPZAP_INSTANCE_ID="your-instance-id"
export ZAPZAP_API_KEY="your-api-key"
export ZAPZAP_API_SECRET="your-api-secret"1. Enviar um texto (1 minuto)
Crie o arquivo send.mjs com o código abaixo e rode com node send.mjs. A extensão .mjs habilita await no nível do arquivo; em CommonJS, envolva a chamada numa função async. Use o seu próprio número primeiro. O número vai com DDI e só dígitos, sem +, espaços ou traços.
Node.js: send.mjs
const BASE = "https://api.zapzapapi.com/api/v1";
const { ZAPZAP_INSTANCE_ID, ZAPZAP_API_KEY, ZAPZAP_API_SECRET } = process.env;
async function zapzap(path, body) {
const res = await fetch(BASE + "/" + ZAPZAP_INSTANCE_ID + path, {
method: "POST",
headers: {
"Content-Type": "application/json",
"x-api-key": ZAPZAP_API_KEY,
"x-api-secret": ZAPZAP_API_SECRET,
},
body: JSON.stringify(body),
signal: AbortSignal.timeout(20_000), // never leave a call hanging
});
const data = await res.json().catch(() => ({}));
if (!res.ok) throw new Error("ZapZap " + res.status + ": " + JSON.stringify(data));
return data;
}
// number: country code + digits only (no +, spaces or dashes)
console.log(await zapzap("/send/text", { number: "5511999999999", text: "Hello from Node.js" }));2. Enviar um PDF ou uma imagem (1 minuto)
Arquivos passam por /send/media. O campo type escolhe o tipo (image, video, audio, ptt, document, sticker), file é uma URL pública, text é a legenda e docName é o nome do arquivo nos documentos. O arquivo precisa estar acessível por uma URL pública; se ele está no seu disco, suba para um armazenamento e use um link temporário.
Node.js
await zapzap("/send/media", {
number: "5511999999999",
type: "document",
file: "https://example.com/invoice-1042.pdf",
text: "Your invoice",
docName: "invoice-1042.pdf",
});3. Receber respostas com Express (1 minuto)
Registre a sua URL pública como webhook da instância com o evento de mensagens ligado (veja configurar webhook). No desenvolvimento local você precisa de um túnel, porque a API precisa alcançar você pela internet.
Descarte sempre os eventos fromMe, ou uma resposta automática responde a si mesma em loop. Confira o formato exato com uma mensagem real primeiro; para validar o cabeçalho X-ZapZap-Signature e eliminar entregas repetidas, siga webhooks em produção.
Node.js: Express
import express from "express";
const app = express();
app.use(express.json());
app.post("/webhook", (req, res) => {
res.sendStatus(200); // answer first, process after
const msg = req.body?.data?.message;
if (!msg || msg.fromMe) return; // ignore your own messages
console.log("from " + msg.chatid + ": " + msg.text);
// reply, save to a database, call an AI agent...
});
app.listen(3000, () => console.log("listening on :3000"));4. Repita só os erros certos
Tente de novo apenas em 429 e 5xx, com esperas crescentes. Um 400, 401 ou 403 significa que a requisição ou as credenciais estão erradas, e repetir só produz mais erros.
Node.js
async function withRetry(fn, tries = 4) {
for (let i = 0; ; i++) {
try {
return await fn();
} catch (err) {
const retryable = /ZapZap (429|5\d\d)/.test(err.message);
if (!retryable || i >= tries - 1) throw err;
await new Promise((r) => setTimeout(r, 2 ** i * 1000));
}
}
}
await withRetry(() => zapzap("/send/text", { number: "5511999999999", text: "Retried send" }));5. Antes de enviar para gente de verdade
Credenciais só em variáveis de ambiente, nunca no git. Timeout em toda chamada e retry só em 429 e 5xx. Em listas, envie um por vez com uma pausa aleatória entre as mensagens em vez de disparar tudo em paralelo; trate os valores da pausa como um ponto de partida seu, não como regra do WhatsApp, e para campanhas grandes a fila do servidor tira isso do seu app. Envie só para quem concordou em receber e respeite quem pediu para sair.
Não oficial significa risco de bloqueio. Ritmo, aquecimento e listas limpas reduzem, nada elimina. Use um número que você possa perder enquanto testa e leia o guia anti-bloqueio. Se você precisa de canal homologado, veja API oficial x ZapZap. Para planejar: R$15 por número ativo por mês, pré-pago, sem cobrança por mensagem (preços).
Lista completa de endpoints na referência da API e no arquivo OpenAPI. Prefere outra linguagem? Os mesmos endpoints funcionam em Python e nos outros guias do blog, ou sem código no n8n. Para a versão com mais detalhes deste guia, veja enviar WhatsApp com Node.js. Os SDKs oficiais ainda não estão publicados no npm nem no PyPI; o código-fonte está no GitHub em zapzap-sdk-node, zapzap-sdk-python e zapzap-sdk-php, e a API REST funciona direto com o fetch, como acima.
Resumo rápido
- O fetch nativo do Node 18 basta: um POST em /send/text com x-api-key e x-api-secret.
- Arquivos vão por /send/media com uma URL pública; respostas chegam por webhook, e eventos fromMe devem ser descartados.
- Repita só 429 e 5xx, com espera crescente. 400, 401 e 403 são erro de requisição ou de credencial.
- Os SDKs ainda não estão no npm ou PyPI (repositórios no GitHub); a API REST funciona direto, sem biblioteca.
Continue lendo
Perguntas frequentes
Preciso de alguma biblioteca para enviar WhatsApp pelo Node.js?
Não. O fetch nativo do Node 18 ou mais novo faz o POST em /send/text. O Express só entra se você quiser receber respostas por webhook.
Por que o meu bot responde a si mesmo em loop?
Porque o webhook também entrega as mensagens que você mesmo enviou. Descarte os eventos em que fromMe é verdadeiro antes de responder.
Existe um SDK da ZapZap para Node.js no npm?
Ainda não está publicado no npm. O código está em github.com/zapzapapi/zapzap-sdk-node, e você pode usar a API REST direto com o fetch, como neste guia.
Enviar por esta API pode bloquear o meu número?
Pode. A API é não oficial e vai contra os termos do WhatsApp. Ritmo, aquecimento, opt-in e listas limpas reduzem o risco, mas nada o elimina. Teste com um número que você possa perder.
Mande a primeira mensagem agora
Crie a conta, conecte um número de teste por QR code e rode o send.mjs. R$15 por número ativo ao mês, sem cobrança por mensagem.
