# 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-10-08

**Added — one key for risk and aviation data.** Every API key, including free keys, now also opens the aviation API:
AirLabs-compatible `/api/v9`, AeroDataBox-compatible `/adb` and `/ext/v1` (in-flight Wi-Fi and Starlink, airline
safety, US airport security wait times, border waits, fare history, IP geolocation). Reference:
https://api.travelriskapi.com/ext/reference/. Existing keys gained access automatically; nothing to change.

**Added — Starter plan**, $29/month: 2,000 risk requests/day and 60,000 aviation calls/month, commercial use.

**Added — aviation limits per plan.** Aviation calls per month: Free 3,000, Starter 60,000, Pro 300,000, Enterprise
unlimited. Under heavy load a flight lookup may return `upstream_budget_exhausted` (429 on `/adb` and `/ext/v1`); retry later or
use a cached result.

**Unchanged:** risk endpoints, their quotas and response shapes. Free keys keep 100 risk requests/day.

---

## 2026-09-24

**Fixed — foreign conflicts attributed to bystander countries.** GDELT tags an event
with the country it could geolocate, and when that is only country-level it is often
the country of *mention*, not of action — a report about China and Japan surfaced as
an armed conflict in Germany. `/api/v1/conflicts` now drops an event when its location
is country-level and both actors are countries other than the tagged one. 226
historical events removed (≈0.1%); anything with a local or neighbouring actor is kept.

**Fixed — `country_iso` on `/api/v1/conflicts` and `/api/v1/advisories`.** The docs
listed `country_iso` but the endpoints only read `country`, so a documented request
was silently unfiltered. Both names now work; `country` is kept indefinitely and marked
deprecated in OpenAPI. Both endpoints also accept alpha-2 (`DE`) and the legacy codes
this API issued before 2026-08-11, matching `/countries` and `/alerts`.

**Added — `/api/v1/conflicts`:** `filters.country_iso` in the response echoes the
normalised code actually applied. `filters.country` is unchanged.

---

## 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
