§1Service health & version
/healthz
Returns dataset version and applied schema migration. Useful as a liveness check.
Request
curl 'https://api.rootfast.org/healthz'
Response · 200
{
"ok": true,
"dataset_version": "0.0.1",
"schema_version": "001"
} Try it → https://api.rootfast.org/healthz
§2One plant, in full
/v1/plants/{usda_symbol}
Returns a plant record with growth habit, duration, native status by region, every USDA characteristic, and all Montana counties where the taxon is recorded. Resolves synonyms transparently.
Request
curl 'https://api.rootfast.org/v1/plants/PIPO'
Response · 200
{
"plant": {
"usda_symbol": "PIPO",
"scientific_name": "Pinus ponderosa Lawson & C. Lawson",
"scientific_name_canonical": "Pinus ponderosa",
"common_name": "ponderosa pine",
"family": "Pinaceae Spreng. ex Rudolphi",
"gbif_taxon_key": 5285053,
"gbif_match_type": "EXACT",
"rank": "Species",
"...": "..."
},
"growth_habits": ["Tree"],
"durations": ["Perennial"],
"native_status": [
{ "region_code": "L48", "status": "Native" },
{ "region_code": "CAN", "status": "Native" }
],
"characteristics": { "drought_tolerance": "High", "...": "..." },
"counties": [
{ "county_fips": "30031", "county_name": "Gallatin", "state_code": "MT", "nativity": "Native" }
]
} §3Flat catalogue
/v1/plants?{filters}&format={json|csv|ndjson}
Every taxon in the catalogue as a flat list, one row per plant — no county aggregation. Accepts the filter parameters under §F (except `nativity`, which requires a distribution join). Use `format=csv` or `format=ndjson` to download in bulk; the response is sent as an attachment with a `rootfast-plants.csv` filename. JSON pagination defaults to 5000 (max 10000); bulk downloads return up to 50000 in a single call.
Request
curl 'https://api.rootfast.org/v1/plants?family=Pinaceae&limit=2'
Response · 200
{
"count": 2,
"total": 45,
"paging": { "limit": 2, "offset": 0, "has_more": true },
"filters": { "family": "Pinaceae" },
"plants": [
{
"usda_symbol": "ABAM",
"scientific_name": "Abies amabilis (Douglas ex Loudon) Douglas ex Forbes",
"common_name": "Pacific silver fir",
"family": "Pinaceae Spreng. ex Rudolphi",
"genus": "Abies",
"rank": "Species",
"gbif_taxon_key": 5285101,
"...": "..."
}
]
} Try it → https://api.rootfast.org/v1/plants?family=Pinaceae&limit=2
§4Plant finder
/v1/plants/search
Cross-cutting search that takes a geographic scope (counties or states) plus filter criteria and returns plants ranked by how many of the requested counties they appear in. Useful for "I live near these places — what fits my conditions?" queries that don't fit a single county or state. Accepts every filter param from §F inside a `filters` object. Default limit 200, max 1000.
Request
curl -X POST 'https://api.rootfast.org/v1/plants/search' \
-H 'content-type: application/json' \
-d '{
"counties": ["30031", "30057", "30077"],
"nativity": "Native",
"filters": {
"growth_habit": "Tree",
"drought_tolerance": "High"
},
"limit": 5,
"include_counties": true
}' Response · 200
{
"scope": { "counties_requested": 3, "states_requested": null },
"filters": { "growth_habit": "Tree", "drought_tolerance": "High" },
"nativity": "Native",
"count": 5,
"limit": 5,
"plants": [
{
"usda_symbol": "ARTR2",
"scientific_name": "Artemisia tridentata Nutt.",
"common_name": "big sagebrush",
"family": "Asteraceae Bercht. & J. Presl",
"matching_county_count": 3,
"requested_county_count": 3,
"match_score": 1.0,
"nativity_rollup": "Native",
"matching_counties": ["30031", "30057", "30077"]
}
]
} §5Plant synonyms
/v1/plants/{usda_symbol}/synonyms
List every synonym known for the plant, drawn from two sources: USDA's Synonyms tab (`usda_synonyms_tab` — names without their own plant record) and any sibling rows in the plants table whose `accepted_plant_id` points back to this taxon (`plants_table`). The endpoint accepts either an accepted symbol or a synonym symbol — synonyms transparently resolve to the canonical plant and the response reports whether resolution happened via `resolved_from_synonym`.
Request
curl 'https://api.rootfast.org/v1/plants/EQAR/synonyms'
Response · 200
{
"accepted": {
"usda_symbol": "EQAR",
"scientific_name": "Equisetum arvense L.",
"scientific_name_canonical": "Equisetum arvense",
"common_name": "field horsetail",
"family": "Equisetaceae Michx. ex DC.",
"gbif_taxon_key": 7924597
},
"queried_symbol": "EQAR",
"resolved_from_synonym": false,
"count": 5,
"synonyms": [
{ "usda_symbol": "EQARA", "scientific_name": "Equisetum arvense L. var. alpestre Wahlenb.", "source": "usda_synonyms_tab" },
{ "usda_symbol": "EQARB", "scientific_name": "Equisetum arvense L. var. boreale (Bong.) Rupr.", "source": "usda_synonyms_tab" },
{ "usda_symbol": "EQCA", "scientific_name": "Equisetum calderi B. Boivin", "source": "usda_synonyms_tab" }
]
} §6Reverse lookup by GBIF
/v1/plants/by-gbif/{taxon_key}
Fetch the plant detail document by its GBIF Backbone taxon_key instead of by USDA symbol. Useful for joining rootfast to any dataset that already speaks GBIF (iNaturalist, ITIS, eBird, BHL, etc.). Response shape is identical to `/v1/plants/{usda_symbol}`. When multiple USDA plants map to the same GBIF key (synonyms), the accepted entry wins.
Request
curl 'https://api.rootfast.org/v1/plants/by-gbif/5285053'
Response · 200
{
"plant": {
"usda_symbol": "PIPO",
"scientific_name": "Pinus ponderosa Lawson & C. Lawson",
"common_name": "ponderosa pine",
"gbif_taxon_key": 5285053,
"gbif_match_type": "EXACT",
"...": "..."
},
"photo": { "url": "...", "thumb_url": "...", "license": "cc-by-nc" },
"growth_habits": ["Tree"],
"durations": ["Perennial"],
"characteristics": { "...": "..." },
"counties": [ { "county_fips": "30031", "county_name": "Gallatin", "state_code": "MT", "nativity": "Native" } ]
} §7A random plant
/v1/plants/random
Returns one random photographed species from the catalogue. Good for "plant of the day" widgets and seed examples. Cached at the edge for 60 s so it rotates often but cheaply.
Request
curl 'https://api.rootfast.org/v1/plants/random'
Response · 200
{
"plant": {
"usda_symbol": "EPSA",
"scientific_name": "Epilobium saximontanum Hausskn.",
"common_name": "Rocky Mountain willowherb",
"family": "Onagraceae Juss.",
"photo_thumb_url": "https://inaturalist-open-data.s3.amazonaws.com/photos/.../square.jpg",
"photo_url": "https://inaturalist-open-data.s3.amazonaws.com/photos/.../medium.jpg"
}
} §8All counties, paginated
/v1/counties?state={code}&limit={n}&offset={n}
Flat list of every county in the catalogue, optionally filtered by state. Default limit is 500, max 5000. Each row carries the FIPS code, state code, centroid lat/lon for mapping.
Request
curl 'https://api.rootfast.org/v1/counties?state=RI'
Response · 200
{
"count": 5,
"total": 5,
"limit": 500,
"offset": 0,
"has_more": false,
"counties": [
{
"county_fips": "44001",
"county_name": "Bristol",
"state_code": "RI",
"centroid_lat": 41.7,
"centroid_lon": -71.28
}
]
} §9Plants in a county
/v1/counties/{county_fips}/plants?{filters}
All taxa recorded in the given county, with nativity. FIPS is the 5-digit Census code (Gallatin County, MT is 30031). Accepts the filter parameters under §F.
Request
curl 'https://api.rootfast.org/v1/counties/30031/plants'
Response · 200
{
"county": {
"county_fips": "30031",
"county_name": "Gallatin",
"state_code": "MT"
},
"count": 1362,
"plants": [
{
"usda_symbol": "ABLA",
"scientific_name": "Abies lasiocarpa (Hook.) Nutt.",
"common_name": "subalpine fir",
"family": "Pinaceae Spreng. ex Rudolphi",
"nativity": "Native"
}
]
} §10List of states
/v1/states
Every state with ingested data, plus county and taxon counts. Today this returns Montana only — nationwide ingest is on the roadmap (see /about).
Request
curl 'https://api.rootfast.org/v1/states'
Response · 200
{
"count": 1,
"states": [
{
"state_code": "MT",
"state_name": "Montana",
"state_fips": "30",
"country": "US",
"county_count": 56,
"taxa_count": 3215
}
]
} Try it → https://api.rootfast.org/v1/states
§11State header
/v1/states/{state_code}
Single-state header with county and taxon counts. State code is the two-letter postal abbreviation (case-insensitive).
Request
curl 'https://api.rootfast.org/v1/states/MT'
Response · 200
{
"state": {
"state_code": "MT",
"state_name": "Montana",
"state_fips": "30",
"country": "US",
"county_count": 56,
"taxa_count": 3215
}
} §12Plants in a state
/v1/states/{state_code}/plants?limit&offset&{filters}
Every taxon in the state, aggregated across counties. county_count is how many counties the taxon appears in; native_counties / introduced_counties / both_counties give the breakdown. nativity is the state-level rollup: "Native" if no introduced records, "Introduced" if no native records, "Both" if mixed or any county records "Both". Paginated (default limit 5000, max 10000) and accepts the filter parameters under §F.
Request
curl 'https://api.rootfast.org/v1/states/MT/plants'
Response · 200
{
"state": {
"state_code": "MT",
"state_name": "Montana",
"state_fips": "30",
"country": "US"
},
"counts": { "taxa": 3215, "counties": 56 },
"paging": { "limit": 5000, "offset": 0, "count": 3215, "has_more": false },
"plants": [
{
"usda_symbol": "PIPO",
"scientific_name": "Pinus ponderosa Lawson & C. Lawson",
"common_name": "ponderosa pine",
"family": "Pinaceae Spreng. ex Rudolphi",
"photo_thumb_url": null,
"county_count": 22,
"native_counties": 22,
"introduced_counties": 0,
"both_counties": 0,
"nativity": "Native"
}
]
} §13Counties in a state
/v1/states/{state_code}/counties
Lightweight listing of every county in the state with its taxa count. Useful for state-overview pages and county pickers.
Request
curl 'https://api.rootfast.org/v1/states/MT/counties'
Response · 200
{
"state": { "state_code": "MT", "state_name": "Montana", "state_fips": "30", "country": "US" },
"count": 56,
"counties": [
{
"county_fips": "30001",
"county_name": "Beaverhead",
"state_code": "MT",
"centroid_lat": 45.07,
"centroid_lon": -112.86,
"taxa_count": 755
}
]
} §14Full-text taxon search
/v1/search?q={query}
Searches across scientific name, common name, family, and genus. Returns up to 25 hits, ranked by BM25.
Request
curl 'https://api.rootfast.org/v1/search?q=ponderosa'
Response · 200
{
"query": "ponderosa",
"results": [
{ "usda_symbol": "PIPO", "scientific_name": "Pinus ponderosa Lawson & C. Lawson", "common_name": "ponderosa pine", "family": "Pinaceae Spreng. ex Rudolphi" },
{ "usda_symbol": "PIPOP", "scientific_name": "Pinus ponderosa Lawson & C. Lawson var. ponderosa C. Lawson", "common_name": "ponderosa pine", "family": "Pinaceae Spreng. ex Rudolphi" }
]
} §BBulk download
Take the data.
The list endpoints accept ?format=csv or
?format=ndjson for archival / spreadsheet / pipeline
use. The response is sent as an attachment with a sensible
filename; Content-Type is set to
text/csv or application/x-ndjson as
appropriate. Bulk downloads default to no pagination cap (50 000
rows max per call); apply filters under §F to scope the result.
Endpoints that support bulk
- ·
/v1/plants— flat catalogue dump - ·
/v1/counties— every US county (also acceptsformat=geojson— see below) - ·
/v1/states/{code}/plants— one state's taxa - ·
/v1/counties/{fips}/plants— one county's taxa
GeoJSON output
/v1/counties?format=geojson returns a standard
GeoJSON FeatureCollection. Each feature is a Point at
the county centroid (we don't store full polygons yet); properties
carry the FIPS code, county name, and state. Drop the URL straight
into Leaflet, Mapbox GL, deck.gl, QGIS, or Mapshaper. Content-Type
is application/geo+json.
# Every Rhode Island county as GeoJSON
curl 'https://api.rootfast.org/v1/counties?state=RI&format=geojson'
# In Leaflet:
fetch('https://api.rootfast.org/v1/counties?state=RI&format=geojson')
.then(r => r.json())
.then(fc => L.geoJSON(fc).addTo(map)); Examples
# Whole catalogue as CSV curl -O 'https://api.rootfast.org/v1/plants?format=csv' # All MT plants as NDJSON (one JSON object per line) curl 'https://api.rootfast.org/v1/states/MT/plants?format=ndjson' \ > mt-plants.ndjson # Drought-tolerant native trees in Gallatin County as CSV curl 'https://api.rootfast.org/v1/counties/30031/plants?\ growth_habit=Tree&nativity=Native&drought_tolerance=High&format=csv' # Every US county as CSV (FIPS, name, state, centroid) curl 'https://api.rootfast.org/v1/counties?format=csv&limit=5000'
All data is CC0 / public domain — use it freely. A citation back to
rootfast.org is appreciated but not required.
§FFilter parameters
Narrow the list.
The list endpoints /v1/states/{code}/plants and
/v1/counties/{fips}/plants accept query parameters that filter
the result set. Multiple parameters AND together. Enum parameters
accept comma-separated lists (OR within the parameter). Invalid
values return 400 bad_request.
Plant-level
family- Prefix match against the USDA family string. Example:
?family=Pinaceae. genus- Exact match. Example:
?genus=Pinus. rank- One of
Species,Subspecies,Variety,Form. growth_habit- One of
Tree,Shrub,Subshrub,Vine,Forb/herb,Forb,Graminoid,Nonvascular,Lichenous. duration- One of
Annual,Biennial,Perennial. nativity- One of
Native,Introduced,Both. Filters at the county-record level.
Characteristics — enums
drought_toleranceHigh,Medium,Low,Noneshade_toleranceIntolerant,Intermediate,Tolerantmoisture_useHigh,Medium,Low,Nonegrowth_rateSlow,Moderate,RapidtoxicityNone,Slight,Moderate,SeverelifespanShort,Moderate,Longcommercial_availabilityNo Known Source,Routinely Available,Field Collections Only,Contracting Onlyfire_toleranceHigh,Medium,Low,Nonesalinity_toleranceHigh,Medium,Low,Nonenitrogen_fixationHigh,Medium,Low,None
Characteristics — booleans
fire_resistant- Accepts
1/0,true/false, oryes/no. palatable_human- Same.
nursery_stock_product- Same.
berry_nut_seed_product- Same.
lumber_product- Same.
Characteristics — colors (exact, case-insensitive)
flower_color- e.g.
Yellow foliage_color- e.g.
Green fruit_color- e.g.
Red
Characteristics — numeric ranges
min_height_ft/max_height_ft- Mature height in feet.
min_ph/max_ph- Soil pH tolerance.
min_precipitation_in/max_precipitation_in- Inches of annual precipitation.
min_frost_free_days- Minimum required frost-free days.
min_temperature_f/max_temperature_f- Minimum tolerable temperature in °F.
min_root_depth_in- Minimum root depth in inches.
Examples
# Drought-tolerant native trees in Gallatin County, MT GET /v1/counties/30031/plants?growth_habit=Tree&nativity=Native&drought_tolerance=High # Mature shrubs 3–10 ft, full-sun-tolerant, found in Rhode Island GET /v1/states/RI/plants?growth_habit=Shrub&shade_tolerance=Intolerant&min_height_ft=3&max_height_ft=10 # Pinaceae statewide in Montana GET /v1/states/MT/plants?family=Pinaceae # Trees or shrubs with high or medium drought tolerance GET /v1/states/AL/plants?growth_habit=Tree,Shrub&drought_tolerance=High,Medium
§EErrors
Errors are structured.
Every error returns a JSON body of
{ "error": { "code": "<machine-readable>", "message": "<human-readable>" } }.
The HTTP status reflects the failure category and the code string
is stable so clients can branch on it.
- 400
bad_request— Malformed query (e.g. invalidlimit).- 404
not_found— Unknown plant symbol, county FIPS, state code, or route.- 405
method_not_allowed— OnlyGETandHEADare supported.- 500
internal_error— Server-side fault. Rare; please include the URL when reporting.
§CCaching
Cache aggressively.
The dataset only changes when we re-import a SQLite chunk (rarely; never silently).
Every response includes a Cache-Control header tuned to that reality.
s-maxage applies at Cloudflare's edge; max-age is the
browser-side hint.
- /healthz
no-store— always fresh.- /v1/plants/{symbol}
max-age=3600, s-maxage=86400- /v1/plants/random
max-age=0, s-maxage=60— rotates ~every minute.- /v1/counties[/...]
max-age=600–3600, s-maxage=86400- /v1/states[/...]
max-age=600, s-maxage=86400- /v1/search
max-age=60, s-maxage=300
ETag + 304 Not Modified
Every cacheable response carries a weak ETag header derived
from the body content. Send it back as If-None-Match on a
follow-up request and the API will respond with
304 Not Modified and no body if nothing changed. Browsers
handle this automatically; library clients should opt in by storing
the ETag and including it on subsequent fetches.
# Conditional GET — second request is empty + 304 if unchanged
ETAG=$(curl -sI 'https://api.rootfast.org/v1/plants/PIPO' \
| awk '/^etag:/ {print $2}' | tr -d '\r')
curl -sI -H "If-None-Match: $ETAG" \
'https://api.rootfast.org/v1/plants/PIPO'
# HTTP/2 304
# etag: W/"abcdef1234567890"
# cache-control: public, max-age=3600, s-maxage=86400 §PPlanned
On the roadmap.
Items below are intentionally not implemented yet. They will land as the API matures and as real usage tells us they're needed. If one of these is blocking you, send a note via contact.
OpenAPI 3.0 spec
A machine-readable description of every endpoint, parameter,
response shape, and error code — served at
/v1/openapi.json. Lets you generate typed clients
in any language with openapi-generator, drop the
spec into Postman / Insomnia / Bruno for "try it" UIs, validate
responses in CI, or feed it to LLM tool-calling frameworks so
AI agents can use the API directly. Will land alongside an
optional /v1/docs route serving an interactive
Swagger UI / Scalar page.
Deferred until the endpoint surface stabilizes — moving endpoints around now would just churn the spec.
API keys & rate limits
The API is open and uncapped today. If a real abuse problem appears, we'll add Cloudflare-level per-IP rate limits with an optional key-issuance flow for higher quotas. Keys, if they land, will be free for non-commercial use; commercial use is already allowed under CC0 with no key needed.
No timeline — added only if abuse forces it.
§LLicense & attribution
Use it freely. Cite the sources.
The Rootfast dataset is derived from public-domain federal data. The original source records are in the public domain. The merged dataset and this API are published as a public good. No API key is required and there is no commercial gating planned.
- · USDA PLANTS — public domain
- · GBIF Backbone Taxonomy — CC0
- · Rootfast code — MIT
- · Rootfast data — CC0
If you build something with this — a tool, a garden plan, a research paper — we would love to hear about it.