Políticas de Rate Limit
A API do TapSign limita a quantidade de requisições para manter o serviço estável e disponível para todos.
Limite por chave de API
Requisições autenticadas com chave de API (Authorization: Bearer tsk_...) podem fazer 60 requisições por minuto por chave, somando todas as rotas. O limite é o mesmo em todos os planos.
| Ambiente | Limite por chave |
|---|---|
| Produção | 60 requisições/minuto |
| Sandbox | 120 requisições/minuto |
A cota da chave é renovada por inteiro a cada minuto. Não é uma janela deslizante: se você gastar as 60 requisições nos primeiros segundos, as próximas recebem 429 até a cota ser renovada.
Rotas com limite menor
Algumas rotas têm um teto próprio, que também vale para chaves de API. Nelas a requisição precisa caber nos dois limites: o da chave e o da rota.
| Rota | Limite |
|---|---|
POST /v1/webhooks/{webhookId}/logs/{logId}/retry | 10 requisições/minuto |
GET /v1/models/{id}/pdf-preview | 10 requisições/minuto |
Requisições sem chave de API
Requisições com token JWT, ou em rotas públicas, são limitadas por IP, com um contador por tipo de requisição. Em produção:
| Requisição | Limite por IP |
|---|---|
GET | 400/minuto |
POST | 40/minuto |
PUT e PATCH | 80/minuto |
DELETE | 20/minuto |
POST /v1/auth/api/token | 5/minuto |
POST /v1/auth/api/token-refresh | 10/minuto |
GET /v1/webhooks/{webhookId}/logs | 60/minuto |
POST /v1/webhooks/{webhookId}/logs/{logId}/retry | 10/minuto |
GET /v1/models/{id}/pdf-preview | 10/minuto |
No sandbox esses limites são mais altos (veja Ambiente de Testes).
Com chave de API, o limite é da chave e não depende do IP. Com JWT, todas as chamadas que saem do mesmo IP dividem os mesmos contadores.
Headers de Rate Limit
As respostas trazem o estado da cota que se aplica à requisição:
| Header | Descrição | Exemplo |
|---|---|---|
X-Rate-Limit-Limit | Tamanho da cota que se aplica à requisição | 60 |
X-Rate-Limit-Remaining | Requisições que ainda cabem na cota atual | 59 |
Retry-After | Só na resposta 429: segundos para tentar de novo | 60 |
Não existe header com o horário de renovação da cota. Em rotas com limite próprio, os headers mostram a cota mais apertada no momento (a da chave ou a da rota).
Exemplo de headers em uma resposta
HTTP/2 200
content-type: application/json
x-rate-limit-limit: 60
x-rate-limit-remaining: 59
Resposta 429: rate limit excedido
Quando a cota acaba, a API responde 429 Too Many Requests:
HTTP/2 429
content-type: application/json
x-rate-limit-limit: 60
x-rate-limit-remaining: 0
retry-after: 60
{
"title": "Rate limit exceeded!",
"status": 429,
"details": "Too many requests. Please wait a moment and try again.",
"timestamp": "2026-09-12T14:31:05Z",
"devMsg": "RateLimitFilter: Too many requests. Please retry after the Retry-After period.",
"fields": {}
}
Com o header Accept-Language: pt-BR, title e details vêm em português ("Limite de requisições excedido!" e "Muitas requisições. Por favor, aguarde um momento e tente novamente.").
Requisições que recebem 429 não são processadas. Reenvie depois de aguardar o tempo indicado em Retry-After.
Boas práticas
1. Monitore os headers
Acompanhe X-Rate-Limit-Remaining para desacelerar antes de chegar a zero.
async function callTapSignAPI(url, options) {
const response = await fetch(url, options);
const remaining = Number(response.headers.get('X-Rate-Limit-Remaining'));
if (remaining < 10) {
console.warn(`Rate limit baixo: ${remaining} requisições restantes`);
}
return response;
}
2. Implemente retry com espera
Ao receber 429, espere o tempo de Retry-After antes de reenviar. Sem o header, use backoff exponencial:
async function fetchWithRetry(url, options, maxRetries = 3) {
for (let attempt = 0; attempt <= maxRetries; attempt++) {
const response = await fetch(url, options);
if (response.status !== 429) {
return response;
}
if (attempt === maxRetries) {
throw new Error('Rate limit excedido após várias tentativas');
}
const retryAfter = response.headers.get('Retry-After');
const waitMs = retryAfter
? Number(retryAfter) * 1000
: Math.pow(2, attempt) * 1000 + Math.random() * 1000;
console.log(`Rate limit atingido. Aguardando ${waitMs} ms...`);
await new Promise(resolve => setTimeout(resolve, waitMs));
}
}
import time
import random
import requests
def fetch_with_retry(url, headers, max_retries=3):
for attempt in range(max_retries + 1):
response = requests.get(url, headers=headers)
if response.status_code != 429:
return response
if attempt == max_retries:
raise Exception("Rate limit excedido após várias tentativas")
retry_after = response.headers.get("Retry-After")
wait_time = int(retry_after) if retry_after else (2 ** attempt) + random.uniform(0, 1)
print(f"Rate limit atingido. Aguardando {wait_time}s...")
time.sleep(wait_time)
3. Distribua as requisições
Evite rajadas. Com 60 requisições por minuto, processe em lotes pequenos com intervalo:
const headers = { Authorization: `Bearer ${process.env.TAPSIGN_API_KEY}` };
// Ruim: 100 requisições simultâneas estouram a cota
await Promise.all(ids.map(id => fetch(`https://api.tapsign.com.br/v1/envelopes/${id}`, { headers })));
// Bom: lotes de 10 a cada 10 segundos (60 por minuto)
async function processInBatches(ids, batchSize = 10, delayMs = 10000) {
const results = [];
for (let i = 0; i < ids.length; i += batchSize) {
const batch = ids.slice(i, i + batchSize);
const batchResults = await Promise.all(
batch.map(id => fetch(`https://api.tapsign.com.br/v1/envelopes/${id}`, { headers }))
);
results.push(...batchResults);
if (i + batchSize < ids.length) {
await new Promise(resolve => setTimeout(resolve, delayMs));
}
}
return results;
}
4. Use webhooks em vez de polling
Em vez de consultar o status de um envelope repetidamente, configure webhooks para ser avisado quando algo mudar.
Webhooks não consomem o seu rate limit: é o TapSign que chama a sua aplicação.
5. Faça cache de respostas
Para dados que mudam pouco (como a lista de modelos), guarde a resposta localmente e evite requisições repetidas.
Próxima seção: Status de Erros: entenda as respostas de erro da API.