Create a domain
You are viewing in-progress documentation for v2 (Beta). Switch to the stable version for the current production release.
A domain is the spatial container every other v2 resource hangs off. Grids,
inventories, and exports are all addressed by a domain-scoped URL
(/domains/{domain_id}/...), so the first call in any FastFuels workflow is
the one that mints a domain. This guide does exactly that: POST /domains
with a GeoJSON polygon, capture the id, verify with GET.
Prerequisites
Section titled “Prerequisites”-
An API key. Create one in the FastFuels web app under your account settings. Set it once here: my-api-key. It will propagate to every code block on the page.
-
A polygon you want to model, satisfying three hard constraints:
- Within CONUS. The continental US only.
- Strictly less than 16 km² in area. A polygon at exactly 16 km² is rejected.
- Wrapped in a
FeatureCollection. A bareFeatureis rejected (v1 accepted both; v2 does not).
Create the domain
Section titled “Create the domain”Submit a POST to /domains with your FeatureCollection. Omit crs and
the API treats your coordinates as WGS84 (EPSG:4326) and reprojects to UTM;
supply crs explicitly when your coordinates are already projected
(EPSG:5070, UTM, etc.) and the API preserves them. The example below uses
a ~1 km² polygon near Missoula, Montana in WGS84 — replace the
geometry, name, and description with your own.
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.11217537297199, 46.82496749915157], [-114.09545796676623, 46.82496749915157], [-114.09545796676623, 46.8324794598619], [-114.11217537297199, 46.8324794598619], [-114.11217537297199, 46.82496749915157] ] ] } } ], "name": "Blue Mountain Recreation Area", "description": "Approximately 1 square kilometer near Missoula, Montana."}'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.11217537297199, 46.82496749915157], [-114.09545796676623, 46.82496749915157], [-114.09545796676623, 46.8324794598619], [-114.11217537297199, 46.8324794598619], [-114.11217537297199, 46.82496749915157], ] ], }, } ], "name": "Blue Mountain Recreation Area", "description": "Approximately 1 square kilometer near Missoula, Montana.",}
response = requests.post(url, headers=headers, json=data)response.raise_for_status()created = response.json()
domain_id = created["id"]{ "bbox": [ 720227.9398802927, 5189763.323999467, 721533.6406826023, 5190645.048516054 ], "type": "FeatureCollection", "features": [ { "type": "Feature", "geometry": { "type": "Polygon", "coordinates": [ [ [720227.9398802927, 5189763.323999467], [721533.6406826023, 5189763.323999467], [721533.6406826023, 5190645.048516054], [720227.9398802927, 5190645.048516054], [720227.9398802927, 5189763.323999467] ] ] }, "properties": { "name": "domain" } }, { "type": "Feature", "geometry": { "type": "Polygon", "coordinates": [ [ [720258.6480286171, 5189763.323999467], [721533.6406826023, 5189810.364218195], [721502.7544906491, 5190645.048516054], [720227.9398802927, 5190598.00908098], [720258.6480286171, 5189763.323999467] ] ] }, "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": [], "id": "your-domain-id", "created_on": "2026-05-19T17:46:42.418469", "modified_on": "2026-05-19T17:46:42.418469"}Two fields on the response drive the rest of your workflow:
id— the domain identifier. Every subsequent request to grids, inventories, or exports for this domain uses it in the URL.crs— the projected CRS the API stored. Any GeoTIFF you later upload to this domain must match it exactly. Don’t assume the value you submitted is what came back — read it off the response.
Record the domain ID
Section titled “Record the domain ID”Copy the id from the response into the widget below. Every other v2 how-to
that touches this domain reads it from here.
Your domain ID: your-domain-id
Verify
Section titled “Verify”A quick GET confirms the domain persisted and is readable.
curl -X 'GET' \ 'https://api-v2-prod-nyvjyh5ywa-uw.a.run.app/domains/your-domain-id' \ -H 'accept: application/json' \ -H 'api-key: my-api-key'import requests
url = "https://api-v2-prod-nyvjyh5ywa-uw.a.run.app/domains/your-domain-id"
headers = { "accept": "application/json", "api-key": "my-api-key",}
response = requests.get(url, headers=headers)response.raise_for_status()domain = response.json()A 200 OK with a matching id confirms it persisted. 404 Not Found means
either the ID is wrong or it was created under a different API key — domains
are scoped to their owner.
Optional fields on create
Section titled “Optional fields on create”nameanddescriptiondefault to empty strings. Set them when you want the domain to be self-identifying in a list view.pad_to_resolution(meters) snaps the working extent outward to a multiple of this value. Set it when you plan to compose grids at multiple resolutions on the same domain; otherwise omit.stylecarries map render hints (stroke_color,fill_opacity, etc.) consumed by the webapp. Skip unless you’re driving a map UI.
Common pitfalls
Section titled “Common pitfalls”-
Bare
Featureinstead ofFeatureCollection— returns422with a pydantic validation array. The fingerprint to look for:{"type":"literal_error","loc":["body","type"],"msg":"Input should be 'FeatureCollection'"}Wrap the feature:
{"type":"FeatureCollection","features":[<your feature>], ...}. -
Polygon outside CONUS — returns
422with:{"detail":"Invalid spatial extent. The domain must be entirely within CONUS."}Even one vertex outside the continental US fails. Alaska, Hawaii, and territories are out of scope.
-
Polygon ≥ 16 km² — returns
422with:{"detail":"Invalid spatial extent. Area must be less than 16 square kilometers."}The check is strict: trim the polygon or split it across multiple domains. If you set
pad_to_resolution, leave headroom — padding runs before the area check. -
Missing or wrong
api-keyheader — returns401with{"detail":"Unauthorized"}. Confirm the header name isapi-key(notAuthorization) and the value matches an active key. -
Mismatched CRS on a follow-up grid upload — not an error on this call, but the most common downstream failure. The CRS the API returns on this domain is the one your GeoTIFFs must match, not the one you submitted. Read
crsoff the create response before reprojecting uploads.