Docs

Using the Wildfire Data API

Every dataset is a set of static files behind one base URL. There is nothing to install and no key to request.

Quickstart

The catalog lists the datasets; each dataset's metadata links to its files.

curl -s "https://api.wildfiredata.dev/v1/catalog"
curl -s "https://api.wildfiredata.dev/v1/active-fires"
curl -sO "https://api.wildfiredata.dev/v1/active-fires/download/active-fires.csv"

URL scheme

All paths start at https://api.wildfiredata.dev. Data paths end in a format extension.

PathWhat it returns
/v1The API index: site name and links.
/v1/catalogEvery dataset on the site with its current version and coverage.
/v1/statusFreshness of every dataset (see the status page).
/v1/{dataset}Metadata: version, coverage, source, license, validation and downloads.
/v1/{dataset}/versionsThe current version, previous versions and monthly snapshots.
/v1/{dataset}/locationsSeries ids with their location ids, and location names. For tables: the partitions.
/v1/{dataset}/{series}/{location}.jsonOne series at one place (also .csv).
/v1/{dataset}/table/{partition}.jsonRows of a table for one partition value, or table/all for tables up to 50,000 rows (also .csv).
/v1/{dataset}/download/{dataset}.csvThe whole dataset in one file (also .parquet).
/v1/{dataset}/{path}A file of a files dataset, at the path its metadata lists.

The API reference lists the exact URLs for every dataset on this site.

IDs

Dataset and series ids are lowercase and kebab-case. Location ids follow one scheme across all the sites:

PlaceFormatExample
The worldworldworld
CountryISO 3166-1 alpha-3, lowercaseusa, twn
US state or equivalentus- and the USPS codeus-ca, us-dc, us-pr
US countyus-county- and the 5-digit FIPS codeus-county-06037
Station or monitorsource prefix and the source's idusgs-01646500, aqs-060371103

Formats

JSON

A series file has a meta object and a data array ordered by date:

{
  "meta": {
    "dataset": "{dataset}",
    "series": "{series}",
    "series_name": "{series name}",
    "location": "{location}",
    "location_name": "{location name}",
    "units": { "value": "{unit}" },
    "source": "…", "license": "…", "attribution": "…"
  },
  "data": [
    { "date": "YYYY-MM", "value": <number> },
    …
  ]
}

A table file has meta and a rows array ordered by the key columns, as in Active fires (US and Canada):

{
  "meta": {
    "dataset": "active-fires",
    "key": ["event_id"],
    "partition": { "country": "…" },   // absent in table/all
    "rows": <integer>,
    "units": { "latitude": "degrees", "longitude": "degrees", "min_latitude": "degrees", "max_latitude": "degrees", "min_longitude": "degrees", "max_longitude": "degrees", "max_frp": "MW", "incident_acres": "acres", "incident_percent_contained": "percent", "incident_distance_km": "km" },
    "source": "…", "license": "…", "attribution": "…"
  },
  "rows": [
    { "event_id": <string>, "country": <string>, "region": <string>, "region_name": <string>, "first_detection": "YYYY-MM-DDTHH:MM:SSZ", "last_detection": "YYYY-MM-DDTHH:MM:SSZ", "detections": <integer>, "raw_detections": <integer>, "low_confidence_detections": <integer>, "satellites": <string>, "latitude": <number>, "longitude": <number>, "min_latitude": <number>, "max_latitude": <number>, "min_longitude": <number>, "max_longitude": <number>, "max_frp": <number>, "days_detected": <integer>, "likely_static_source": <true|false>, "incident_irwin_id": <string> or null, "incident_name": <string> or null, "incident_type": <string> or null, "incident_acres": <number> or null, "incident_percent_contained": <number> or null, "incident_discovered": "YYYY-MM-DDTHH:MM:SSZ" or null, "incident_link": <string> or null, "incident_distance_km": <number> or null },
    …
  ]
}

Missing values are null, never sentinel numbers. Dates are strings: YYYY-MM for months, YYYY-MM-DD for days, YYYY for years and ISO 8601 UTC ending in Z for times.

CSV

UTF-8, a header row, comma-separated, \n line endings, and an empty field for a missing value. The same columns as the JSON.

Parquet

Zstandard-compressed with column statistics, so tools such as polars and DuckDB can read only the row groups they need over HTTP range requests. Bulk Parquet files are usually a fraction of the size of the CSV.

Pinned versions

Each published update is a version with an id like 2026-10-23.3f9a1c2e4b: the publish date and the start of a hash of every file. Plain URLs always serve the current version. Put a version after the dataset id to pin it:

curl -s "https://api.wildfiredata.dev/v1/active-fires/versions"

# the same data, frozen at one version
curl -s "https://api.wildfiredata.dev/v1/active-fires@2026-10-23.3f9a1c2e4b"
  • /v1/{dataset}@{version}/... works for every path of a dataset: metadata, series, tables and downloads.
  • Pinned responses never change and are cached as immutable for a year.
  • The first version of each month is a snapshot kept permanently: cite those in papers and reports. Other versions stay available for at least 30 days, then are removed unless they are the current version or one of the 10 before it.
  • /versions lists the current version, recent previous ones and one snapshot per month.
  • A version that failed validation is never served. Asking for it returns unknown-version.

Caching and ETags

  • Every data file has an ETag: the SHA-256 of its content. Send it back in If-None-Match and an unchanged file returns 304 Not Modified with no body.
  • Current-version data URLs are cached for 5 minutes for datasets that update more than every 2 hours, 30 minutes for those updating up to every 2 days, and an hour otherwise, with stale-while-revalidate=86400, stale-if-error=604800.
  • Pinned URLs send public, max-age=31536000, immutable.
  • The catalog and dataset metadata are cached for 5 minutes, and the status for 1 minute.
  • X-Data-Version names the version that answered, Last-Modified is when it was published, and Link: rel="describedby" points to the metadata.
  • Range requests (Range: bytes=…) return 206 Partial Content.
curl -si "https://api.wildfiredata.dev/v1/active-fires/download/active-fires.csv" | grep -i etag
# etag: "3f9a…"   (the SHA-256 of the file)

curl -si -H 'If-None-Match: "3f9a…"' "https://api.wildfiredata.dev/v1/active-fires/download/active-fires.csv"
# HTTP/2 304   (nothing downloaded; your copy is current)

Errors

Errors are RFC 9457 problem documents with Content-Type: application/problem+json. The type URL points to an explanation, and most add a hint with the request to make instead.

{
  "type": "https://wildfiredata.dev/docs/errors#unknown-file",
  "title": "Not found",
  "status": 404,
  "detail": "table/nowhere.json is not part of active-fires version 2026-10-23.3f9a1c2e4b.",
  "instance": "/v1/active-fires/table/nowhere.json",
  "request_id": "8d1f0c2b9e7a4f31-IAD",
  "hint": "GET /v1/active-fires/locations"
}

Every error type, with what causes it and how to fix it.

Rate limits and API keys

Without a key you can make 120 requests every 60 seconds from one IP address. Past that, the API answers 429 with a rate-limited problem and Retry-After: 60.

API keys are coming, with higher limits. Until then everyone is anonymous. For large pulls, download the bulk CSV or Parquet file once instead of requesting thousands of small files, and use ETags so unchanged files cost nothing.

CORS

Any website can call the API from the browser. Responses allow every origin for GET, HEAD, OPTIONS, accept the Authorization, X-API-Key, If-None-Match, Range request headers, and expose ETag, Last-Modified, X-Data-Version, Link, Content-Range, Accept-Ranges, Retry-After.

Licenses and credit

The data keeps its source's license. Each dataset page, and the sources page, gives the license, whether commercial use is allowed and the exact credit line to use. Every JSON file repeats the license and attribution in its meta.