Entenda os limites de taxa da API do Zenovay e otimize sua integração para confiabilidade e desempenho.
Visão Geral dos Limites de Taxa
A API REST é uma função paga. As chaves de API funcionam apenas nos planos Pro, Scale e Enterprise — as chaves do plano Free são rejeitadas com uma resposta 403 API_PAID_PLAN_REQUIRED.
Limites da API por Plano
Estes limites se aplicam à API REST (endpoints sob /api/external/v1/):
| Plano | Requisições/Minuto | Requisições/Mês |
|---|---|---|
| Pro | 30 | 10 000 |
| Scale | 60 | 100 000 |
| Enterprise | 120 | 1 000 000 |
Os limites são resolvidos a partir da assinatura da sua equipe. Se você pertence a vários times, a API usa o plano mais alto do qual você é membro.
O Que Conta como Requisição
Cada chamada de API autenticada conta como uma requisição contra sua cota mensal — os endpoints de leitura (GET) e o endpoint de consulta (POST /query/:websiteId). A API REST é somente leitura; não há endpoints PUT, PATCH ou DELETE.
O Que Não Conta
As requisições que falham na autenticação (401, por exemplo, uma chave ausente ou inválida) não contam contra sua cota, pois a chave nunca é validada.
Headers de Rate Limit
Headers de Resposta
Cada resposta da API inclui headers de uso:
X-RateLimit-Limit: 30
X-Usage-Monthly: 4521
X-Usage-Limit: 10000
X-Usage-Reset: 2026-07-01T00:00:00.000Z
X-Usage-Total: 89234
| Header | Descrição |
|---|---|
| X-RateLimit-Limit | Seu limite de requisições por minuto |
| X-RateLimit-Remaining | Requisições restantes neste minuto (nem sempre presentes — ver abaixo) |
| X-Usage-Monthly | Requisições usadas neste mês |
| X-Usage-Limit | Limite mensal de requisições |
| X-Usage-Reset | Quando o uso mensal é redefinido (ISO 8601) |
| X-Usage-Total | Total de requisições jamais realizadas com esta chave |
X-RateLimit-Remaining é incluído apenas quando uma contagem exata de requisições restantes está disponível; trate sua ausência como normal e confie em X-RateLimit-Limit mais sua própria contagem de requisições para orçamento.
Lendo os Headers
const response = await fetch(url, { headers });
const limit = response.headers.get('X-RateLimit-Limit');
const remaining = response.headers.get('X-RateLimit-Remaining'); // pode ser null — veja a nota acima
const monthlyReset = response.headers.get('X-Usage-Reset'); // timestamp ISO 8601
console.log(`${remaining ?? '?'}/${limit} requisições restantes este minuto`);
console.log(`O uso mensal é redefinido em ${monthlyReset}`);
Limite de Taxa Excedido
Resposta
Quando o limite por minuto é excedido:
HTTP/1.1 429 Too Many Requests
Retry-After: 42
{
"success": false,
"error": {
"code": "RATE_LIMIT_EXCEEDED",
"message": "Rate limit exceeded (30 requests/minute). Try again in 42 seconds",
"timestamp": "2026-06-13T12:00:00.000Z"
}
}
O 429 por minuto inclui um header Retry-After (em segundos) informando quanto tempo esperar antes de tentar novamente.
Quando a quota monthly é excedida, o corpo do erro usa a mesma forma com "code": "MONTHLY_LIMIT_EXCEEDED". Esta resposta não carrega um header Retry-After — em vez disso, verifique o header X-Usage-Reset, que informa quando sua alocação mensal é redefinida (o primeiro do mês seguinte).
Tratando Erros 429
async function apiRequest(url, options, retries = 3) {
const response = await fetch(url, options);
if (response.status === 429 && retries > 0) {
const retryAfter = response.headers.get('Retry-After') || 60;
console.log(`Rate limited. Waiting ${retryAfter}s...`);
await sleep(retryAfter * 1000);
return apiRequest(url, options, retries - 1);
}
return response;
}
Melhores Práticas
Cache
Armazene em cache as respostas quando possível:
const cache = new Map();
const CACHE_TTL = 5 * 60 * 1000; // 5 minutos
async function getWithCache(endpoint) {
const cached = cache.get(endpoint);
if (cached && Date.now() - cached.time < CACHE_TTL) {
return cached.data;
}
const response = await api.get(endpoint);
cache.set(endpoint, { data: response, time: Date.now() });
return response;
}
Solicite Apenas o Que Precisa
Use o filtro range para limitar os dados retornados:
# Filtrar por um intervalo de tempo específico:
GET /api/external/v1/analytics/WEBSITE_ID?range=7d
Use Filtros de Data
Limite os dados retornados a períodos específicos (valores suportados: 24h, 7d, 30d, 90d, 1y):
# Buscar apenas os últimos 30 dias:
GET /api/external/v1/analytics/WEBSITE_ID?range=30d
Paginação
O endpoint de visitantes pagina com limit e offset (não números de página). limit tem um padrão de 100 e é limitado a 1000.
Requisição
GET /api/external/v1/analytics/WEBSITE_ID/visitors?range=30d&limit=100&offset=0
Resposta
{
"success": true,
"data": {
"visitors": [...],
"pagination": {
"limit": 100,
"offset": 0,
"has_more": true
}
},
"timestamp": "2026-06-13T12:00:00.000Z"
}
Paginação Eficiente
Continue solicitando páginas até que has_more seja false, incrementando offset por limit a cada vez:
async function getAllVisitors(websiteId) {
let allVisitors = [];
let offset = 0;
const limit = 100;
let hasMore = true;
while (hasMore) {
const response = await api.get(
`/analytics/${websiteId}/visitors?limit=${limit}&offset=${offset}`
);
const { visitors, pagination } = response.data;
allVisitors = allVisitors.concat(visitors);
hasMore = pagination.has_more;
offset += limit;
// Respeite os limites de taxa
await sleep(100);
}
return allVisitors;
}
Backoff Exponencial
Implementação
async function requestWithBackoff(fn, maxRetries = 5) {
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
return await fn();
} catch (error) {
if (error.status === 429 && attempt < maxRetries - 1) {
const delay = Math.pow(2, attempt) * 1000 + Math.random() * 1000;
console.log(`Retry ${attempt + 1} after ${delay}ms`);
await sleep(delay);
} else {
throw error;
}
}
}
}
Cronograma de Backoff
| Tentativa | Tempo de Espera |
|---|---|
| 1 | 1-2 segundos |
| 2 | 2-4 segundos |
| 3 | 4-8 segundos |
| 4 | 8-16 segundos |
| 5 | 16-32 segundos |
Monitorando o Uso
Verificar Uso Atual
curl https://api.zenovay.com/api/external/v1/usage \
-H "X-API-Key: zv_YOUR_API_KEY"
Isso retorna sua contagem de requisições mensuais atual, cota restante, limite de taxa por minuto e nível de assinatura.
Cada resposta da API também carrega seu uso ao vivo nos headers (X-Usage-Monthly, X-Usage-Limit, X-Usage-Reset), permitindo rastrear o consumo sem uma chamada separada.
No Painel
Abra Chaves de API (app.zenovay.com/api-keys) para ver suas chaves e os totais de requisições de cada chave, bem como a atividade recente. Use o endpoint GET /usage acima para a contagem mensal precisa e a cota restante.
Otimizando para Alto Volume
Use Endpoints Agregados
Use o endpoint de visão geral de analytics para obter totais de resumo em uma única chamada em vez de paginar por registros de visitantes brutos:
# Requisição única para visão geral de analytics:
GET /api/external/v1/analytics/WEBSITE_ID?range=30d
Isso retorna um bloco summary (total de visitantes, visualizações de página, visitantes únicos) mais daily_stats em uma única resposta. Para desagregações geográficas, por página ou por dispositivo, use os endpoints dedicados /countries, /pages e /technology.
Limites de Taxa Enterprise
Enterprise PlanoOs planos Enterprise começam nos limites publicados mais altos (120 requisições/minuto, 1 000 000 requisições/mês). Se precisar de limites mais altos, envie um email para [email protected] com:
- Seus padrões de uso atuais
- Crescimento esperado
- Detalhes do seu caso de uso
Solução de Problemas
Frequentemente com Rate Limit
Se estiver atingindo o limite por minuto frequentemente:
- Adicione atrasos entre requisições (ou uma fila de requisições)
- Use backoff exponencial em 429s
- Armazene em cache as respostas que reutiliza
- Use endpoints agregados em vez de muitas chamadas pequenas
- Atualize seu plano para um limite mais alto
Cota Mensal Esgotada
Um MONTHLY_LIMIT_EXCEEDED (429) significa que você esgotou sua alocação mensal de requisições. Ele é redefinido no primeiro dia do mês seguinte (veja o header X-Usage-Reset). Atualize seu plano para uma cota mensal mais alta.
Limites Inesperadamente Baixos
Se seus limites parecerem incorretos:
- Verifique seu nível de plano — os limites são baseados na assinatura da sua equipe
- Confirme que está usando uma chave do time correto
- Entre em contato com o suporte