Integração

API de WhatsApp para Python

Use requests ou httpx para integrar WhatsApp em Python.

import requests

response = requests.post(
    "https://zapixo.com.br/api/v1/messages/text",
    headers={"Authorization": f"Bearer {API_KEY}"},
    json={
        "instance": "python-app",
        "number": "5511999999999",
        "text": "Olá do Python!",
    },
)
print(response.json())

Em Python, a integração com o Zapixo é requests ou httpx para enviar e uma rota FastAPI ou Flask para receber. A API é REST com JSON, sem SDK para instalar.

Para o primeiro envio com calma, o tutorial de Python no blog cobre texto, mídia e estrutura de projeto. Aqui o foco é a integração inteira, com recebimento e os erros de produção.

O que dá para fazer com Python e WhatsApp

  • Rotina agendada

    Um script no cron avisa os clientes de manhã: vencimento, agenda do dia, pedido pronto para retirar.

  • Atendimento com dados do sistema

    A rota do Django ou do FastAPI recebe a pergunta e responde com o dado real do banco.

  • Bot com modelo de linguagem

    O webhook passa o texto para o modelo, que devolve a resposta enviada pelo mesmo número.

  • Registro para análise

    Toda mensagem recebida vai para o banco, pronta para relatório de volume e tempo de resposta.

Enviar mensagens pelo Python

  1. Guarde a chave no ambiente

    Gere a chave no painel e coloque em ZAPIXO_API_KEY. Ela começa com zpx_ e dá acesso para enviar em nome da instância, então fica fora do código.

  2. Função de envio com requests

    Uma Session reaproveita a conexão e o header. O timeout não é opcional: sem ele, o requests espera para sempre uma conexão travada.

    import os
    import re
    import requests
    
    BASE = "https://zapixo.com.br/api/v1"
    SESSION = requests.Session()
    SESSION.headers["Authorization"] = f"Bearer {os.environ['ZAPIXO_API_KEY']}"
    
    
    def enviar_texto(instance: str, number: str, text: str) -> dict:
        resp = SESSION.post(
            f"{BASE}/messages/text",
            json={"instance": instance, "number": re.sub(r"\D", "", number), "text": text},
            timeout=15,
        )
        if resp.status_code == 429:
            reabre = resp.headers.get("X-RateLimit-Reset")
            raise RuntimeError(f"limite por minuto atingido, reabre em {reabre}")
        resp.raise_for_status()
        return resp.json()
  3. Assíncrono com httpx

    Em código async, o httpx faz o mesmo sem bloquear:

    import httpx
    
    async with httpx.AsyncClient(
        base_url=BASE,
        headers={"Authorization": f"Bearer {os.environ['ZAPIXO_API_KEY']}"},
        timeout=15,
    ) as client:
        resp = await client.post(
            "/messages/text",
            json={"instance": "minha-loja", "number": "5511999999999", "text": "Olá!"},
        )
        resp.raise_for_status()

Receber mensagens no Python

  1. Cadastre a URL no painel

    Abra a instância no painel e cole a URL da sua rota em Webhook de mensagens. Precisa ser HTTPS. O Zapixo não assina as requisições, então ponha um segredo na URL e confira na rota: https://seuapp.com/webhooks/zapixo?chave=....

  2. A rota em FastAPI

    BackgroundTasks roda o processamento depois da resposta. O evento de mensagem nova é messages.upsert:

    import os
    from fastapi import BackgroundTasks, FastAPI, HTTPException, Request
    
    app = FastAPI()
    
    
    @app.post("/webhooks/zapixo")
    async def webhook(request: Request, tasks: BackgroundTasks, chave: str = ""):
        if chave != os.environ["ZAPIXO_WEBHOOK_SECRET"]:
            raise HTTPException(status_code=401)
    
        payload = await request.json()
        data = payload.get("data") or {}
        key = data.get("key") or {}
    
        if payload.get("event") == "messages.upsert" and not key.get("fromMe"):
            tasks.add_task(processar, data)
    
        return {"ok": True}
  3. A rota em Flask

    No Flask, responda logo e mande o trabalho para fora da requisição — uma fila como Celery ou RQ:

    @app.post("/webhooks/zapixo")
    def webhook():
        if request.args.get("chave") != os.environ["ZAPIXO_WEBHOOK_SECRET"]:
            abort(401)
    
        payload = request.get_json(silent=True) or {}
        data = payload.get("data") or {}
        if payload.get("event") == "messages.upsert" and not data.get("key", {}).get("fromMe"):
            processar.delay(data)  # tarefa Celery
        return {"ok": True}
  4. Número e texto

    O número vem no remoteJid, com sufixo. Texto simples fica em conversation; resposta citando outra mensagem costuma vir em extendedTextMessage:

    numero = data["key"]["remoteJid"].split("@")[0]
    msg = data.get("message") or {}
    texto = msg.get("conversation") or (msg.get("extendedTextMessage") or {}).get("text", "")

Erros comuns e como resolver

Mensagem processada duas vezes
Se a rota não devolver 2xx em até 10 segundos, o Zapixo tenta de novo — até 3 tentativas. Responda rápido e guarde data['key']['id'] das mensagens já tratadas.
O bot responde a si mesmo
Mensagens enviadas pela própria instância também chegam, com fromMe: true. Filtre antes de responder, senão o bot conversa com ele mesmo.
Mensagem de grupo
Grupo chega com remoteJid terminando em @g.us. Se a rota só atende conversa individual, descarte.
Erro 429
60 requisições por minuto por chave. No 429, espere até o horário de X-RateLimit-Reset (segundos Unix). Para lista grande, espace os envios em vez de disparar em laço.
CSRF no Django
A view do webhook recebe POST de fora do site, sem token CSRF. Marque-a com @csrf_exempt e proteja pelo segredo na URL.
Erros 401, 404 e 502
401: chave errada ou sem o Bearer na frente. 404: instance não bate com o nome no painel. 502: a entrega ao WhatsApp falhou, e o motivo vem em details.

Perguntas frequentes

Existe SDK do Zapixo para Python?

Não precisa. A API é REST com JSON, e requests ou httpx resolvem.

Funciona com Django?

Funciona. A lógica do webhook é a mesma, numa view marcada com @csrf_exempt.

O webhook do Zapixo tem assinatura?

Não. Proteja a rota com um segredo na URL e confira em cada requisição.

Consigo enviar imagem pelo Python?

Sim, pelo endpoint /api/v1/messages/media, com mediaUrl (link público do arquivo) e caption opcional.

O trial tem limite de mensagens?

Tem. Ao atingir o limite, a API responde 402 com o link para assinar.

Leia também