Introduction to the FastFuels API
You are viewing in-progress documentation for v2 (Beta). Switch to the stable version for the current production release.
The FastFuels API is a RESTful web service that turns a polygon you draw on a map into the high-resolution, three-dimensional fuel inputs that physics-based wildfire models — QUIC-Fire, FIRETEC, FDS — need to run. You send it JSON over HTTPS describing where you are working and what fuel data you want; it fetches the national datasets, runs the fuel science, and hands back voxelized canopy, surface fuels, topography, and the rest, packaged for a fire model. It is the managed, multi-user, cloud-hosted form of the open-source FastFuels fuel-modeling stack, reachable from any HTTP client in any language.
This page explains the API itself: what it is, why FastFuels is offered as a
hosted service in addition to the open-source library, and how it works as a
system. It is the conceptual frame the rest of the Explanation section builds
on; the resource pages (Domains,
Features,
Inventories) each cover one part of the
model introduced here. For procedures, see the
QUIC-Fire tutorial, which
runs the full pipeline, and the how-to guides, which cover individual tasks. For
the field-by-field reference, see the live OpenAPI documentation the service
publishes at /docs.
Why the API exists
Section titled “Why the API exists”The fuel science is an open-source Python library,
fastfuels-core: the crown
models, the allometry, the rasterization and voxelization routines all live
there, and in principle you could pip install it and run it on your laptop.
The API exists because turning that science into a usable landscape fuelbed
requires four things that a local script handles poorly:
-
Managed access to national data. A fuelbed is assembled from large, awkward source datasets — LANDFIRE surface fuels, 3DEP elevation, TreeMap forest-plot imputations, NAIP-derived canopy height, OpenStreetMap roads and water. These are national in extent, distributed in formats and projections that differ from one another, and far too large to download whole. The API holds them server-side and does the fetch-clip-reproject work for you: you name a source, and it returns the slice of that dataset aligned to your area, in your domain’s coordinate system. You never provision the data or the geospatial toolchain to read it.
-
Compute delegation. Expanding a TreeMap plot into individual trees, detecting crowns from a height surface, voxelizing a stand into a 3D canopy, rasterizing surface fuels — these are heavy, and they spike. The API offloads them to specialized cloud workers that scale up under load and idle when there’s none, so a large job doesn’t tie up your machine and you don’t install GDAL, rasterio, and a fuel-science stack to run it.
-
A standard schema and reproducibility. Before FastFuels v2, building 3D fuel inputs meant a bespoke pipeline per research group: no shared schema, inconsistent methods, and results that were hard to reproduce. The API is the authoritative interface to a single national 3D-fuel schema. Every resource records its full provenance — which source, which version, which parameters — so a result can be examined, traced, and recreated exactly, as scientific work requires.
-
Shared, multi-user, programmatic access. One backend sits behind both the web application and the API, so a domain built in the browser is the same resource your script reads back. API keys make access programmatic and attributable, and because the contract is plain HTTP and JSON, you reach it from Python, R, JavaScript, a shell — or the browser — without a FastFuels-specific runtime.
What the API is, concretely
Section titled “What the API is, concretely”Under the hood the API is a FastAPI service
speaking JSON over HTTPS. It publishes interactive, machine-readable
documentation it generates from its own code: a Swagger UI at /docs, a ReDoc
view at /redoc, and the raw schema at /openapi.json. This generated
documentation is the canonical field-by-field reference for every endpoint and
schema.
The organizing idea is resource orientation. Every entity the API manages — a domain, a feature, an inventory, a grid, an export — is a resource with a stable, server-generated ID and the same five standard operations:
| Operation | HTTP verb | Success | Returns |
|---|---|---|---|
| Create | POST | 201 | the full new resource |
| Get | GET .../{id} | 200 | the full resource |
| List | GET ... | 200 | a paginated, sortable collection |
| Update | PATCH .../{id} | 200 | the updated resource |
| Delete | DELETE .../{id} | 204 | empty |
Updates use PATCH: you send only the fields you want to change. The
conventions are uniform across every resource — snake_case field names,
created_on / modified_on timestamps, the same pagination and sorting on
every list — so once you have learned one resource, the others behave the same
way. Creating a domain, for instance, is a single POST carrying a GeoJSON
FeatureCollection and an api-key header:
POST /domainsapi-key: <your-api-key>Content-Type: application/json
{ "type": "FeatureCollection", "features": [ … ], "name": "Blue Mountain" }The 201 response contains the created domain: its server-assigned id, the
projected CRS the server computed, and the working extent. Every operation
follows this pattern — you send JSON and receive the full resource, including an
id to use in later requests.
The resource hierarchy
Section titled “The resource hierarchy”Resources form a hierarchy. The domain is the parent container for everything built over an area of ground, and its children live under domain-scoped URLs:
/domains/{domain_id} ├─ /features roads, water, uploaded layersets ├─ /inventories trees (TreeMap, CHM, uploaded) ├─ /pointclouds uploaded lidar / photogrammetry └─ /grids rasterized & voxelized fuelbed
/exports packaged fire-model inputs (top-level; outlive their domain)The URL nesting reflects ownership: a grid for a domain is addressed at
/domains/{domain_id}/grids/{grid_id}, and every child document also stores its
own domain_id, so its parent is identifiable without parsing the URL. Exports
are an exception — they are created under a domain, but their lifecycle is
top-level, so an export survives the deletion of the domain it was built in.
About domains covers the resource hierarchy
in full, and the other resource pages build on it.
Authentication: API keys
Section titled “Authentication: API keys”Every request carries an api-key header. You mint keys in the
web application — personal
keys attributed to your account, or application keys attributed to an
application for shared, multi-user work — each with a read or read-write
scope and an expiration you set. A read-scoped key can GET but not modify; a
write-scoped key can create, update, and delete. The mechanics of creating and
managing keys are covered in the web-app guide
API Keys; here it is enough to know that the
key in the header is what identifies you and bounds what you may do.
Synchronous and asynchronous operations
Section titled “Synchronous and asynchronous operations”Operations come in two kinds, because the work behind them differs greatly in cost.
Synchronous calls do their work within the request and return the finished
resource. Creating a domain is synchronous: the server validates and reprojects
your polygon and hands back the completed domain in the same 201. Uploading a
layerset is synchronous too — it is validated and stored in the one call.
Asynchronous calls start a long-running job and return immediately, before
the work is done. Anything that fetches a national dataset, expands a tree list,
detects crowns, voxelizes a stand, or assembles an export is asynchronous: the
POST returns a resource with status: "pending", a background worker picks it
up, and you poll the resource until it finishes. A create response for an
async grid looks like this — note the empty georeference, which the worker
fills in only on completion:
{ "id": "{grid_id}", "domain_id": "{domain_id}", "status": "pending", // job has not started "progress": null, "georeference": null, // populated when status becomes "completed" "source": { "name": "landfire", "product": "fbfm40", "version": "2024" }}Every async resource shares the same lifecycle and the same status fields:
pending ──▶ running ──▶ completed └───▶ failedstatusmovespending → running → completed(orfailed).progresscarries apercentand a human-readablemessage("Fetching LANDFIRE fbfm40 v2024…") while it runs.erroris populated on failure with acode, amessage, and often asuggestionfor how to fix it.
You watch a job by re-GET-ting the resource until its status settles:
GET /domains/{domain_id}/grids/{grid_id} → status: "running", progress: 25%GET /domains/{domain_id}/grids/{grid_id} → status: "completed", georeference: { … }There is no separate job or operation object to track. The resource itself
carries the status, progress, and error fields, so you poll the resource
you created to follow its progress.
Reading data back: metadata, partitions, and signed URLs
Section titled “Reading data back: metadata, partitions, and signed URLs”A resource’s metadata — its status, CRS, bounding box, provenance, the names
of its bands or columns — is small, and a plain GET returns it. A resource’s
data — millions of trees, a multi-band raster of cells — can be enormous, so
it is never inlined into that response. It is served separately, in pieces:
- A
data/metadataendpoint returns the shape of the dataset — total row or feature count, how it is partitioned — by reading only a file footer, so it is cheap. It is how you find out, for example, whether a fetched road feature actually contains any geometry before you rely on it. - A
data/{partition}endpoint (or, for a grid,data/{band}/{chunk}) returns one partition at a time. You stream a whole dataset by reading the partition count from the metadata and looping over the chunks.
Large files move through signed URLs rather than the API itself. To bring
your own data in — a GeoTIFF, a NetCDF, a tree list — the API hands you a
time-limited URL you PUT the file to directly, and a worker processes it once
it lands. To take a finished export out, the export resource carries a
signed_url you download from. The API brokers the transfer; the bytes flow
straight to and from cloud storage, so neither huge uploads nor huge downloads
pass through the request path.
Calling the API: raw HTTP and the Python SDK
Section titled “Calling the API: raw HTTP and the Python SDK”Because the API is plain HTTP and JSON, you can use any HTTP client. The how-to
guides give every example in two forms — a curl command and a Python snippet
using the requests library — neither of which requires anything
FastFuels-specific:
import requests
resp = requests.post( "https://<api-host>/domains", headers={"api-key": "<your-api-key>"}, json={"type": "FeatureCollection", "features": [...], "name": "Blue Mountain"},)domain_id = resp.json()["id"] # carry this into the next callFor Python users there is also a dedicated client, the
FastFuels Python SDK
(pip install fastfuels-sdk). It is a convenience layer over the same HTTP API:
it mirrors the resource model in Python objects, holds your key, and absorbs the
boilerplate of polling async jobs to completion and paging through partitioned
data. It provides no capability beyond the HTTP API; it reduces the boilerplate
of the common workflows.
Design principles
Section titled “Design principles”The API follows a small set of design principles drawn from standard resource-oriented API design. They explain patterns that recur across every resource:
- Self-documenting resources. A resource carries everything needed to
understand how it was made — its
source, itsmodifications, itsbands— so you can reconstruct how it was built from its own representation. - Explicit relationships. Resources reference each other by ID, you choose which grids combine and the role each plays, and constraints are validated by the server.
- Reproducibility. The stored schema keeps every parameter needed to
reproduce a result — source, version, transformations, seed. Re-running the
same inputs produces the same output, which is why a random step such as the
TreeMap expansion records a
seed. - Composable primitives. The API provides primitives you combine into your own workflow. A domain holds many grids and inventories, you assemble exports yourself, and operations such as resample and blend can be chained, so simple cases stay straightforward and complex workflows remain achievable.
- Unified schemas. Surface fuels, canopy, topography, and barriers all use
the same
Gridtype, distinguished by validated content; every grid operation therefore works on every grid.
Together these produce the API’s consistency: the same status fields on every
async resource, a source record on every resource, a single grid type, and
PATCH for metadata updates.
Open source
Section titled “Open source”The whole stack is open source, and the pieces map onto what this page describes:
- the API service itself —
FastFuels-API-v2— the FastAPI service and the cloud workers behind it; - the fuel science the workers run —
fastfuels-core— the core algorithms driving FastFuels; - the Python SDK —
fastfuels-sdk-python— the client described above.
Because the source code is open, you can inspect exactly how a result was produced, verify it, report issues, or run the stack yourself. The hosted service provides the managed data, compute, and shared access; the open-source code lets you audit and reproduce what it returns.
Where to go next
Section titled “Where to go next”- About domains — the root of the resource model, and the right next page: every other resource is created inside a domain and inherits its frame.
- About features and About inventories — the vector and the discrete-object branches of the model, with their own companions for modifications and CHM tree detection.
- Generate QUIC-Fire simulation inputs — the end-to-end pipeline, where everything above is put to work in order.
- The how-to guides — the concrete procedures (create a domain, fetch a grid,
build an inventory) in
curland Python. - The live OpenAPI documentation at the service’s
/docs— the canonical, field-by-field reference for every endpoint and schema.