← Blog

Como enviar imagem no WhatsApp via API (com legenda)

Mensagem de texto resolve aviso simples, mas boa parte das automações precisa de imagem: comprovante de pagamento, foto do produto, etiqueta de envio, cardápio, arte de campanha. Quem já integrou envio de texto costuma travar no primeiro envio de mídia — não porque o endpoint seja difícil, mas porque a mídia depende de algo que o texto não exige: uma URL pública e estável.

Este guia percorre o caminho completo de envio de imagem pela API do Zapixo, incluindo os erros que aparecem em produção e como evitá-los.

Como a API recebe a imagem

Diferente de um formulário web, você não faz upload do arquivo para a API. Você envia a URL onde a imagem já está hospedada, e o servidor busca esse arquivo para entregar ao destinatário.

O endpoint é POST /api/v1/messages/media e o corpo tem quatro campos:

CampoObrigatórioDescrição
instancesimNome da instância conectada no painel
numbersimNúmero no formato internacional, só dígitos
mediaUrlsimURL pública e válida da imagem
captionnãoLegenda, até 1024 caracteres

Um envio mínimo com cURL:

curl -X POST https://zapixo.com.br/api/v1/messages/media \
  -H "Authorization: Bearer SUA_CHAVE" \
  -H "Content-Type: application/json" \
  -d '{"instance":"minha-loja","number":"5511999999999","mediaUrl":"https://cdn.suaempresa.com.br/etiquetas/pedido-4821.jpg","caption":"Sua etiqueta de envio do pedido #4821"}'

Resposta de sucesso:

{
  "success": true,
  "messageId": "3EB0C767D26B8A2F1C4E"
}

O messageId é a identificação da mensagem no WhatsApp. Guarde-o: é por ele que você correlaciona os eventos de entrega e leitura que chegam depois pelo webhook.

O ponto que mais quebra: a URL precisa ser pública

A API busca a imagem a partir da URL que você enviou. Se o servidor de mídia não conseguir baixar o arquivo, a mensagem não sai. Os casos mais comuns de falha:

  • URL atrás de login. Link de Google Drive, Dropbox ou S3 privado que exige sessão ou assinatura não funciona. O que abre no seu navegador logado não abre para um servidor anônimo.
  • URL de rede interna. http://localhost:3000/arquivo.jpg ou um IP privado só existe dentro da sua rede.
  • Link temporário já expirado. URL pré-assinada com validade de 5 minutos pode vencer entre a geração e o envio, principalmente se você usa fila.
  • Certificado TLS inválido. HTTPS com certificado vencido ou autoassinado costuma ser recusado.
  • Redirecionamento em cadeia. Encurtadores que passam por várias etapas antes do arquivo real são frágeis.

A regra prática: abra a URL em uma janela anônima antes de integrar. Se a imagem aparece sem login, serve.

Onde hospedar

Para volume baixo, uma pasta pública no seu próprio domínio já resolve. Para volume ou arquivos gerados dinamicamente, use armazenamento de objetos com acesso público de leitura (S3, R2, Blob) ou um CDN. Se a imagem é sensível e não pode ficar pública para sempre, gere URL pré-assinada com validade generosa — 24 horas, não 5 minutos — e apague depois.

Formatos e tamanho

O WhatsApp aceita imagem em JPEG, PNG e WebP. Duas recomendações que evitam problema:

  1. Prefira JPEG para foto e PNG para arte com texto. WebP funciona, mas alguns aparelhos antigos renderizam pior.
  2. Mantenha o arquivo abaixo de 5 MB. Arquivos grandes aumentam o tempo de busca da mídia e o risco de timeout. Redimensione antes: 1600px no lado maior é suficiente para qualquer tela de celular.

Se você gera a imagem no seu backend — um comprovante, um gráfico, uma etiqueta —, comprima antes de publicar a URL. Uma etiqueta em PNG de 4 MB vira um JPEG de 180 KB sem perda visível.

Legenda: use, mas com limite

O campo caption aceita até 1024 caracteres e aparece logo abaixo da imagem. Vale a pena usá-lo em vez de mandar uma segunda mensagem de texto — é uma requisição a menos, uma notificação a menos no aparelho do cliente, e o contexto fica colado na imagem.

Cuidado com um detalhe: legenda longa é cortada com "Ler mais" na maioria dos aparelhos. Se a informação for essencial, coloque nos primeiros 100 caracteres.

Enviando a partir do seu backend

Exemplo em Node.js, com tratamento de erro:

async function enviarImagem({ instancia, numero, url, legenda }) {
  const res = await fetch("https://zapixo.com.br/api/v1/messages/media", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.ZAPIXO_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      instance: instancia,
      number: numero,
      mediaUrl: url,
      caption: legenda,
    }),
  });

  const data = await res.json();

  if (!res.ok) {
    throw new Error(`Falha ao enviar (${res.status}): ${JSON.stringify(data)}`);
  }

  return data.messageId;
}

Em Python:

import os
import requests

def enviar_imagem(instancia, numero, url, legenda=None):
    resposta = requests.post(
        "https://zapixo.com.br/api/v1/messages/media",
        headers={"Authorization": f"Bearer {os.environ['ZAPIXO_API_KEY']}"},
        json={
            "instance": instancia,
            "number": numero,
            "mediaUrl": url,
            "caption": legenda,
        },
        timeout=30,
    )
    resposta.raise_for_status()
    return resposta.json()["messageId"]

Repare no timeout=30. Envio de mídia é mais lento que texto porque o servidor precisa baixar o arquivo antes de entregar. Timeout curto demais faz seu código desistir de uma mensagem que estava saindo normalmente — e você acaba reenviando, duplicando a mensagem para o cliente.

Os códigos de erro e o que fazer com cada um

CódigoSignificadoAção
400JSON inválido ou campo fora do formatoConfira mediaUrl (precisa ser URL completa) e number (mínimo 10 dígitos)
401Chave ausente, revogada ou fora do formato esperadoGere nova chave no painel
402Limite de mensagens do período de teste atingidoAssine um plano
404Instância não existe ou não pertence à sua contaConfira o nome exato usado no painel
429Passou de 60 requisições por minuto na chaveAguarde a janela reabrir e implemente fila
502O envio falhou no WhatsAppVerifique a URL da mídia e o status da instância

O 502 é o que mais confunde, porque a requisição chegou certa e mesmo assim falhou. Nesse caso a causa quase sempre está fora do seu código: URL de mídia inacessível, número que não existe no WhatsApp ou instância desconectada. Antes de reprocessar, confira o status da instância.

Os cabeçalhos X-RateLimit-Remaining e X-RateLimit-Reset vêm em toda resposta, inclusive nas de erro. Se você dispara em volume, leia esses valores em vez de descobrir o limite pelo 429 — o assunto está detalhado no guia sobre fila e rate limit.

Boas práticas que economizam dor de cabeça

Valide o número antes. Enviar para número que não tem WhatsApp gasta requisição e polui seu log. Use o validador de número ou trate o retorno de erro adequadamente.

Nunca gere a URL da mídia no mesmo instante do envio se houver fila. Publique o arquivo, confirme que está acessível, só então enfileire a mensagem.

Guarde o messageId junto com o pedido. Sem isso, quando o cliente disser "não recebi", você não tem como investigar.

Não reenvie automaticamente em 502 sem checar a causa. Se a instância caiu, o retry vai falhar igual e você vai queimar tentativas. Reenvie depois de confirmar que a conexão voltou.

Respeite o contexto. Imagem não solicitada em massa é o caminho mais rápido para denúncia e bloqueio. As regras que reduzem esse risco estão no guia sobre como evitar banimento.

Resumo

Enviar imagem pela API é um POST com quatro campos, e o único requisito incomum é que a mídia esteja em uma URL que qualquer servidor consiga baixar. Resolvido isso, o resto é o mesmo fluxo do envio de texto: autenticação por chave, respeito ao rate limit e tratamento honesto dos códigos de erro.

Se ainda não tem uma instância conectada, o cadastro no Zapixo inclui período de teste para validar a integração antes de assinar. A referência completa dos endpoints está na documentação.