API reference
Base URL: https://soilpoint.dev · Free · US coverage only · No key required to start
Endpoint
GET /v1/soil
| Parameter | Type | Required | Description |
|---|---|---|---|
lat | number | yes | Latitude in WGS84 decimal degrees (−90 to 90) |
lon | number | yes | Longitude in WGS84 decimal degrees (−180 to 180) |
Keyless example:
curl "https://soilpoint.dev/v1/soil?lat=37.368402&lon=-121.771"
Use with AI agents
SoilPoint also runs a remote MCP server at https://soilpoint.dev/v1/mcp. It has one tool, get_soil(lat, lon), which returns the same JSON as /v1/soil. No key, nothing to install, same rate limits.
Claude Code:
claude mcp add --transport http soilpoint https://soilpoint.dev/v1/mcp
Codex:
codex mcp add soilpoint --url https://soilpoint.dev/v1/mcp
Cursor, VS Code and other clients that take a JSON config:
{"mcpServers": {"soilpoint": {"url": "https://soilpoint.dev/v1/mcp"}}}
In the Claude app, add it as a custom connector with the same URL.
Every page of these docs is also plain Markdown: add .md to the URL, as in /docs.md. /llms.txt lists them, and /openapi.json describes the REST endpoint.
API keys & rate limits
Keys are optional. Without one you share an IP-based bucket; with one you get a dedicated, higher-limit bucket. Keys don't expire. A revoked or mistyped key gets 401 INVALID_API_KEY.
How to get a key: on the homepage, look up any coordinate, then click "Get my key" in the panel that appears.
Send your key in the x-api-key request header:
curl -H "x-api-key: sp_live_..." "https://soilpoint.dev/v1/soil?lat=42.024&lon=-93.648"
| Tier | Per minute | Per day |
|---|---|---|
| Keyless (per IP) | 60 req | 1,000 req |
| Keyed (per key) | 300 req | 20,000 req |
Over the limit → 429 with {"error":{"code":"RATE_LIMITED",...}} and a Retry-After header with the seconds to wait.
Response fields
All fields are present in every 200 response. Fields marked nullable return null for Miscellaneous land areas (urban land, water bodies, pits) where soil properties are undefined by design.
| Field | Type | Nullable | Description |
|---|---|---|---|
lat | number | Echoed latitude from your request | |
lon | number | Echoed longitude from your request | |
mukey | string | SSURGO map unit key for this point | |
mapUnitName | string | Human-readable map unit name (e.g. "Diablo clay, 9 to 15 percent slopes") | |
soilSeries | string | yes | Soil series name of the dominant component |
componentKind | string | Component kind: "Series" for named soils, "Miscellaneous area" for urban land, water and the like | |
componentPct | number | Percentage of the map unit this dominant component occupies (0–100) | |
texture | string | yes | USDA textural class of the dominant surface horizon, as SSURGO spells it (e.g. "Clay", "Sandy loam") |
drainageClass | string | yes | USDA drainage class, as SSURGO spells it (e.g. "Well drained", "Somewhat poorly drained") |
hydrologicGroup | string | yes | NRCS hydrologic soil group: A, B, C, D, or dual (e.g. "B/D") |
awc | number | yes | Available water capacity (cm/cm fraction), depth-weighted over 0–30 cm |
ph | number | yes | pH (1:1 water method), depth-weighted over 0–30 cm |
organicMatter | number | yes | Organic matter (% by weight), depth-weighted over 0–30 cm |
nccpi | number | yes | National Commodity Crop Productivity Index (0–1). Often null for non-cropland. |
nccpiClass | string | yes | NCCPI class as SSURGO rates it (e.g. "High inherent productivity", "Not rated") |
depthWindowCm | array | Depth range used for weighted averages, as [min, max]. Default [0, 30]. | |
dataVintage | string | yes | SSURGO survey version date for this area (ISO 8601 date string) |
stale | boolean | Reserved for answers served from cache during a USDA outage. The current in-memory cache never sets it, so it is always false; use cachedAt for the answer's age. | |
cachedAt | string | yes | ISO 8601 timestamp of when SoilPoint fetched this answer from USDA |
dataSource | string | Data source: "USDA-NRCS SSURGO via Soil Data Access" | |
surveyDisclaimer | string | Usage disclaimer: as-is, US only, not for regulatory use |
Errors
All errors return a JSON envelope: {"error":{"code":"...","message":"..."}}
| Code | HTTP | Meaning |
|---|---|---|
INVALID | 400 | Missing or out-of-range lat or lon parameter |
NO_SOIL_DATA | 404 | Point is in the ocean, outside US coverage, or in a gap in the survey |
RATE_LIMITED | 429 | Rate limit exceeded. The Retry-After header says how long to wait |
INVALID_API_KEY | 401 | The x-api-key header doesn't match an active key |
SDA_UPSTREAM_ERROR | 502 | USDA Soil Data Access failed, timed out or returned something unexpected, and this point isn't cached |
Limits
- US coverage only. SSURGO covers the contiguous US, Alaska, Hawaii, and US territories. Ocean and off-coverage points return
NO_SOIL_DATA. - Dominant component. Each map unit may contain several soil components. SoilPoint returns the one with the highest percentage. Use
componentPctto judge how representative it is. - NCCPI can be null. It comes from the same USDA query as the other fields and is null for non-cropland and areas SSURGO hasn't rated.
- Not for regulatory determinations. Soil data is provided as-is for planning and exploration. It is not a substitute for on-site investigation and is not for regulatory or legal purposes. SoilPoint is not a USDA-endorsed product.
- Outages. SoilPoint keeps answers in memory until its next deploy. While USDA is down, points looked up since then still return 200 (check
cachedAtfor their age); other points return502 SDA_UPSTREAM_ERROR, so retry those later. - See our privacy notice for what we record when you call the API.