API de SMS e WhatsApp no backend: guia com Node, PHP e Python

Toda aplicação chega ao momento em que precisa falar com o usuário fora da tela: confirmar um pedido, enviar um código de verificação, avisar que a encomenda saiu. Uma API de SMS e WhatsApp resolve isso com uma requisição HTTP a partir do backend, sem depender de celular na mesa nem de automação de navegador.

Este guia mostra como essas APIs funcionam no Brasil (autenticação com token, endpoint de envio, acompanhamento de status), quando escolher cada canal e traz exemplos funcionais em Node.js, PHP e Python usando o gateway da APIBrasil como referência. No fim, as boas práticas que separam um envio que converte de um que vai parar na lista de bloqueio.

Quando usar SMS e quando usar WhatsApp

Os dois canais entregam mensagens no celular, mas com características bem diferentes. O SMS chega em qualquer aparelho, mesmo sem internet e sem app instalado, e é lido quase na hora; em compensação, é só texto, tem limite de caracteres e não permite conversa de verdade. O WhatsApp aceita mídia, botões e respostas, e é onde o brasileiro já conversa; mas exige que o destinatário tenha o app e, na API oficial, que mensagens iniciadas pela empresa usem templates aprovados.

CritérioSMSWhatsApp
AlcanceQualquer celular, sem internetPrecisa do app e de dados
ConteúdoTexto curtoTexto, imagem, PDF, áudio, botões
ConversaLimitadaNatural, em tempo real
Casos típicosCódigo OTP, alertas críticos, avisos curtosAtendimento, notificações ricas, relacionamento
ExigênciasNúmero no formato internacionalOpt-in e templates (oficial) ou número conectado (não oficial)

Para verificação de identidade, a escolha tem nuances: o SMS é universal, mas pode ser alvo de fraude de tráfego; o WhatsApp entrega o código dentro de uma conversa autenticada, mas falha para quem não tem o app. O artigo do aut2fa sobre SMS ou WhatsApp para código de verificação compara custo, taxa de entrega e experiência em cada cenário.

Fora dos sistemas transacionais, o WhatsApp também virou canal de relacionamento para criadores de conteúdo. Um exemplo é avisar o público da live pelo WhatsApp minutos antes de começar, algo que uma lista de transmissão manual não dá conta quando o público cresce e que o backend resolve com um loop e uma fila.

Como funciona uma API de SMS e WhatsApp (token, endpoint, status)

Independentemente do fornecedor, o fluxo é o mesmo e tem quatro partes:

  1. Autenticação: um token de acesso enviado no cabeçalho Authorization: Bearer. No caso do WhatsApp, costuma haver um segundo identificador para o número conectado (na APIBrasil, o cabeçalho DeviceToken).
  2. Envio: um POST com JSON contendo o número de destino no formato internacional (DDI 55 + DDD + número, só dígitos) e o conteúdo da mensagem.
  3. Resposta imediata: indica se a requisição foi aceita e devolve um identificador do envio. Aceita não significa entregue.
  4. Status de entrega: consultado depois, por endpoint de status ou, melhor, recebido por webhook (enviado, entregue, lido, falhou).

Na APIBrasil, o gateway é https://gateway.apibrasil.io/api/v2. O envio de SMS avulso usa POST /sms/send/credits com os campos number e message; o envio de texto no WhatsApp usa POST /evolution/message/sendText com number e text, além do cabeçalho DeviceToken do dispositivo conectado. As respostas trazem um campo error (booleano) e message, com os dados do envio dentro de response.data. A API pode responder HTTP 200 com error: true, então verifique sempre o campo, e não só o status HTTP.

Dois comportamentos valem para qualquer API desse tipo: cada chamada aceita é tarifada, então nunca repita automaticamente uma requisição que falhou com erro 4xx (saldo, número inválido, token errado); e o status da resposta inicial costuma ser algo como processed ou queued, que só vira delivered ou failed depois, no webhook.

Infográfico comparando SMS e WhatsApp no backend: SMS com alcance universal sem internet, WhatsApp com mídia, botões e conversa, opt-in sempre, fila para envios em massa e acompanhamento de status por webhook

Exemplo em Node.js

Com Node 18 ou superior, chamar uma API de SMS e WhatsApp não exige biblioteca: o fetch nativo basta. A função abaixo encapsula a chamada, trata o campo error e devolve o identificador do envio:

// mensageria.js
const GATEWAY = 'https://gateway.apibrasil.io/api/v2';

async function chamarApi(path, body, headersExtras = {}) {
  const res = await fetch(`${GATEWAY}${path}`, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      Authorization: `Bearer ${process.env.APIBRASIL_TOKEN}`,
      ...headersExtras,
    },
    body: JSON.stringify(body),
  });

  const json = await res.json();
  // a API pode responder 200 com error: true — confira o campo, não só o status
  if (!res.ok || json.error) {
    throw new Error(`Falha no envio (${res.status}): ${json.message ?? 'sem detalhes'}`);
  }
  return json.response?.data ?? json; // id e status do envio
}

export function enviarSms(numero, mensagem) {
  return chamarApi('/sms/send/credits', { number: numero, message: mensagem });
}

export function enviarWhatsApp(numero, texto) {
  return chamarApi(
    '/evolution/message/sendText',
    { number: numero, text: texto },
    { DeviceToken: process.env.APIBRASIL_DEVICE_TOKEN }
  );
}

// uso
const envio = await enviarSms('5531999999999', 'Seu pedido #1234 foi confirmado.');
console.log(envio.id, envio.status);

Para envios em massa (avisar mil pessoas da live, por exemplo), não chame a função em um for sem controle: use uma fila (BullMQ com Redis é a escolha mais comum em Node) com concorrência limitada e retentativa apenas para erros de rede ou 5xx. Assim você respeita o limite do fornecedor e não paga duas vezes pela mesma mensagem.

Exemplo em PHP

Em PHP, o Guzzle é o cliente HTTP mais usado e lida bem com JSON e cabeçalhos. Instale com composer require guzzlehttp/guzzle e crie uma classe pequena:

<?php
// Mensageria.php
require 'vendor/autoload.php';

use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;

class Mensageria
{
    private Client $http;

    public function __construct()
    {
        $this->http = new Client([
            'base_uri' => 'https://gateway.apibrasil.io/api/v2/',
            'timeout'  => 15,
            'headers'  => ['Authorization' => 'Bearer ' . getenv('APIBRASIL_TOKEN')],
        ]);
    }

    public function enviarSms(string $numero, string $mensagem): array
    {
        return $this->post('sms/send/credits', ['number' => $numero, 'message' => $mensagem]);
    }

    public function enviarWhatsApp(string $numero, string $texto): array
    {
        return $this->post(
            'evolution/message/sendText',
            ['number' => $numero, 'text' => $texto],
            ['DeviceToken' => getenv('APIBRASIL_DEVICE_TOKEN')]
        );
    }

    private function post(string $path, array $body, array $headers = []): array
    {
        try {
            $res  = $this->http->post($path, ['json' => $body, 'headers' => $headers]);
            $json = json_decode((string) $res->getBody(), true);
        } catch (RequestException $e) {
            throw new RuntimeException('Erro HTTP ao enviar: ' . $e->getMessage());
        }

        if (!empty($json['error'])) {
            throw new RuntimeException('API recusou o envio: ' . ($json['message'] ?? 'sem detalhes'));
        }
        return $json['response']['data'] ?? $json;
    }
}

$m = new Mensageria();
$envio = $m->enviarWhatsApp('5531999999999', 'Olá! Sua live começa em 10 minutos.');
echo $envio['id'] ?? 'enviado', PHP_EOL;

Se preferir um SDK em vez de montar as chamadas, o tutorial de enviar SMS com PHP no blog da APIBrasil mostra o caminho com a biblioteca oficial e os erros mais comuns de configuração. Para quem já usa Guzzle com WhatsApp, o artigo sobre enviar mensagens de texto com a API do WhatsApp em PHP e Guzzle complementa este exemplo.

Exemplo em Python

Em Python, a biblioteca requests é o caminho direto. O exemplo segue o mesmo contrato dos anteriores:

# mensageria.py
import os
import requests

GATEWAY = "https://gateway.apibrasil.io/api/v2"
HEADERS = {"Authorization": f"Bearer {os.environ['APIBRASIL_TOKEN']}"}


def _post(path: str, body: dict, headers_extras: dict | None = None) -> dict:
    res = requests.post(
        f"{GATEWAY}{path}",
        json=body,
        headers={**HEADERS, **(headers_extras or {})},
        timeout=15,
    )
    dados = res.json()
    # confira o campo error: a API pode devolver 200 com erro no corpo
    if not res.ok or dados.get("error"):
        raise RuntimeError(f"Falha no envio ({res.status_code}): {dados.get('message', 'sem detalhes')}")
    return dados.get("response", {}).get("data", dados)


def enviar_sms(numero: str, mensagem: str) -> dict:
    return _post("/sms/send/credits", {"number": numero, "message": mensagem})


def enviar_whatsapp(numero: str, texto: str) -> dict:
    return _post(
        "/evolution/message/sendText",
        {"number": numero, "text": texto},
        {"DeviceToken": os.environ["APIBRASIL_DEVICE_TOKEN"]},
    )


if __name__ == "__main__":
    envio = enviar_sms("5531999999999", "Seu código de acesso expira em 5 minutos.")
    print(envio.get("id"), envio.get("status"))

Para rodar envios agendados ou em lote em Python, Celery com Redis ou RQ cumprem o papel da fila. Se estiver começando na linguagem, o tutorial de Python 3 para iniciantes cobre ambiente virtual, instalação de pacotes e estrutura de projeto.

Boas práticas: opt-in, horário, templates e custo

A parte técnica é a mais fácil. O que define se a mensageria funciona a longo prazo são as regras de uso:

  • Opt-in sempre. Envie apenas para quem autorizou receber aquele tipo de mensagem, guarde a data e a origem do consentimento e ofereça saída fácil (“responda SAIR”). Além de exigência da LGPD, é o que evita bloqueios e denúncias no WhatsApp.
  • Respeite o horário. Notificações transacionais (código, confirmação) podem ir a qualquer hora; mensagens promocionais, não. Defina uma janela e agende o que cair fora dela.
  • Templates e tom. Na API oficial do WhatsApp, mensagens iniciadas pela empresa fora da janela de conversa precisam de template aprovado. Mesmo onde não é exigido, padronize textos curtos, com o nome da empresa e sem encurtadores de link suspeitos.
  • Valide o número antes. Normalize para só dígitos com DDI 55, rejeite tamanhos inválidos e, se possível, confirme por verificação prévia. Cada envio para número errado é dinheiro jogado fora.
  • Controle o custo. SMS e WhatsApp são cobrados por mensagem ou por conversa, conforme o fornecedor e o plano. Consulte a tabela atual, registre o custo por envio nos seus logs e crie alertas de volume anormal.
  • Fila e idempotência. Envios em massa passam por fila com concorrência limitada; cada mensagem tem um identificador próprio para que uma retentativa não gere duplicata.
  • Acompanhe o status por webhook. Grave os eventos de entrega e falha. Taxa de falha subindo em uma operadora ou em um template é sinal para parar e investigar.

Para códigos de verificação em específico, considere um serviço dedicado a OTP (como a aut2fa, que expõe POST /v1/send para disparar o código e POST /v1/send/{id}/verify para validá-lo) em vez de montar a lógica de geração, expiração e tentativas por conta própria. É menos código no seu backend e menos superfície para fraude.

Perguntas frequentes

Preciso de um número próprio para enviar WhatsApp pela API?

Depende do tipo de API. Nas integrações não oficiais (como as baseadas em Baileys ou WhatsMeow), você conecta um número seu via QR code e envia a partir dele. Na API oficial da Meta, o número é verificado e vinculado a uma conta de negócios, e as mensagens ativas usam templates aprovados.

Como sei se a mensagem foi entregue?

A resposta do envio só confirma que a API aceitou a requisição. A entrega chega depois, por webhook ou consulta de status, com estados como enviado, entregue, lido ou falhou. Grave esses eventos no banco para medir a taxa de entrega por canal.

Quanto custa usar uma API de SMS e WhatsApp?

Varia por fornecedor, volume e canal: SMS costuma ser cobrado por mensagem, e WhatsApp por mensagem ou por conversa, dependendo do tipo de API. Consulte a tabela atual do fornecedor antes de estimar e monitore o gasto em produção.

Posso enviar a mesma mensagem para milhares de contatos de uma vez?

Tecnicamente sim, com uma fila; na prática, só para quem deu opt-in. Envio em massa sem consentimento gera denúncias, bloqueio do número e problemas com a LGPD. Segmente, personalize e espalhe os envios ao longo do tempo.

SMS ainda faz sentido com o WhatsApp dominante?

Faz. O SMS é o único canal que chega sem internet e sem app, o que o torna a escolha para códigos de verificação, alertas críticos e fallback quando o WhatsApp falha. O ideal é ter os dois e escolher por tipo de mensagem.

Próximos passos

Crie uma conta no fornecedor, gere o token, guarde-o em variável de ambiente e rode um dos exemplos acima para o seu próprio número. Depois, monte o webhook de status, registre os eventos e só então ligue a fila para envios em lote. Com essa base, trocar de API de SMS e WhatsApp ou adicionar um canal vira questão de ajustar uma função.