ZAP-API
PreçosCasos de UsoBlogDocsLogin
Começar grátis
  1. Blog
  2. Multi-instância WhatsApp: arquitetura para escalar sem banimento
Arquitetura

Multi-instância WhatsApp: arquitetura para escalar sem banimento

Como distribuir envios entre múltiplas instâncias WhatsApp para escalar volume, isolar clientes e proteger números críticos de banimento por sobrecarga.

26 de maio de 2026·14 min de leitura·Equipe Editorial ZAP API

"Estamos enviando 4 mil mensagens por dia e o número foi banido". Essa frase aparece, em variações, toda semana em fóruns de marketing digital. A causa é quase sempre a mesma: tudo concentrado em uma única instância, sem warmup gradual, sem distribuição inteligente, sem fallback. Quando o WhatsApp detecta o padrão (volume súbito, taxa de bloqueio acima da média), o número é desligado — junto com toda a operação.

A solução não é mágica nem proibida: distribua. Empresas que mandam 200 mil mensagens/dia operam pools de 80-150 instâncias com roteamento inteligente, isolamento por contexto e warmup automático de números novos. Esse artigo mostra a arquitetura completa.

O limite seguro de uma instância

Não existe número oficial publicado pelo WhatsApp. Mas observação empírica de produção (milhões de mensagens analisadas em 2024-2026):

  • Número novo (semana 1): 50-100 mensagens/dia
  • Número aquecido (1-2 meses): 500-800 mensagens/dia
  • Número maduro (6+ meses): 1.500-2.500 mensagens/dia
  • Número estabelecido com baixa rejeição (1+ ano): até 5.000 mensagens/dia

Acima desses limites, taxa de banimento sobe não-linearmente. A 3.000/dia em número novo, 78% banem em 14 dias.

Arquitetura do pool

Conceito básico

Em vez de uma instância recebendo todo o volume, você tem N instâncias categorizadas. Um router decide qual instância manda cada mensagem baseado em:

  • Maturidade do número (novo/aquecido/maduro)
  • Volume diário acumulado (não estourar limite)
  • Contexto da mensagem (suporte vs marketing vs transacional)
  • Cliente final (instância dedicada por cliente vs compartilhada)
  • Saúde da instância (disconnected_since, error rate, latência)

Categorias de instância

  • HOT: >6 meses, baixa rejeição, vai 1500+/dia. Para campanhas críticas e clientes premium.
  • WARM: 1-6 meses, confiável até 800/dia. Para uso geral.
  • COLD: <30 dias, em warmup, max 200/dia. Para tráfego misto.
  • QUARANTINE: instância com problema recente — não recebe envios novos por 24h.

Implementação do router em Node

import axios from "axios";
import Redis from "ioredis";

const ZAP = axios.create({
  baseURL: "https://api.zap-api.tech/v1",
  headers: { Authorization: `Bearer ${process.env.ZAP_TOKEN}` },
});
const redis = new Redis(process.env.REDIS_URL);

// Pool em memória, atualizado a cada 5min via cron
let pool = [];

async function refreshPool() {
  const { data } = await ZAP.get("/instances");
  pool = data.instances.map((i) => ({
    id: i.id,
    name: i.name,
    category: i.metadata?.category || "WARM",
    dailyLimit: { HOT: 1500, WARM: 800, COLD: 200, QUARANTINE: 0 }[i.metadata?.category || "WARM"],
    status: i.waStatus,
    healthScore: i.healthScore,
  }));
}

async function getDailyCount(instanceId) {
  const key = `pool:count:${instanceId}:${new Date().toISOString().slice(0, 10)}`;
  const v = await redis.get(key);
  return Number(v || 0);
}

async function incDailyCount(instanceId) {
  const key = `pool:count:${instanceId}:${new Date().toISOString().slice(0, 10)}`;
  await redis.incr(key);
  await redis.expire(key, 60 * 60 * 26);
}

async function pickInstance({ priority = "normal" } = {}) {
  // 1. Filtra apenas conectadas e fora de quarentena
  const candidatas = pool.filter(
    (p) => p.status === "CONNECTED" && p.category !== "QUARANTINE"
  );

  // 2. Filtra que ainda têm capacidade hoje
  const comCapacidade = [];
  for (const c of candidatas) {
    const used = await getDailyCount(c.id);
    if (used < c.dailyLimit) {
      comCapacidade.push({ ...c, used, free: c.dailyLimit - used });
    }
  }

  if (comCapacidade.length === 0) {
    throw new Error("POOL_EXHAUSTED — todas instâncias atingiram limite diário");
  }

  // 3. Para prioridade alta, escolhe HOT primeiro
  const orderByPriority = priority === "high"
    ? ["HOT", "WARM", "COLD"]
    : ["WARM", "COLD", "HOT"]; // poupa HOT pra crítico

  for (const cat of orderByPriority) {
    const dessaCat = comCapacidade.filter((c) => c.category === cat);
    if (dessaCat.length === 0) continue;
    // Round-robin ponderado por capacidade livre
    return dessaCat.sort((a, b) => b.free - a.free)[0];
  }
}

export async function sendMessage({ to, type, content, priority = "normal" }) {
  const inst = await pickInstance({ priority });
  const result = await ZAP.post(`/instances/${inst.id}/messages`, {
    to,
    type,
    [type]: content,
  });
  await incDailyCount(inst.id);
  return { messageId: result.data.id, instanceUsed: inst.id };
}

Health check do pool

// Cron a cada 5 minutos: marca quarentena automática
import cron from "node-cron";

cron.schedule("*/5 * * * *", async () => {
  await refreshPool();

  for (const inst of pool) {
    const { data: health } = await ZAP.get(`/instances/${inst.id}/health`);

    // Critério de quarentena: 3+ desconexões na última hora ou error rate >5%
    if (health.disconnectsLastHour >= 3 || health.errorRate24h > 0.05) {
      console.warn(`Quarentenando ${inst.id} (disconnects=${health.disconnectsLastHour})`);
      await ZAP.patch(`/instances/${inst.id}`, {
        metadata: { category: "QUARANTINE", quarantinedSince: new Date().toISOString() },
      });
    }

    // Sai da quarentena depois de 24h se voltar ao normal
    if (
      inst.category === "QUARANTINE" &&
      health.disconnectsLastHour === 0 &&
      Date.now() - new Date(inst.metadata.quarantinedSince).getTime() > 24 * 3600 * 1000
    ) {
      await ZAP.patch(`/instances/${inst.id}`, {
        metadata: { category: "WARM" }, // volta como WARM, precisa re-aquecer pra HOT
      });
    }
  }
});

Warmup de instância nova

Número novo NÃO entra no pool com limite cheio. Ele passa por warmup gradual:

// Programa de warmup: dia 1-30
const warmupSchedule = {
  1: 30,   // dia 1: max 30 msgs
  2: 50,
  3: 80,
  4: 120,
  5: 180,
  7: 250,  // semana 1 fim
  10: 350,
  14: 500, // semana 2 fim
  21: 700, // semana 3 fim
  30: 800, // graduado pra WARM completo
};

async function calcularLimiteWarmup(instanceId) {
  const inst = pool.find((p) => p.id === instanceId);
  const idade = Math.floor(
    (Date.now() - new Date(inst.createdAt).getTime()) / (24 * 3600 * 1000)
  );

  const dias = Object.keys(warmupSchedule).map(Number).sort((a, b) => a - b);
  const aplicavel = dias.filter((d) => d <= idade).pop() || 1;
  return warmupSchedule[aplicavel] || 30;
}

// Substitui dailyLimit fixo no pool
async function refreshPool() {
  const { data } = await ZAP.get("/instances");
  pool = await Promise.all(
    data.instances.map(async (i) => ({
      id: i.id,
      category: i.metadata?.category || "COLD",
      dailyLimit: i.metadata?.category === "COLD"
        ? await calcularLimiteWarmup(i.id)
        : { HOT: 2000, WARM: 1000, QUARANTINE: 0 }[i.metadata?.category || "WARM"],
      // ...
    }))
  );
}

Isolamento por contexto

Mistura de tipos de tráfego degrada qualidade. Separe pools:

  • Pool transacional (confirmação, recibo, OTP): número estável, alta entrega, jamais usado pra marketing
  • Pool suporte (atendimento bidirecional): rotativo, com identificador "suporte" no nome
  • Pool marketing (campanha em massa): assume mais risco, números mais novos, com warmup mais longo
async function sendMessage({ to, type, content, contexto }) {
  // contexto: "transacional" | "suporte" | "marketing"
  const poolFiltrado = pool.filter((p) => p.tags?.includes(contexto));
  const inst = await pickInstanceFrom(poolFiltrado);
  // ...
}

Casos práticos

E-commerce médio: 8 instâncias, 12 mil msg/dia

2 HOT (transacional: pedido confirmado, código rastreio) + 4 WARM (suporte) + 2 COLD em warmup. Roteamento por contexto. Banimento mensal: 0 nos últimos 9 meses.

Agência de marketing: 30 instâncias, 50 mil msg/dia

Pool dividido por cliente final (cada cliente tem 2-4 instâncias dedicadas). Quando cliente cresce, agência aprovisiona instância nova e roda warmup automático. Operação roda 24/7 sem intervenção humana.

SaaS B2B: 12 instâncias, isolamento por feature

3 instâncias só pra OTP (alta criticidade) — número da operadora oficial. 6 pra notificações de produto. 3 pra suporte bidirecional. Falha em 1 instância de OTP escala automaticamente pra fila de SMS como fallback.

FAQ

Quantas instâncias eu preciso?

Regra: divida volume diário esperado por 800 (limite seguro de WARM). Some 30% de margem. Para 10k msgs/dia: 10000/800 × 1.3 = 17 instâncias. Para 50k: 80 instâncias.

Posso usar o mesmo número em várias instâncias?

Não. Um número WhatsApp é único — duas instâncias conectadas no mesmo número geram conflito imediato (uma desconecta a outra). Cada instância = um número diferente.

Como sticky session: cliente sempre conversa com mesma instância?

Hash do telefone do cliente final — mesmo cliente sempre vai pra mesma instância. Implementação: instances[hash(phone) % instances.length]. Útil pra suporte (cliente vê mesmo "atendente"), mas reduz flexibilidade do pool.

Custo de 50 instâncias na ZAP API?

2 primeiras: 2 × R$49 = R$98. 48 seguintes: 48 × R$29 = R$1.392. Total: R$1.490/mês. Por mensagem: R$1.490/50.000 = R$0,0298. Significativamente mais barato que SMS (R$0,15-0,25/msg) e que enterprise (R$0,06-0,12/msg).

O que evita falha em cascata?

Quarentena automática + circuit breaker por instância + dailyLimit por categoria. Quando uma instância falha, ela sai do pool, requests vão pra outras. Quando 30%+ do pool falha simultaneamente (caso raro de bug do gateway), router serve erro explícito pra cliente upstream em vez de tentar mensagens fantasmas.

Pronto para escalar sem banir? A ZAP API atende empresas que precisam de 5 a 200+ instâncias com pricing volumétrico e provisionamento via API. Criar conta grátis e ative reseller pra escalar.

Experimente a ZAP API gratuitamente

7 dias de trial sem precisar de cartão. A partir de R$29/mês*.

Criar instância grátis
EE
Equipe Editorial ZAP APIRevisão técnica

Desenvolvedores e especialistas em integrações WhatsApp. Todo conteúdo passa por revisão técnica para garantir precisão e aplicabilidade.

Ver perfil completoDocumentaçãoTrial grátis

Leia também

Arquitetura · 30 de abr. de 2026 · 14 min

Como escalar WhatsApp para 100 mil mensagens/dia: arquitetura, fila e custo real

Limites por instância, cálculo de quantas você precisa, outbox pattern, circuit breaker, warmup pool e 4 métricas vitais. Custo unitário de R$ 0,0009/msg.

Arquitetura · 19 de mai. de 2026 · 13 min

Rate limiting em WhatsApp API: throttle inteligente por sessão

Implemente rate limiting inteligente para disparos WhatsApp: throttle dinâmico por número, janelas deslizantes e backoff automático para evitar banimento.

Arquitetura · 30 de mai. de 2026 · 13 min

Mensagem idempotente no WhatsApp: nunca envie a mesma mensagem duas vezes

Como implementar idempotência em disparos WhatsApp para evitar mensagens duplicadas em retries, falhas de rede e race conditions em sistemas distribuídos.

Arquitetura · 15 de mai. de 2026 · 13 min

Circuit breaker em WhatsApp API: proteja sua instância de sobrecarga

Implemente circuit breaker para sua integração WhatsApp: evite cascata de falhas, proteja sua instância de rate limiting e garanta degradação graciosa.

Tópicos:Chatbots com IAE-commerceAPI WhatsApp

Explore também

Casos de usoWhatsApp API por segmentoComparativoZAP API vs alternativasPreçosPlanos e o que está inclusoGlossárioTermos técnicos de WhatsApp API
ZAP-API

API REST para WhatsApp com webhooks assinados, Meta Pixel/CAPI e compliance LGPD. Sem aprovação da Meta.

Status operacional🇧🇷 Feito no Brasil

Produto

  • Preços
  • Casos de uso
  • Comparativo
  • Trial grátis
  • Dashboard

Recursos

  • Documentação
  • Blog
  • Glossário
  • RSS Feed

Empresa

  • Sobre
  • Imprensa
  • Termos de uso
  • Privacidade
  • Criar conta
  • Login

Contato

  • [email protected]
  • [email protected]
  • Resposta em até 24h úteis
© 2026 ZAP-API — Todos os direitos reservados·CNPJ 42.130.949/0001-56·Termos·Privacidade

Desenvolvido por PreviusIA