Skip to main content
POST
Fetch

When to use this

Use /v1/fetch when:
  • The caller knows exactly which fields they need.
  • The workflow is deterministic and you don’t want LLM latency or variability on every call.
  • You are powering a downstream model that needs raw structured values rather than synthesized prose.
  • You want a preset bundle (e.g., everything needed for flood underwriting) in one round trip.
Use /v1/ask instead when the question is phrased in natural language and the right answer requires combining values with interpretation.

Request shape

The body needs a location — either lat + lng or an address, never both — plus at least one of fields or preset (both is allowed — preset expands first, then fields unions in, and the combined list is capped at 50). Sending both a coordinate and an address, or neither, is a 422.

Fetching by address

The response is identical to a coordinate request, plus a geocode block:
resolved_location is on every response — coordinate requests too, where source is "coordinate" and no geocode block exists. It states, on one uniform key across all endpoints, which point the response actually answered about: a wrong-place answer is only catchable if the place is stated. Check parcel_grade before trusting parcel-specific fields. false means the coordinate was estimated along a street centerline rather than matched to a building — up to ~2,872 m out in rural areas, which is far enough to describe a neighbouring property. precision_note carries that warning in prose, and normalized_address is what the provider actually matched, so compare it against what your user typed. Budget an extra 5.5 s. An address request resolves the geocode before the fan-out starts, and that resolution is worst-case 3 s (primary) + 2.5 s (fallback) on top of the normal fetch time. A cached address adds effectively nothing. If your client timeout is tight, geocode once via /v1/geocode and reuse the coordinate. Address failures use the same codes as /v1/geocodeaddress_not_found, address_too_coarse, geocode_timeout, and friends — one table across /v1/geocode, /v1/fetch and /v1/ask.

Presets

Presets are use-case bundles. Pass "preset": "flood_risk" and you get the floodplain-relevant fields without naming each one. The preset expansions:
To add fields beyond a preset’s bundle, send both:

Example 1: flat fields list in Manhattan

Example 2: preset on a coastal coordinate

Houston, downtown — flood-relevant features in the same call:

Per-field response shape

Each value in fields is a self-contained record: A failed entry additionally carries error (string) and retryable (bool); its value is null. Read status to tell a real value from no-data from a failure without parsing notes.

The honesty pattern: status + partial_failures

/v1/fetch always returns 200 unless the request itself is malformed. A field that failed to fetch is surfaced two ways, so it can never be silently dropped or misread as “not requested”:
  1. In fields, with "status": "failed", "value": null, and the error + retryable hints inline — so every requested field is present in fields, distinguished by its status.
  2. In partial_failures, a flat list of just the failures (kept for back-compat).
Two recoverability hints (present on both the fields entry and the partial_failures record):
  • retryable: true — transient and worth a retry with backoff: a timeout / connection reset, or a metered quota that resets later (e.g. a Regrid billing-period exhaustion).
  • retryable: false — retrying won’t help: the upstream returned a structured error (a missing plan entitlement, an unsupported request).
We never silently drop a field we couldn’t fetch: every requested field appears in fields with a status (ok / absent / failed). Read status (or cross-check partial_failures) rather than assuming presence in fields means success — a failed field is present too.
Never cache a failed field. The HTTP status is 200, so a cache keyed on “request succeeded” will freeze a transient upstream timeout as a permanent answer. Cache ok and absent entries for up to their ttl_seconds; re-fetch failed entries (with backoff when retryable: true).

Failed fields are refunded

Fields that resolve with status: "failed" — an upstream outage on our side — are automatically refunded: the per-field credit price of each failed field is handed back after the response is assembled, and a parcel-record charge is refunded when every parcel field in the selection failed. absent fields are real answers (“the authoritative source has no data here”) and bill normally. The refund is bookkeeping only — the response body is unchanged, and your month-to-date usage on GET /v1/users/me/usage reflects it within seconds.

Limits

  • 50 fields maximum per request (after preset expansion). Exceeding this returns 400 fields_too_many.
  • Batching: one location per request here; up to 25 locations in one call via POST /v1/fetch/batch, which returns index-aligned results in this endpoint’s exact response shape.
  • No caching headers on /v1/fetch. Each call hits the underlying layer orchestrators, which keep their own 24-hour response cache (local disk, plus a shared Redis tier in production). The ttl_seconds hint is for the caller’s own cache layer.

Body

application/json
fields
string[] | null
preset
enum<string> | null
Available options:
terrain,
flood_risk,
wildfire_underwrite,
land_cover,
site_selection,
building_lookup,
points_of_interest,
utilities,
boundaries,
solar_siting,
wind_siting,
storage_siting,
data_center_siting,
grid_interconnect,
natural_hazard
lat
number | null
lng
number | null
address
string | null
Required string length: 1 - 256

Response

Successful Response