Integration guide

Best practices

Six rules that prevent most of what goes wrong in an integration: limits hit, data re-fetched, and readers misled about how current what they are seeing is. The figures on this page are the ones the service applies.

The six rules

Incêndio · Sintra

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

01Required

Say where it came from

Attribute the source

Whoever reads your application has to be able to tell where the data came from and how recent it is. This is not a courtesy: an occurrence shown without a source or a time reads as yours and as current.

Live occurrences30 s
Traffic constraints1 min
ICNF occurrence record5 min
02Wastes quota

Do not poll faster

Respect the intervals

Each family of endpoints has an interval below which the response does not change. Polling faster than that spends quota to receive exactly the same body.

X-RateLimit-Limit: 60
X-RateLimit-Remaining: 12
X-RateLimit-Reset: 1735689600
03Causes failures

The response tells you what is left

Read the quota headers

Every response says what is left in each window. An integration that reads them slows down before hitting the limit; one that does not discovers the limit by receiving a 429.

1s
2s
4s
8s
16s
04Causes failures

Wait longer each time

Back off exponentially

On a 429 or a 5xx, wait and retry at growing intervals with a random jitter. Without the jitter, every client that failed at the same moment comes back in the same second.

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

Say who you are

Identify yourself

A User-Agent that names the integration and allows contact. It is how we tell a legitimate spike from abuse — and how we warn you before limiting the key.

404do not retry
429back off
503retry later
06Correctness

Not every error is alike

Handle each status code

A 404 is not a 429 and neither is a 500. Treating everything as “it failed” makes the application retry what will never work and give up on what would have worked on the second attempt.

The attribution line

Show the source and the time of the last update beside the data, not in a footer or an about page. This is the minimum form:

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

Intervals by family

Below these intervals the response is the same. These are the values the service applies, not a recommendation.

Nature
Live occurrences
Interval
30 s
Endpoints
/prociv/
Nature
Traffic constraints
Interval
1 min
Endpoints
/traffic/
Nature
ICNF occurrence record
Interval
5 min
Endpoints
/icnf/ocorrencia
Nature
Detection and context
Interval
5 min
Endpoints
/fires/ · /context
Nature
Weather warnings
Interval
10 min
Endpoints
/ipma/warnings
Nature
Forecast and risk
Interval
30 min
Endpoints
/ipma/forecast · /ipma/fire-risk
Nature
Historical series and routes
Interval
1 h
Endpoints
/icnf/ · /geo/route
Nature
Reference data
Interval
24 h
Endpoints
/prociv/distritos · /geo/municipality-boundary

Quota headers

On every response. When `Remaining` reaches zero, further requests get a 429 until the moment given in `Reset`.

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

Backoff, in code

The essentials: honour `Retry-After` where present, grow the interval, and add a random jitter.

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

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

    // Honour Retry-After where the server sends one.
    const after = Number(res.headers.get("Retry-After"));
    const backoff = Number.isFinite(after)
      ? after * 1000
      : 2 ** attempt * 1000;

    // Jitter: without it, every client that failed together
    // comes back in the same second.
    await sleep(backoff + Math.random() * 1000);
    return call(url, attempt + 1);
  }

  throw new Error(`request failed: ${res.status}`);
}

User-Agent

The integration's name, its version, and a way to make contact. Keep it stable across versions.

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

What each status means

Code
200
Means
The request succeeded
Do
Nothing. Check the quota headers.
Code
400
Means
Invalid parameter
Do
Fix the request. Retrying will not help.
Code
401
Means
Missing or invalid key
Do
Check the Authorization header. Do not retry in a loop.
Code
403
Means
Endpoint outside the key's scope
Do
Request the extension in the console.
Code
404
Means
The resource does not exist
Do
Treat as absence, not as an error.
Code
429
Means
Window limit reached
Do
Back off. Honour Retry-After.
Code
503
Means
Upstream source unavailable
Do
Retry with backoff. It is not your key.