# Travel Risk API — changelog

All changes to the public API. We add fields; we do not rename or remove them without
notice. If you parse our JSON strictly (rejecting unknown keys), read the "Added"
entries — a new field is the most likely thing to surprise your client.

Machine-readable: https://api.travelriskapi.com/openapi.json

---

## 2026-08-26

**Fixed — `risk_score` disagreed between endpoints.**
`/api/v1/countries` returned a stored value while `/api/v1/risk-score/{iso}` computed a
composite, so the same country could report `0.0` in one place and `5.0` in the other
(reported by a user, affected CIV, COD, SXM, XKX). Both endpoints now share one
implementation. The composite is `advisory_level + (Critical x1.0 + High x0.5 +
Medium x0.25)`, capped at 5.0; Low-severity alerts contribute nothing.

**Added — `/api/v1/countries`:** `base_risk_score` (advisory level alone) and
`active_alerts`, so a score can be explained without a second call.

**Added — alert change tracking.** Alerts keep a **stable `id`** when an upstream
revises them; we update the record in place and bump the new `updated_at` field.
Previously a revised alert was skipped entirely, so an escalated severity never
reached clients.

**Added — `?updated_since=<ISO-8601>` on `/api/v1/alerts`** for incremental sync:
returns only alerts created or revised since that timestamp. Invalid values return 422.

**Added — `?days=1..365` on `/api/v1/alerts`.** Note: `days` was previously accepted
and silently ignored — if you were passing it and expecting a filter, you were getting
every active alert. It now filters by `event_date`.

**Fixed — conflict events had stopped updating.** An upstream moved to HTTPS and our
fetch failed on the redirect.

**Fixed — Canada advisories.** That source had been returning zero countries; now 214.
`/api/v1/advisories` therefore carries a third government source for many countries —
if you render sources or compute a maximum level, expect three where there were two.

## 2026-08-11

**Fixed — invalid ISO country codes.** Some records carried codes that were not ISO
3166-1 alpha-3, and two were plain wrong: `TUR` held Turks-and-Caicos data instead of
Türkiye, and `MAR` held the Marshall Islands instead of Morocco. All corrected.
The legacy codes we used to emit are still **accepted as input** indefinitely, so
existing integrations keep working.

**Added — `/api/v1/countries/codes`:** every supported code in one response, no
pagination, for validating input locally.

**Added — alpha-2 country codes are now accepted** wherever a country is taken
(`US` as well as `USA`). Previously they returned 404.

**Added — `data_updated_at` and `generated_at`** on list responses, so you can tell how
fresh an answer is.

**Changed — page size limit raised from 100 to 1000** on all list endpoints. Existing
callers passing `limit<=100` are unaffected; a full export of ~19,000 alerts is now 20
requests instead of 192.

**Changed — responses are compressed** (gzip/zstd) when your client advertises support.

**Changed — the shared demo key is limited to 3 requests/day per IP.** Register a free
key (100 requests/day, no card) at `POST /api/v1/register`.

**Added — quota headers** on every response: `X-Quota-Limit`, `X-Quota-Used`,
`X-Quota-Remaining`, `X-Credits-Balance`.

---

## Conventions

- **API keys do not expire.** A free key stays valid indefinitely; we do not issue
  time-limited trial credentials.
- **Additive changes only.** New fields may appear at any time. Existing fields keep
  their name, type and meaning; if one ever has to change, it will be announced here
  first and the old form kept for a transition period.
- **Trailing slashes redirect.** `/api/v1/alerts/` returns a 307 to `/api/v1/alerts`.
  Most HTTP clients follow it, but prefer the form without the trailing slash.
- Questions: support@travelriskapi.com
