Como enviar mensagem WhatsApp com Python
Automatizar o envio de mensagens no WhatsApp com Python é uma das tarefas mais comuns em projetos de atendimento, notificações transacionais e integrações com ERPs. Se você já trabalha com scripts de automação, bots internos ou pipelines de dados, adicionar WhatsApp ao fluxo costuma ser o próximo passo natural — desde que você use uma API estável, com documentação clara e autenticação simples.
Neste guia, vamos percorrer o caminho completo: preparar o ambiente, autenticar na API, enviar mensagens de texto, tratar erros, respeitar rate limits e estruturar o código para produção. Usaremos a Zapixo, uma API de WhatsApp gerenciada para desenvolvedores brasileiros, que expõe endpoints REST prontos para consumo sem exigir que você hospede e mantenha servidores da Evolution API por conta própria.
Por que usar Python para WhatsApp?
Python é amplamente adotado em automações empresariais no Brasil. Bibliotecas como requests e httpx tornam trivial fazer chamadas HTTP, e frameworks como FastAPI ou Flask permitem expor endpoints que disparam mensagens em resposta a eventos do seu sistema — um novo pedido no e-commerce, um boleto vencido, um agendamento confirmado.
A vantagem de usar uma API REST como a do Zapixo, em vez de bibliotecas que emulam o cliente web do WhatsApp, é a previsibilidade: você envia um JSON, recebe um messageId e monitora o status da instância. Não precisa lidar com sessões, QR codes no código ou atualizações que quebram scrapers.
Pré-requisitos
Antes de escrever qualquer linha de código, certifique-se de ter:
- Uma conta no Zapixo com instância WhatsApp conectada (via QR code no painel).
- Uma chave API gerada em Painel → Chaves API (formato
zpx_...). - Python 3.10 ou superior instalado.
- A biblioteca
requestsouhttpxno seu ambiente virtual.
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install requests python-dotenv
Crie um arquivo .env na raiz do projeto — nunca commite chaves no repositório:
ZAPIXO_API_KEY=zpx_sua_chave_aqui
ZAPIXO_INSTANCE=minha-loja
Enviando sua primeira mensagem de texto
O endpoint principal para mensagens de texto é POST /api/v1/messages/text. A autenticação é feita via header Authorization: Bearer SUA_CHAVE.
import os
import requests
from dotenv import load_dotenv
load_dotenv()
API_KEY = os.environ["ZAPIXO_API_KEY"]
INSTANCE = os.environ["ZAPIXO_INSTANCE"]
BASE_URL = "https://zapixo.com.br/api/v1"
def enviar_texto(numero: str, texto: str) -> dict:
"""Envia mensagem de texto via Zapixo."""
response = requests.post(
f"{BASE_URL}/messages/text",
headers={
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json",
},
json={
"instance": INSTANCE,
"number": numero,
"text": texto,
},
timeout=30,
)
response.raise_for_status()
return response.json()
if __name__ == "__main__":
resultado = enviar_texto(
numero="5511999999999",
texto="Olá! Seu pedido #1234 foi confirmado.",
)
print(f"Mensagem enviada. ID: {resultado.get('messageId')}")
Alguns detalhes importantes sobre o campo number:
- Use o formato internacional sem símbolos:
5511999999999(código do país + DDD + número). - Não inclua
+, espaços ou hífens. - Para números brasileiros, o nono dígito do celular deve estar presente.
Se tudo correr bem, a resposta será algo como {"success": true, "messageId": "3EB0..."}.
Enviando mídia (imagem com legenda)
Para notificações visuais — comprovantes, catálogos, status de entrega — use o endpoint POST /api/v1/messages/media:
def enviar_imagem(numero: str, url_imagem: str, legenda: str = "") -> dict:
response = requests.post(
f"{BASE_URL}/messages/media",
headers={
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json",
},
json={
"instance": INSTANCE,
"number": numero,
"mediaUrl": url_imagem,
"caption": legenda,
},
timeout=30,
)
response.raise_for_status()
return response.json()
A URL da mídia precisa ser pública e acessível via HTTPS. Armazene imagens em S3, Cloudflare R2, Vercel Blob ou qualquer CDN confiável.
Verificando o status da instância
Antes de disparar campanhas ou integrações críticas, confirme que a instância está conectada:
def status_instancia() -> dict:
response = requests.get(
f"{BASE_URL}/instances/{INSTANCE}/status",
headers={"Authorization": f"Bearer {API_KEY}"},
timeout=15,
)
response.raise_for_status()
return response.json()
Se o status retornar disconnected, acesse o painel do Zapixo e reconecte via QR code. Tentar enviar mensagens com instância offline resultará em erro 502.
Tratamento de erros e rate limit
A API do Zapixo aplica 60 requisições por minuto por chave. Os headers de resposta informam o consumo:
X-RateLimit-Remaining: requisições restantes na janela atual.X-RateLimit-Reset: timestamp Unix de quando o limite reinicia.
Implemente retry com backoff para o status 429:
import time
def enviar_com_retry(numero: str, texto: str, max_tentativas: int = 3) -> dict:
for tentativa in range(max_tentativas):
response = requests.post(
f"{BASE_URL}/messages/text",
headers={
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json",
},
json={"instance": INSTANCE, "number": numero, "text": texto},
timeout=30,
)
if response.status_code == 429:
reset = int(response.headers.get("X-RateLimit-Reset", 0))
espera = max(reset - time.time(), 1)
time.sleep(espera)
continue
response.raise_for_status()
return response.json()
raise RuntimeError("Rate limit excedido após múltiplas tentativas")
Para erros 401 (chave inválida), 404 (instância não encontrada) e 400 (validação), trate cada caso explicitamente e registre logs estruturados — isso facilita debug em produção.
Estruturando para produção
Para projetos que vão além de scripts pontuais, recomendamos:
Classe de serviço centralizada — encapsule todas as chamadas em um módulo zapixo_client.py e injete a chave via variável de ambiente.
Fila de mensagens — use Celery, RQ ou AWS SQS para não bloquear sua aplicação principal e respeitar o rate limit naturalmente.
Validação de números — normalize telefones antes do envio com uma função que remove caracteres não numéricos e adiciona o prefixo 55 quando ausente.
Logs sem dados sensíveis — registre messageId e status, mas evite logar o conteúdo completo de mensagens com dados pessoais (LGPD).
Exemplo com FastAPI
Se você expõe um endpoint interno que dispara notificações:
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
app = FastAPI()
class Notificacao(BaseModel):
telefone: str
mensagem: str
@app.post("/notificar")
def notificar(dados: Notificacao):
try:
resultado = enviar_texto(dados.telefone, dados.mensagem)
return {"ok": True, "messageId": resultado.get("messageId")}
except requests.HTTPError as e:
raise HTTPException(status_code=e.response.status_code, detail=str(e))
Alternativa com httpx (async)
Para aplicações assíncronas, httpx oferece a mesma interface com async/await:
import httpx
async def enviar_texto_async(numero: str, texto: str) -> dict:
async with httpx.AsyncClient() as client:
response = await client.post(
f"{BASE_URL}/messages/text",
headers={"Authorization": f"Bearer {API_KEY}"},
json={"instance": INSTANCE, "number": numero, "text": texto},
timeout=30,
)
response.raise_for_status()
return response.json()
Conclusão
Enviar mensagens WhatsApp com Python usando a API do Zapixo é direto: uma requisição POST com JSON, autenticação Bearer e tratamento adequado de erros. Você evita a complexidade de manter infraestrutura própria da Evolution API e ganha um painel brasileiro com pagamento via Pix, instância no ar em minutos e documentação em português.
Para ir além do envio, configure webhooks no painel da instância e receba mensagens entrantes no seu backend Python — assunto que abordamos em outro artigo do blog. Enquanto isso, crie sua conta em zapixo.com.br, gere sua chave API e comece a integrar.