← Blog

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:

  1. Uma conta no Zapixo com instância WhatsApp conectada (via QR code no painel).
  2. Uma chave API gerada em Painel → Chaves API (formato zpx_...).
  3. Python 3.10 ou superior instalado.
  4. A biblioteca requests ou httpx no 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.