Synergy Connect

Documentação

Limites de taxa

Toda recusa por limite é um 429 com Retry-After (segundos). Nunca vira 5xx.

Os limites#

LimiteValorChave do limiteResposta
Borda (envio, mídia, handoff, token de Flow)2.000 requisições a cada 10 schave de API × phone_number_id, por localidade da Cloudflare429 Graph, código 130429, Retry-After: 10
Gestão de webhooks (/v1/webhooks…)30 requisições a cada 60 schave de API429 { "error": "Rate limit hit" }, Retry-After: 60
Vazão do número, mensagens automatizadas64 por segundo (rajada de 64)número (todas as chaves juntas)429 Graph, código 130429, Retry-After em segundos (mínimo 1)
Vazão total do número80 por segundo, divididos com os atendentesnúmeroidem
Envios em andamento200 por númeronúmeroidem
Volume do cicloo do plano (limite rígido)organização429 Graph, código 1390006, Retry-After: 60
Id novo na rota de envio e das Jornadas (Instagram ou WhatsApp?)30 ids novos por minutochave de API, por servidor429 Graph, código 130429, Retry-After: 1 (veja abaixo)
Respostas privadas a comentários (Instagram)10 % da fila e da vazãoconta429 Graph, código 130429, Retry-After: 60 com a fila cheia
Respostas privadas seguidas recusadas pela Meta (Instagram)20 em sequênciachave de API × conta429 Graph, código 130429, Retry-After até a pausa de 1 hora acabar
Envelopes de webhook de uma conta do Instagram60 por minutoendpoint × contao excedente é descartado e contado

Quando o número acabou de recusar por vazão, o 429 se repete por 200 ms (Retry-After: 1) sem consultar o número de novo.

O limite de volume do ciclo só sai com uma recarga, upgrade ou o próximo ciclo: esperar o Retry-After não o resolve. O details do erro traz o link da recarga.

Instagram#

O Instagram usa os mesmos limites do WhatsApp, com a conta no lugar do número: a borda (2.000 requisições a cada 10 s por chave × ig-user-id), a vazão da conta (as mensagens automatizadas dividem os 64 por segundo) e o volume do ciclo do plano. Em cima deles:

  • Id novo. A chave syn_ só carrega a organização, então um {id} que o servidor ainda não conhece é tentado como conta do Instagram, uma vez. Cada chave tem 30 tentativas por minuto em cada servidor, e a tentativa volta quando o id é seu (uma conta do Instagram que respondeu, ou um número de WhatsApp da organização). Só um id que não é seu gasta a tentativa. Sem tentativa disponível, o envio de um número válido de WhatsApp acontece normalmente, e um id que a Synergy não reconhece recebe 429 com Retry-After: 1 em vez de 404: sem a tentativa ela não sabe dizer que o id não existe. Repita depois de 1 segundo. Um envio de uma organização com dezenas de números, logo depois de uma atualização da plataforma, nunca recebe 404 por causa disso.
  • Resposta privada a comentário. A API usa no máximo 10 % da fila e da vazão que as Jornadas dividem com ela. Depois de 20 respostas seguidas que a Meta recusa por o comentário não ser elegível, a chave fica 1 hora sem resposta privada.
  • Webhooks. Uma conta entrega no máximo 60 envelopes por minuto a cada endpoint (Instagram).
  • Jornadas. POST …/journeys/{journey_id}/runs obedece aos tetos da própria Jornada (por pessoa no dia, por hora, reentrada e o disjuntor): um teto atingido responde 429 com Retry-After: 1 (Jornadas).

Cabeçalhos devolvidos#

A API devolve apenas Retry-After, e só nos 429. Não há cabeçalhos X-RateLimit-*: não é possível consultar o saldo restante. Controle a taxa do lado da integração.

Backoff#

  1. Em 429, espere os segundos de Retry-After (no mínimo) antes de repetir.
  2. Acrescente jitter aleatório para vários processos não voltarem juntos.
  3. Em 503 (código 2), repita com espera exponencial (1 s, 2 s, 4 s…) e um teto de tentativas.
  4. Ao repetir um envio, use Idempotency-Key para a mensagem chegar à Meta uma vez só.
  5. Não repita 4xx que não sejam 429: o resultado será o mesmo.
Exemplo

curl

for i in 1 2 3 4 5; do
  code=$(curl -s -o resposta.json -D cabecalhos.txt -w '%{http_code}' -X POST \
    'https://api.synergyconnect.com.br/v25.0/106540352242922/messages' \
    -H "Authorization: Bearer $SYNERGY_API_KEY" \
    -H 'Content-Type: application/json' \
    -H 'Idempotency-Key: pedido-42-saiu' \
    -d '{"messaging_product":"whatsapp","to":"5511999999999","type":"text","text":{"body":"oi"}}')
  [ "$code" != "429" ] && break
  espera=$(grep -i '^retry-after:' cabecalhos.txt | tr -dc '0-9')
  sleep "${espera:-1}"
done

JavaScript

async function sendWithBackoff(url, init, attempts = 5) {
  for (let i = 0; i < attempts; i++) {
    const res = await fetch(url, init);
    if (res.status !== 429 && res.status < 500) return res;
    const retryAfter = Number(res.headers.get('Retry-After')) || 2 ** i;
    const jitter = Math.random() * 500;
    await new Promise((r) => setTimeout(r, retryAfter * 1000 + jitter));
  }
  throw new Error('limite de tentativas');
}

Python

import random
import time

import requests


def send_with_backoff(url, attempts=5, **kwargs):
    for i in range(attempts):
        res = requests.post(url, **kwargs)
        if res.status_code != 429 and res.status_code < 500:
            return res
        wait = int(res.headers.get("Retry-After", 2**i))
        time.sleep(wait + random.random() * 0.5)
    raise RuntimeError("limite de tentativas")

Ficou com dúvida? Escreva para suporte@synergyconnect.com.br.