Limites e idempotência
Rate limit por conta e por IP, cabeçalhos RateLimit-* e a Idempotency-Key que evita cobrança dupla.
Limite de requisições
A API aceita, por padrão, 120 requisições por minuto em produção e 60 com a chave de teste. A contagem é feita em janela de 60 segundos e em duas camadas: por IP de origem (antes da autenticação) e por conta e ambiente (depois dela). Os contadores de teste e de produção são separados: um laço contra o sandbox nunca gasta a cota da integração que está no ar.
Se a sua operação precisar de outro volume, fale com a equipe antes de subir a carga.
Cabeçalhos de limite
Toda resposta traz o estado do limite, para você se ajustar antes de ser barrado:
| Cabeçalho | Conteúdo |
|---|---|
RateLimit-Limit | Teto da janela (ex.: 120). |
RateLimit-Remaining | Quantas requisições ainda cabem nesta janela. |
RateLimit-Reset | Segundos até a janela zerar. |
Retry-After | Só no 429: quantos segundos esperar. Respeite este valor em vez de escolher um intervalo por conta própria. |
HTTP/1.1 429 Too Many Requests
Retry-After: 37
RateLimit-Limit: 120
RateLimit-Remaining: 0
RateLimit-Reset: 37
{
"erro": "Limite de requisições excedido para esta conta. Tente novamente em instantes.",
"codigo": "RATE_LIMITED",
"retryable": true
}429 não consome saldo e não cria consulta: é seguro repetir depois do Retry-After. O que não é seguro é repetir num laço apertado, que mantém a janela sempre cheia.Idempotência: evite cobrança duplicada
O saldo é debitado no POST. Se a resposta se perder no caminho (timeout do seu cliente HTTP, processo reiniciado, deploy no meio da requisição), você não sabe se a consulta foi criada. Repetir o POST às cegas cria uma segunda consulta e um segundo débito.
O cabeçalho Idempotency-Key resolve isso: um identificador seu, único por operação. Se a mesma chave chegar de novo com o mesmo corpo, devolvemos a resposta da primeira, sem criar consulta nova e sem cobrar de novo.
curl -X POST "https://api.zapcarconsulta.com.br/v1/consultas" \
-H "Authorization: Bearer $ZAPCAR_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: pedido-erp-984281" \
-d '{"servico":"consulta","placa":"ABC1D23"}'O que você recebe em cada caso
| Situação | Resposta |
|---|---|
| Primeira vez com a chave | 201: consulta criada e cobrada normalmente. |
| Mesma chave e mesmo corpo, primeira já concluída | 201 com a mesma resposta da primeira (mesmo id). Nada é cobrado de novo. |
| Mesma chave enquanto a primeira ainda está sendo criada | 409 IDEMPOTENCY_IN_PROGRESS, com retryable: true. Espere alguns segundos e repita com a mesma chave. |
| Mesma chave com corpo diferente | 422 IDEMPOTENCY_KEY_REUSED. Nada é criado: uma chave pertence a uma operação só. |
| Chave fora do formato | 400 IDEMPOTENCY_KEY_INVALIDA. |
POST /v1/fipe e POST /v1/fipe/tabela o replay devolve o mesmo 200 da primeira resposta.Regras da Idempotency-Key
| Regra | Comportamento |
|---|---|
| Onde vale | Nos endpoints que cobram: POST /v1/consultas, POST /v1/fipe e POST /v1/fipe/tabela. |
| Formato | De 8 a 255 caracteres. Qualquer texto; recomendamos algo rastreável do seu lado. |
| Escopo | Por conta. A chave de um cliente nunca colide com a de outro. |
| Validade | 30 dias. Depois disso, a mesma chave volta a ser tratada como nova (e cobra). |
| Concorrência | Duas requisições simultâneas com a mesma chave: uma executa e a outra recebe 409. Nunca as duas cobram. |
| Se a criação falhar | A chave é liberada (ex.: saldo insuficiente, validação). Reenviar com a mesma chave conta como tentativa nova. |
| Sem o cabeçalho | Comportamento histórico, sem a proteção. |
| No sandbox | O cabeçalho é aceito e ignorado: no ambiente de teste nada é cobrado. |
Uma chave por tentativa, não por placa
Idempotency-Key protege o retry de rede, não a nova tentativa de uma consulta que falhou. Se o status virou erro, a próxima tentativa é uma consulta nova e precisa de chave nova (pedido-984281-t2): reusar a anterior devolveria, em replay, a consulta que já falhou. Ver o guia de resiliência.Proteção automática contra pedido duplicado
Mesmo sem o cabeçalho, um POST /v1/consultas do mesmo serviço para o mesmo identificador enquanto a consulta anterior ainda está em processamento devolve 200 com reaproveitada: true, valor_cobrado: 0 e o id da consulta existente. Isso cobre o duplo clique e o retry imediato.
{
"id": "6f1c2e2a-1b3d-4a9e-8f7c-2a1b3c4d5e6f",
"servico": "consulta",
"status": "processando",
"valor_cobrado": 0,
"saldo_restante": 465.01,
"resultado_url": "/v1/consultas/6f1c2e2a-1b3d-4a9e-8f7c-2a1b3c4d5e6f",
"reaproveitada": true,
"mensagem": "Essa solicitação já está sendo processada. Você não precisa realizar uma nova."
}O que essa proteção não cobre: a consulta anterior já terminou. Aí um POST novo cria e cobra outra consulta, como deve ser. Para o retry de rede, use a chave.