API do WhatsApp em Python: tutorial com a API não oficial (requests + Flask)
API do WhatsApp em Python com a API não oficial: conectar por QR Code, enviar texto, imagem e botões com requests e receber mensagens num webhook Flask.
Para usar a API do WhatsApp em Python com a API não oficial da WAME, você só precisa de requests para enviar e de um servidor web como Flask para receber o webhook — não há SDK obrigatório nem servidor de WhatsApp para hospedar. O número conecta por QR Code, a autenticação é a key da instância na própria URL, e cada mensagem recebida chega como um POST JSON no envelope padrão da Meta.
Neste tutorial você monta, do zero, um pequeno bot em Python: conecta o número, envia texto, imagem e botões, recebe mensagens e responde com o indicador "digitando…".
O que você precisa
- Python 3.10 ou mais recente;
- uma instância na WAME e a sua
key; pip install requests flask;- uma URL pública para o webhook (em desenvolvimento,
ngrokou similar).
A API é hospedada e gerenciada: diferente de rodar uma biblioteca de WhatsApp Web por conta própria, você não mantém sessão, reconexão nem atualização de protocolo. Seu Python só conversa com HTTP.
Passo 1: um cliente mínimo
Comece com um módulo pequeno que centraliza a URL e a key:
# wame.py
import os
import requests
BASE = "https://us.api-wa.me"
KEY = os.environ["WAME_KEY"]
session = requests.Session()
session.headers.update({"Content-Type": "application/json"})
def call(method: str, path: str, **kwargs):
url = f"{BASE}/{KEY}{path}"
resp = session.request(method, url, timeout=30, **kwargs)
resp.raise_for_status()
return resp.json() if resp.content else NoneGuardar a key em variável de ambiente não é detalhe: ela autentica tudo. Veja segurança de token e webhook.
Passo 2: conectar o número
Gere o QR Code:
from wame import call
qr = call("POST", "/instance")
print(qr) # o formato do retorno (QR em base64, status) está em /docsEscaneie em Aparelhos conectados no app do WhatsApp. Se estiver num servidor sem tela, prefira o código de pareamento:
call("POST", "/instance/pairing-code", json={"phoneNumber": "5511999999999"})Você digita o código no celular e a instância conecta. Detalhes em conectar sem QR Code. Para conferir o estado:
print(call("GET", "/instance"))Passo 3: enviar mensagens
Números sempre em formato internacional, só dígitos: DDI + DDD + número.
Texto:
def enviar_texto(para: str, texto: str):
return call("POST", "/message/text", json={"to": para, "text": texto})
enviar_texto("5511999999999", "Olá! Mensagem enviada pelo Python.")Imagem por URL:
def enviar_imagem(para: str, url: str, legenda: str = ""):
return call("POST", "/message/image", json={
"to": para,
"url": url,
"caption": legenda,
})Botões de resposta rápida:
def enviar_menu(para: str):
return call("POST", "/message/button_reply", json={
"to": para,
"header": {"title": "Atendimento"},
"text": "Como posso ajudar?",
"footer": "Escolha uma opção",
"buttons": [
{"type": "quick_reply", "id": "pedido", "text": "Meu pedido"},
{"type": "quick_reply", "id": "suporte", "text": "Suporte"},
{"type": "quick_reply", "id": "humano", "text": "Falar com alguém"},
],
})Botões, listas, enquete e figurinha estão entre os recursos que a camada não oficial libera por conectar como o WhatsApp Web — veja todos em recursos da API não oficial.
Uma observação prática: mensagens interativas funcionam melhor em conversas já iniciadas. Se o contato nunca falou com o seu número, abra com um texto antes do menu. O motivo está em aquecer número na API não oficial.
Passo 4: configurar o webhook
Aponte a instância para a sua URL, no formato meta:
call("PUT", "/instance", json={
"allowWebhook": True,
"allowNumber": "all",
"webhookMessage": "https://seu-dominio.com/webhook/wame",
"webhookFormat": "meta",
})No formato meta, cada mensagem chega no mesmo envelope da Meta Cloud API:
{
"object": "wame",
"provider": "whatsapp",
"entry": [{
"changes": [{
"field": "messages",
"value": {
"messages": [{
"from": "5511999999999",
"id": "wamid.XXXX",
"type": "text",
"text": { "body": "oi, quero saber do meu pedido" }
}]
}
}]
}]
}Passo 5: receber no Flask
O extrator, com as guardas que evitam os bugs mais comuns:
def extrair(body: dict):
try:
msg = body["entry"][0]["changes"][0]["value"]["messages"][0]
except (KeyError, IndexError, TypeError):
return None # status de entrega e outros eventos não têm "messages"
if msg.get("type") != "text":
return None
return {
"de": msg["from"],
"texto": msg["text"]["body"],
"id": msg["id"],
}E o servidor. A regra de ouro: responda 200 imediatamente e processe depois. Webhook que demora é reenviado, e o cliente recebe a resposta duas vezes.
# app.py
import threading
from flask import Flask, request
from wame import call
app = Flask(__name__)
processadas = set() # em produção, use Redis ou banco
@app.post("/webhook/wame")
def webhook():
msg = extrair(request.get_json(silent=True) or {})
if msg and msg["id"] not in processadas:
processadas.add(msg["id"])
threading.Thread(target=responder, args=(msg,), daemon=True).start()
return "", 200Passo 6: responder com "digitando…"
Resposta instantânea parece robô. Mostre "digitando…" por um tempo proporcional ao texto antes de enviar:
import random
import time
def responder(msg: dict):
texto = msg["texto"].lower()
if "pedido" in texto:
resposta = "Me passa o número do pedido que eu confiro pra você."
elif "humano" in texto or "atendente" in texto:
resposta = "Certo! Já chamo alguém do time."
else:
enviar_menu(msg["de"])
return
call("POST", "/message/presence", json={"to": msg["de"], "status": "composing"})
time.sleep(min(6, 1 + len(resposta) / 18) * random.uniform(0.8, 1.2))
call("POST", "/message/text", json={"to": msg["de"], "text": resposta})A WAME já aplica tempo humano e limites de ritmo na camada de conexão; o indicador "digitando…" é a parte visível para o cliente, e melhora a experiência. Veja também como simular digitando pela API.
Levando para produção
O código acima funciona, mas três mudanças separam o protótipo do sistema real:
- Servidor WSGI. Rode com
gunicorn app:app, não com o servidor de desenvolvimento do Flask. - Fila em vez de thread. Thread solta perde trabalho quando o processo reinicia. Use RQ, Celery ou uma fila no Redis: o webhook só enfileira e responde 200; um worker processa.
- Idempotência persistente. O
set()em memória some no restart. Guarde os ids tratados em Redis com expiração. O artigo webhook em produção mostra o padrão completo.
Para envios iniciados por você — lembretes, notificações — coloque os envios numa fila com intervalo entre eles, em vez de um for com requests.post. O motivo e o desenho estão em fila, rate limit e retry.
Usando com responsabilidade
A API não oficial é estável e a taxa de bloqueio é muito baixa para quem usa do jeito certo: responder quem chamou, notificar cliente, atender. A WAME não apoia spam — um script Python que percorre uma lista de números desconhecidos é exatamente o que derruba números. Leia uso responsável da API não oficial antes de automatizar envios ativos.
A camada não oficial não é afiliada ao WhatsApp nem à Meta, e o uso é de responsabilidade de quem envia. Se precisar de garantia formal da Meta, compare com a API oficial.
Conclusão
Integrar WhatsApp em Python com a API não oficial da WAME é HTTP puro: requests para enviar texto, imagem e botões, Flask para receber o webhook no envelope da Meta, e um endpoint de presença para o "digitando…". Conecte por QR Code ou código de pareamento, responda 200 na hora, processe em fila e ignore duplicatas. O resto — sessão, reconexão, uptime — fica com a plataforma. A referência completa de endpoints está na documentação.
Pronto para automatizar seu WhatsApp?
Crie sua conta gratuita e comece a enviar mensagens pela API em minutos.
Começar grátisPerguntas frequentes
Existe biblioteca Python para a API do WhatsApp da WAME?+
Você não precisa de biblioteca específica. A API é REST: com o pacote requests você envia mensagens, e com Flask (ou FastAPI, Django) você recebe o webhook. A autenticação é a key da instância na própria URL, sem header extra.
Como conecto meu número usando Python?+
Faça requests.post para https://us.api-wa.me/SUA_KEY/instance para gerar o QR Code e escaneie em Aparelhos conectados no celular. Se preferir não usar QR, envie o número para /instance/pairing-code e digite o código no próprio WhatsApp.
Como recebo mensagens do WhatsApp em Python?+
Configure a URL do webhook da instância com PUT /instance e formato meta. Cada mensagem chega como POST JSON no envelope da Meta Cloud API; no Flask você lê request.get_json(), extrai entry → changes → value → messages e responde 200 imediatamente.
Por que meu bot em Python responde duas vezes a mesma mensagem?+
Normalmente porque o webhook demorou para responder e o evento foi reenviado. Responda 200 na hora, processe em segundo plano (thread, fila ou worker) e guarde os ids das mensagens já tratadas para ignorar duplicatas.
A API não oficial funciona com Python em produção?+
Sim. A infraestrutura é hospedada e gerenciada pela WAME, com 99,9% de uptime; seu código Python só faz chamadas HTTP. Para produção, rode o Flask atrás de um servidor WSGI como gunicorn e use uma fila para o processamento.
Continue lendo
Agente de voz no WhatsApp: latência, interrupção (barge-in) e silêncio
Como deixar um agente de voz no WhatsApp natural: latência, streaming, detecção de fala, interrupção (barge-in), silêncio e eco, com exemplos em Node.js.
Anti-detecção na API não oficial do WhatsApp: como a WAME protege seu número
Como funciona a camada de anti-detecção da API não oficial da WAME: identidade de dispositivo, tempo humano, ritmo de envio, reconexão e monitor de saúde.
API do WhatsApp em C# (.NET): enviar mensagens e receber webhook
Tutorial de API do WhatsApp em C# e .NET: HttpClient tipado, envio de texto, imagem e lista, webhook em ASP.NET Core com fila em background e tratamento de 429.