Getting started with 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 web service that builds 3D wildfire fuel data on demand. You send it a polygon as JSON. It fetches the national datasets, runs the fuel science on its own servers, and hands back voxelized trees, surface fuels, and terrain — packaged for QUIC-Fire, FIRETEC, or FDS.
It’s ordinary REST: HTTPS and JSON, an api-key header, one URL per resource.
Any language with an HTTP client can call it — Python, R, JavaScript, a shell —
and there’s nothing FastFuels-specific to install, no national dataset to
download, and no geospatial toolchain to set up. It’s also the same service
behind the
web application, so a domain you
create here opens in the browser, and one you drew in the browser is readable
from your script.
By the end of this page you’ll have made a real call and be holding a domain of your own — the thing every other request hangs off.
Before you start
Section titled “Before you start”You need an API key. Mint one in the web application under your account
settings; the web-app guide API Keys covers
the details. Give it read-write scope — a read-scoped key can GET, but it
can’t create anything, including the domain below.
Set your key once here and every code block on this page picks it up:
my-api-keyYou’ll also want curl, or Python with requests. That’s the whole list.
Your first call: create a domain
Section titled “Your first call: create a domain”A domain is the patch of ground you’re working over. You give it a polygon; the server works out the right projected coordinate system and the extent, and hands you back a resource with an ID. It’s the parent of everything else — trees, terrain, and fuel grids all live inside a domain — so it’s the first thing you make.
POST a GeoJSON FeatureCollection to /domains. The polygon below is about a
square kilometer at the Blue Mountain Recreation Area, near Missoula, Montana.
Run it as-is; you can bring your own coordinates once it works.
curl -X 'POST' \ 'https://api-v2-prod-nyvjyh5ywa-uw.a.run.app/domains' \ -H 'accept: application/json' \ -H 'api-key: my-api-key' \ -H 'Content-Type: application/json' \ -d '{ "type": "FeatureCollection", "features": [ { "type": "Feature", "properties": {}, "geometry": { "type": "Polygon", "coordinates": [ [ [-114.09545796676623, 46.8324794598619], [-114.11217537297199, 46.8324794598619], [-114.11217537297199, 46.82496749915157], [-114.09545796676623, 46.82496749915157], [-114.09545796676623, 46.8324794598619] ] ] } } ], "name": "Blue Mountain Recreation Area", "description": "Approximately 1 square kilometer near Missoula, Montana.", "pad_to_resolution": 2}'import requests
url = "https://api-v2-prod-nyvjyh5ywa-uw.a.run.app/domains"
headers = { "accept": "application/json", "api-key": "my-api-key", "Content-Type": "application/json",}
data = { "type": "FeatureCollection", "features": [ { "type": "Feature", "properties": {}, "geometry": { "type": "Polygon", "coordinates": [ [ [-114.09545796676623, 46.8324794598619], [-114.11217537297199, 46.8324794598619], [-114.11217537297199, 46.82496749915157], [-114.09545796676623, 46.82496749915157], [-114.09545796676623, 46.8324794598619], ] ], }, } ], "name": "Blue Mountain Recreation Area", "description": "Approximately 1 square kilometer near Missoula, Montana.", "pad_to_resolution": 2,}
response = requests.post(url, headers=headers, json=data)response.raise_for_status()created = response.json()
domain_id = created["id"]print(domain_id){ "bbox": [720226.0, 5189762.0, 721534.0, 5190646.0], "type": "FeatureCollection", "features": [ { "type": "Feature", "geometry": { "type": "Polygon", "coordinates": [ [ [720226.0, 5189762.0], [721534.0, 5189762.0], [721534.0, 5190646.0], [720226.0, 5190646.0], [720226.0, 5189762.0] ] ] }, "properties": { "name": "domain" } }, { "type": "Feature", "geometry": { "type": "Polygon", "coordinates": [ [ [721502.7544906491, 5190645.048516054], [720227.9398802927, 5190598.00908098], [720258.6480286171, 5189763.323999467], [721533.6406826023, 5189810.364218195], [721502.7544906491, 5190645.048516054] ] ] }, "properties": { "name": "input" }, "id": "0" } ], "name": "Blue Mountain Recreation Area", "description": "Approximately 1 square kilometer near Missoula, Montana.", "crs": { "type": "name", "properties": { "name": "EPSG:32611" } }, "tags": [], "pad_to_resolution": 2.0, "id": "your-domain-id", "created_on": "2026-07-14T20:24:14.090251", "modified_on": "2026-07-14T20:24:14.090251"}A 201 means it worked, and the body is the finished domain — creating one is
immediate, so there’s nothing to wait for. Two fields in it are worth a look:
crs— you sent longitude/latitude, and the server reprojected it into the appropriate UTM zone (EPSG:32611for western Montana). Everything you build in this domain lands in that coordinate system.id— server-assigned, and the handle for every later request.
Record the id now; the rest of the docs assume you have it:
your-domain-idHow the rest of the API fits together
Section titled “How the rest of the API fits together”Your domain is a container, and the things you build inside it live at domain-scoped URLs:
/domains/{domain_id} ├─ /features roads, water, uploaded layersets ├─ /inventories trees — from TreeMap, canopy height, or your own list ├─ /pointclouds uploaded lidar / photogrammetry └─ /grids the rasterized & voxelized fuelbed
/exports packaged fire-model inputsEvery resource behaves the same way: POST to create and get a 201 with the
full resource, GET .../{id} to read it back, PATCH to change a field,
DELETE to remove it.
One difference matters more than any other, and it’s the thing that surprises
people first. Creating a domain was synchronous — the work happened inside
the request. Most of what comes next isn’t. Fetching LANDFIRE, expanding TreeMap
plots into individual trees, voxelizing a stand: those are asynchronous. The
POST returns right away with "status": "pending", a background worker picks
the job up, and you re-GET the resource until its status settles on
"completed" or "failed". There’s no separate job object to track — the
resource carries its own status, progress, and error.
Where to go next
Section titled “Where to go next”-
Build something end to end. The QUIC-Fire tutorial runs the full pipeline — fuels, terrain, moisture, export. It opens by creating this same Blue Mountain domain, so paste in your your-domain-id and pick it up at Step 2.
-
Solve one specific problem. The How-To Guides are recipes, one task each: pull trees from TreeMap, mask fuels with roads, upload your own GeoTIFF, resample a grid.
-
Understand why it works this way. The Explanation section starts with Introduction to the FastFuels API — why FastFuels is a hosted service, and how the system is put together.
For the exact fields on every endpoint, the service publishes its own reference
at /docs. It’s generated by
the running API, so it never drifts out of date.
And if you work in Python, there’s an optional
FastFuels SDK
(pip install fastfuels-sdk) that wraps these same calls: it holds your key,
polls async jobs to completion, and pages through data for you. It adds no
capability over raw HTTP — just less boilerplate. The examples throughout these
docs stay on curl and requests so they translate to any language.