[← SoilPoint](https://soilpoint.dev/)

# 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](https://modelcontextprotocol.io) 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](https://soilpoint.dev/docs.md). [/llms.txt](https://soilpoint.dev/llms.txt) lists them, and [/openapi.json](https://soilpoint.dev/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 `componentPct` to 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 `cachedAt` for their age); other points return `502 SDA_UPSTREAM_ERROR`, so retry those later.
- See our [privacy notice](https://soilpoint.dev/privacy) for what we record when you call the API.

[← Back to SoilPoint](https://soilpoint.dev/) · [Blog](https://soilpoint.dev/blog)
