Geographic Boundaries
Build choropleth maps, run spatial analysis, and visualize Census data — all from the PrairieCloud API. The boundary endpoints deliver GeoJSON shapes for every geography we cover, at multiple resolution tiers.
The killer feature: add include_geometry=true to any /v1/data request and get data and boundary shapes in a single API call. One request, one response, one map.
Available Geography Types
PrairieCloud serves 331,559 GeoJSON boundaries across all 7 geography levels:
| Type | Description | Boundary Count | Tier Required |
|---|---|---|---|
nation | United States | 1 | Free |
state | U.S. states + DC + territories | 56 | Free |
county | Counties and county equivalents | 3,243 | Pioneer |
metro | Metropolitan and micropolitan statistical areas | 939 | Pioneer |
cd | Congressional districts (118th Congress) | 436 | Pioneer |
tract | Census tracts | 85,058 | Pro |
block_group | Census block groups | 241,854 | Business |
Tract boundaries (85,058) are available on the Pro plan and above. Block group boundaries (241,854) require a Business plan or higher. Both include boundaries for ACS 5-Year vintages 2017–2024.
Resolution Tiers
Boundary files are available at three resolution tiers, matching the U.S. Census Bureau's cartographic boundary file conventions:
| Resolution | Best For | File Size (states) | Tier Access |
|---|---|---|---|
| 20m (default) | Web maps, dashboards, interactive visualizations | ~1 MB | Free / Pioneer+ |
| 5m | Detailed maps, print-quality at regional scale | ~5 MB | Pro+ |
| 500k | High-fidelity print, enterprise cartography | ~25 MB | Enterprise |
The 20m resolution is visually identical to 5m at typical web zoom levels and loads significantly faster. Only upgrade when you need fine coastal or border detail.
Endpoints
Get boundaries by type
GET /v1/boundaries/{geo_type}
Returns a GeoJSON FeatureCollection containing all geographies of the specified type.
Path parameters:
| Parameter | Type | Description |
|---|---|---|
geo_type | string | One of: nation, state, county, metro, cd, tract, block_group |
Query parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
state | string | — | Filter by state FIPS code (e.g., 48 for Texas) |
resolution | string | 20m | Resolution tier: 20m, 5m, or 500k |
vintage | string | 2024 | Boundary vintage year |
Examples:
# All counties in Texas
curl -H "X-API-Key: YOUR_KEY" \
"https://api.prairiecloud.io/v1/boundaries/county?state=48&resolution=20m"
# All census tracts in Harris County, Texas
curl -H "X-API-Key: YOUR_KEY" \
"https://api.prairiecloud.io/v1/boundaries/tract?state=48&county=201"
# All block groups in a specific county (Business tier)
curl -H "X-API-Key: YOUR_KEY" \
"https://api.prairiecloud.io/v1/boundaries/block_group?state=48&county=201"
Get a single boundary
GET /v1/boundaries/{geo_key}
Returns a single GeoJSON Feature for a specific geography.
Path parameters:
| Parameter | Type | Description |
|---|---|---|
geo_key | string | Geography key (e.g., state:48, county:48201, tract:48201311100, block_group:482013111001) |
Examples:
# A county boundary
curl -H "X-API-Key: YOUR_KEY" \
"https://api.prairiecloud.io/v1/boundaries/county:48201"
# A census tract boundary
curl -H "X-API-Key: YOUR_KEY" \
"https://api.prairiecloud.io/v1/boundaries/tract:48201311100"
# A block group boundary (Business tier)
curl -H "X-API-Key: YOUR_KEY" \
"https://api.prairiecloud.io/v1/boundaries/block_group:482013111001"
Get centroids
GET /v1/centroids/{geo_type}
Returns lightweight GeoJSON Point geometries — the geographic center of each boundary. Ideal for dot maps, label placement, and marker clustering.
Path parameters:
| Parameter | Type | Description |
|---|---|---|
geo_type | string | One of: nation, state, county, metro, cd, tract, block_group |
Query parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
state | string | — | Filter by state FIPS code |
Examples:
# County centroids in Texas
curl -H "X-API-Key: YOUR_KEY" \
"https://api.prairiecloud.io/v1/centroids/county?state=48"
# Tract centroids in Harris County
curl -H "X-API-Key: YOUR_KEY" \
"https://api.prairiecloud.io/v1/centroids/tract?state=48&county=201"
Data + shapes in one call
GET /v1/data?include_geometry=true
This is the fastest path from API call to map. Add include_geometry=true to any /v1/data request and each data point includes its full GeoJSON geometry. No second request needed.
Requires: Pro tier or above.
Example:
# County-level population map of Texas
curl -H "X-API-Key: YOUR_KEY" \
"https://api.prairiecloud.io/v1/data?vintage=2024&variables=pop_total&geo=county:48*&include_geometry=true"
# Tract-level median income in Harris County
curl -H "X-API-Key: YOUR_KEY" \
"https://api.prairiecloud.io/v1/data?vintage=2024&variables=income_median_household&geo=tract:48201*&include_geometry=true"
Each data point in the response includes a geometry field:
{
"data": {
"tract:48201311100": {
"name": "Census Tract 3111, Harris County, Texas",
"variables": {
"income_median_household": {
"estimate": 72150,
"margin_of_error": 8420
}
},
"geometry": {
"type": "MultiPolygon",
"coordinates": [[[[-95.4, 29.7], ...]]]
}
}
}
}
Bulk boundary delivery
The public API serves boundary data through /v1/boundaries/{identifier} for single features and filtered collections. PrairieCloud does not expose a public static bulk-download endpoint.
Enterprise customers can arrange custom bulk boundary delivery for approved use cases, including higher-resolution 500k cartographic boundaries where available.
Tier Access
| Feature | Free | Pioneer | Pro | Business | Enterprise |
|---|---|---|---|---|---|
| Boundary and centroid types | nation, state | + county, metro, cd | + tract | + block_group | All |
| Resolution tiers | 20m only | 20m only | 20m, 5m | 20m, 5m | 20m, 5m, 500k |
include_geometry on /v1/data | ❌ | ❌ | Through tract | Through block group | Through block group |
| Custom bulk boundary delivery | ❌ | ❌ | ❌ | ❌ | ✅ |
| Centroids | nation, state | + county, metro, cd | + tract | + block_group | All |
Boundary and centroid requests are free — they cost 0 units against your monthly quota for every tier that has access.
Response Format
All boundary responses conform to GeoJSON RFC 7946.
Collection response (/v1/boundaries/{geo_type}):
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"id": "state:48",
"properties": {
"geo_key": "state:48",
"name": "Texas",
"fips": "48",
"geo_type": "state"
},
"geometry": {
"type": "MultiPolygon",
"coordinates": [[[[-106.6, 31.8], [-106.5, 31.8], ...]]]
}
}
]
}
Single feature response (/v1/boundaries/{geo_key}):
{
"type": "Feature",
"id": "tract:48201311100",
"properties": {
"geo_key": "tract:48201311100",
"name": "Census Tract 3111, Harris County, Texas",
"fips": "48201311100",
"geo_type": "tract",
"state_fips": "48",
"county_fips": "48201"
},
"geometry": {
"type": "MultiPolygon",
"coordinates": [[[[-95.4, 29.7], [-95.3, 29.7], ...]]]
}
}
Response headers:
| Header | Description |
|---|---|
X-Boundary-Vintage | Boundary vintage year (e.g., 2024) |
Content-Type | application/geo+json |
Cache-Control | public, max-age=604800, s-maxage=2592000 (7-day browser, 30-day CDN) |
ETag | Content hash for conditional requests |
Caching
Boundary data changes infrequently (typically once per year with new Census releases). The API sets aggressive cache headers:
- Browser cache: 7 days (
max-age=604800) - CDN cache: 30 days (
s-maxage=2592000) - ETag/If-None-Match: Send the
ETagvalue back asIf-None-Matchon subsequent requests. If the boundary hasn't changed, you'll receive a304 Not Modifiedwith zero body — saving bandwidth and latency.
# First request — get the ETag
curl -i -H "X-API-Key: YOUR_KEY" \
"https://api.prairiecloud.io/v1/boundaries/state"
# Response includes: ETag: "abc123..."
# Subsequent request — conditional
curl -H "X-API-Key: YOUR_KEY" \
-H 'If-None-Match: "abc123..."' \
"https://api.prairiecloud.io/v1/boundaries/state"
# Returns 304 Not Modified if unchanged
Code Examples
Python: Tract-level choropleth map
Build a choropleth map of median household income by tract in Harris County with a single API call:
import requests
import plotly.express as px
import pandas as pd
API_KEY = "your_key_here"
# One call: data + shapes
resp = requests.get(
"https://api.prairiecloud.io/v1/data",
params={
"vintage": "2024",
"variables": "income_median_household",
"geo": "tract:48201*",
"include_geometry": "true",
},
headers={"X-API-Key": API_KEY},
)
data = resp.json()
# Build GeoJSON for plotly
features = []
values = []
for geo_key, point in data["data"].items():
income = point["variables"]["income_median_household"]["estimate"]
if income is not None:
features.append({
"type": "Feature",
"id": geo_key,
"properties": {"name": point["name"]},
"geometry": point["geometry"]
})
values.append({"geo_key": geo_key, "income": income})
geojson = {"type": "FeatureCollection", "features": features}
df = pd.DataFrame(values)
fig = px.choropleth(df, geojson=geojson, locations="geo_key",
featureidmap=lambda x: x["id"],
color="income",
title="Median Household Income by Tract — Harris County, TX")
fig.show()
JavaScript: Leaflet map with tracts
const response = await fetch(
'https://api.prairiecloud.io/v1/boundaries/tract?state=48&county=201',
{ headers: { 'X-API-Key': 'your_key_here' } }
);
const geojson = await response.json();
L.geoJSON(geojson, {
style: { weight: 1, color: '#666', fillOpacity: 0.3 }
}).addTo(map);
R: Plot tract boundaries
library(sf)
library(httr2)
resp <- request("https://api.prairiecloud.io/v1/boundaries/tract") |>
req_url_query(state = "48", county = "201") |>
req_headers("X-API-Key" = "your_key_here") |>
req_perform()
tracts <- st_read(resp_body_string(resp))
plot(st_geometry(tracts), main = "Census Tracts — Harris County, TX")
Next Steps
- Data Guide — Core data endpoints and query patterns
- Rate Limiting — Quota limits and cost units
- Error Handling — Full error response format
- Python Examples — More Python code examples