# API Usage — Petrel Fuel Layer v1.0 Global FM40 Crosswalk The Petrel Fuel Layer v1.0 is delivered through the Petrel API. Every download is authenticated; downloads are rate-limited per account tier. The **data itself is CC-BY-4.0** (reuse, including commercial, is permitted with attribution) — the API tiers below govern download *volume*, not reuse rights. ## Authentication You need a Petrel account and an API key: 1. Sign up at https://petreldata.io/signup (free tier available) 2. Generate an API key under your account settings 3. Export it as `PETREL_API_KEY` in your shell environment ```bash # Petrel keys look like tlk.. — copy yours from # https://petreldata.io/account#api-keys export PETREL_API_KEY=tlk.1a2b3c4d-5e6f-7890-abcd-ef1234567890.xxxxxxxx ``` ## Endpoints (v1 stable) | Endpoint | Method | Description | |---|---|---| | `/v1/layer/fuel` | GET | List available fuel vintages | | `/v1/layer/fuel/v1.0_global_crosswalk` | GET | Layer manifest (STAC item + docs) | | `/v1/layer/fuel/v1.0/bundle` | GET | Request signed URLs for the full layer download (rate-limited) | ## Quotas Your plan's monthly allowance is shared across the API; the authoritative figures live at https://petreldata.io/pricing. | Plan | Included lookups / month | Beyond the quota | |---|---|---| | Free | 1,000 | requests return HTTP 429 `quota_exceeded`; nothing is charged | | Indie | 10,000 | overage billed per lookup if you enable it | | Growth | 50,000 | overage billed per lookup if you enable it | | Scale | 250,000 | overage billed per lookup if you enable it | Overage is off until you turn it on (Account -> Billing). A short burst limit also applies per key, so batch your requests rather than firing them in parallel bursts. ## Code snippets ### curl — full bundle download ```bash # 1. Get the signed-URL manifest (URLs valid for 1 hour) curl -s -H "X-API-Key: $PETREL_API_KEY" \ https://api.petreldata.io/v1/layer/fuel/v1.0/bundle > bundle.json # 2. Download the class raster jq -r '.files[] | select(.name=="fuel_model_class_270m.tif") | .url' bundle.json \ | xargs -I{} curl -s "{}" -o fuel_model_class_270m.tif ``` ### Python — point query (read the FM40 class) The value is a categorical FM40 code — read it directly, do not scale it. Map it to a family with the ranges below: ```python import os, requests, rasterio FAM = {range(91,100): "NB non-burnable", range(101,110): "GR grass", range(121,125): "GS grass-shrub", range(141,150): "SH shrub", range(161,166): "TU timber-understory", range(181,190): "TL timber-litter"} def family(c): return next((v for k, v in FAM.items() if c in k), "unknown") api_key = os.environ["PETREL_API_KEY"] r = requests.get( "https://api.petreldata.io/v1/layer/fuel/v1.0/bundle", headers={"X-API-Key": api_key}, ).json() urls = {f["name"]: f["url"] for f in r["files"]} # 1-hour presigned URLs lat, lon = 40.42, -3.70 # Madrid (Mediterranean shrub → SH) with rasterio.open(urls["fuel_model_class_270m.tif"]) as src: code = int(next(src.sample([(lon, lat)]))[0]) if code == src.nodata: # 255 print("no data (ocean / outside coverage)") else: print(f"FM40 class {code} — {family(code)}") ``` ### Python — windowed read over HTTPS You don't need to download the full layer if you only want a region. `rasterio` can stream-open a COG directly. **Categorical data — use nearest-neighbour if you resample:** ```python import os, requests import rasterio from rasterio.windows import from_bounds api_key = os.environ["PETREL_API_KEY"] r = requests.get( "https://api.petreldata.io/v1/layer/fuel/v1.0/bundle", headers={"X-API-Key": api_key}, ).json() urls = {f["name"]: f["url"] for f in r["files"]} # 1-hour presigned URLs # FM40 class over the Iberian Peninsula with rasterio.open(urls["fuel_model_class_270m.tif"]) as src: win = from_bounds(-10, 36, 4, 44, transform=src.transform) fm40 = src.read(1, window=win) # uint8 FM40 codes; 255 = NoData ``` ### R — windowed read (using `terra`) ```r library(terra) api_key <- Sys.getenv("PETREL_API_KEY") manifest <- httr::GET( "https://api.petreldata.io/v1/layer/fuel/v1.0/bundle", httr::add_headers(`X-API-Key` = api_key) ) files <- httr::content(manifest)$files url <- Filter(function(f) f$name == "fuel_model_class_270m.tif", files)[[1]]$url r <- rast(url) # categorical FM40 codes iberia <- crop(r, ext(-10, 4, 36, 44)) plot(iberia) ``` ### QGIS / ArcGIS For desktop GIS use: 1. Download via the bundle endpoint (Petrel API key required) 2. Open the `.tif` directly in QGIS/ArcGIS — internal tiling and overviews are honored. Style it as a **categorical / unique-values** layer (it is class data, not a continuous ramp). 3. The STAC item (`/v1/layer/fuel/v1.0_global_crosswalk` → STAC JSON) can be used with STAC plugins for automated discovery. ## Layer encoding reference The layer is **categorical uint8** — the value **is** the FM40 class code, with **no scale factor** (unlike the continuous Petrel layers). NoData sentinel: `255` (ocean, Antarctica, unscored cells). | File | Dtype | Encoding | Values | NoData | |---|---|---|---|---| | `fuel_model_class_270m.tif` | uint8 | categorical (no scale) | FM40 class code | 255 | FM40 family ranges: | Code range | Family | |---|---| | 91–99 | NB — non-burnable | | 101–109 | GR — grass | | 121–124 | GS — grass-shrub | | 141–149 | SH — shrub | | 161–165 | TU — timber-understory | | 181–189 | TL — timber-litter | **Interpretation reminder:** this is a **class code** — never average, scale, or bilinearly resample it; reproject with nearest-neighbour only. Key downstream logic on the **family** (the validated unit); the specific in-family code is a representative assignment. Native resolution is ~270 m (9 arcsec). ## Errors and rate-limit behavior The API returns standard HTTP status codes: - `401 Unauthorized` — missing or invalid API key - `403 Forbidden` — license tier doesn't permit this endpoint - `429 Too Many Requests` — rate limit exceeded; response includes `Retry-After` header and `X-RateLimit-Reset` timestamp - `404 Not Found` — vintage or layer not recognized - `503 Service Unavailable` — temporary backend issue; safe to retry with exponential backoff ## Support - **Documentation:** https://petreldata.io/docs/layers/fuel - **API status:** https://status.petreldata.io - **Bugs / questions:** layers@petreldata.io - **Higher-volume / bulk API access:** https://petreldata.io/demo (the data is CC-BY-4.0; this is about download throughput, not reuse rights)