Guia de integração

Boas práticas

Seis regras que evitam a maior parte do que corre mal numa integração: limites atingidos, dados repetidos, e leitores induzidos em erro sobre a actualidade do que estão a ver. Os valores desta página são os que o serviço aplica.

As seis regras

Incêndio · Sintra

Fonte: ANEPC · via api.emcurso.pt · 14:32

01Obrigatório

Diga de onde vem

Atribua a fonte

Quem lê a sua aplicação tem de conseguir saber de onde vem o dado e quão recente é. Não é uma cortesia: uma ocorrência apresentada sem fonte nem hora lê-se como sendo sua e como sendo de agora.

Ocorrências ao vivo30 s
Constrangimentos de trânsito1 min
Ficha de ocorrência do ICNF5 min
02Desperdiça quota

Não peça mais depressa

Respeite os intervalos

Cada família de endpoints tem um intervalo abaixo do qual a resposta não muda. Pedir mais depressa do que isso gasta quota para receber exactamente o mesmo corpo.

X-RateLimit-Limit: 60
X-RateLimit-Remaining: 12
X-RateLimit-Reset: 1735689600
03Causa falhas

A resposta diz-lhe quanto resta

Leia os cabeçalhos de quota

Cada resposta diz quanto resta em cada janela. Uma integração que os lê trava antes de bater no limite; uma que não os lê descobre o limite ao receber 429.

1s
2s
4s
8s
16s
04Causa falhas

Espere mais de cada vez

Recue com backoff

Perante 429 ou 5xx, espere e volte a tentar com intervalos crescentes e uma variação aleatória. Sem essa variação, todos os clientes que falharam ao mesmo tempo voltam no mesmo segundo.

User-Agent:
CamaraDeSintra-Despacho/1.4
(+https://cm-sintra.pt)
05Obrigatório

Diga quem é

Identifique-se no User-Agent

Um User-Agent que nomeie a integração e permita o contacto. É por aí que distinguimos um pico legítimo de um abuso — e por aí que o avisamos antes de limitar a chave.

404não repita
429recue
503repita depois
06Correcção

Nem todo o erro é igual

Trate cada código

Um 404 não é um 429 e nenhum dos dois é um 500. Tratar tudo como «falhou» faz a aplicação repetir o que nunca vai resultar e desistir do que resultaria à segunda.

A linha de atribuição

Apresente a fonte e a hora da última actualização junto do dado, não num rodapé nem numa página «sobre». Esta é a forma mínima:

html
<p class="attribution">
  Fonte: ANEPC · via api.emcurso.pt · {{ultimaActualizacao}}
</p>

Intervalos por família

Abaixo destes intervalos a resposta é a mesma. São os valores que o serviço aplica, não uma recomendação.

Natureza
Ocorrências ao vivo
Intervalo
30 s
Endpoints
/prociv/
Natureza
Constrangimentos de trânsito
Intervalo
1 min
Endpoints
/traffic/
Natureza
Ficha de ocorrência do ICNF
Intervalo
5 min
Endpoints
/icnf/ocorrencia
Natureza
Deteção e contexto
Intervalo
5 min
Endpoints
/fires/ · /context
Natureza
Avisos meteorológicos
Intervalo
10 min
Endpoints
/ipma/warnings
Natureza
Previsão e risco
Intervalo
30 min
Endpoints
/ipma/forecast · /ipma/fire-risk
Natureza
Séries históricas e rotas
Intervalo
1 h
Endpoints
/icnf/ · /geo/route
Natureza
Dados de referência
Intervalo
24 h
Endpoints
/prociv/distritos · /geo/municipality-boundary

Cabeçalhos de quota

Em cada resposta. Quando `Remaining` chega a zero, os pedidos seguintes recebem 429 até ao instante indicado em `Reset`.

http
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 47
X-RateLimit-Reset: 1735689600
Retry-After: 23

Backoff, em código

O essencial: respeitar `Retry-After` quando existe, crescer o intervalo, e somar uma variação aleatória.

javascript
async function call(url, tentativa = 0) {
  const res = await fetch(url, { headers });
  if (res.ok) return res.json();

  if (res.status === 429 || res.status >= 500) {
    if (tentativa >= 5) throw new Error(`desisti: ${res.status}`);

    // Respeitar o Retry-After quando o servidor o envia.
    const after = Number(res.headers.get("Retry-After"));
    const espera = Number.isFinite(after)
      ? after * 1000
      : 2 ** tentativa * 1000;

    // Variação aleatória: sem ela, todos os clientes que
    // falharam ao mesmo tempo voltam no mesmo segundo.
    await sleep(espera + Math.random() * 1000);
    return call(url, tentativa + 1);
  }

  throw new Error(`pedido falhou: ${res.status}`);
}

User-Agent

Nome da integração, versão, e uma forma de contacto. Mantenha-o estável entre versões.

http
User-Agent: CamaraDeSintra-Despacho/1.4 (+https://cm-sintra.pt)

O que cada estado significa

Código
200
Significa
O pedido resultou
Faça
Nada. Verifique os cabeçalhos de quota.
Código
400
Significa
Parâmetro inválido
Faça
Corrija o pedido. Repetir não resolve.
Código
401
Significa
Chave em falta ou inválida
Faça
Verifique o header Authorization. Não repita em ciclo.
Código
403
Significa
Endpoint fora do âmbito da chave
Faça
Peça o alargamento na consola.
Código
404
Significa
O recurso não existe
Faça
Trate como ausência, não como erro.
Código
429
Significa
Limite da janela atingido
Faça
Recue. Respeite Retry-After.
Código
503
Significa
Fonte a montante indisponível
Faça
Repita com backoff. Não é da sua chave.