Skip to main content

Comparing Geographies

The /v1/compare endpoint is built for when you need the same variables across multiple places at once — and you want the response structured for easy comparison rather than iteration.

Compare vs. /v1/data

Both endpoints query ACS data. The difference is structure:

/v1/data/v1/compare
Primary useSingle geography, flexible outputMultiple geographies, tabular output
Response shapeFlat variable map per geographyColumn-oriented table with geographies as rows
Best forDetailed single-place analysisRanking, benchmarking, dashboards

If you're building a table where each row is a geography and each column is a variable, use /v1/compare. If you're pulling a deep variable set for one place, use /v1/data.

Basic Example: Population Across Three States

Compare total population for Texas, California, and New York.

curl -G "https://api.prairiecloud.io/v1/compare" \
-H "X-API-Key: pck_live_your_key_here" \
--data-urlencode "variables=pop_total" \
--data-urlencode "geo=state:48,state:06,state:36" \
--data-urlencode "vintage=2024" \
--data-urlencode "dataset=acs5"
import requests

resp = requests.get(
"https://api.prairiecloud.io/v1/compare",
headers={"X-API-Key": "pck_live_your_key_here"},
params={
"variables": "pop_total",
"geo": "state:48,state:06,state:36",
"vintage": "2024",
"dataset": "acs5",
},
)
data = resp.json()

Response:

{
"vintage": 2024,
"dataset": "acs5",
"columns": ["geography", "geo_id", "pop_total", "pop_total_moe"],
"rows": [
{ "geography": "Texas", "geo_id": "state:48", "pop_total": 30503301, "pop_total_moe": null },
{ "geography": "California", "geo_id": "state:06", "pop_total": 38965193, "pop_total_moe": null },
{ "geography": "New York", "geo_id": "state:36", "pop_total": 19571216, "pop_total_moe": null }
]
}
info

Margin of error (_moe fields) is null for state-level estimates because the sample size is large enough that Census suppresses the MOE. At smaller geographies, you'll see real values.

Multi-Variable Example

Add median household income and median age to the same request:

curl -G "https://api.prairiecloud.io/v1/compare" \
-H "X-API-Key: pck_live_your_key_here" \
--data-urlencode "variables=pop_total,income_median_household" \
--data-urlencode "geo=state:48,state:06,state:36" \
--data-urlencode "vintage=2024" \
--data-urlencode "dataset=acs5"
resp = requests.get(
"https://api.prairiecloud.io/v1/compare",
headers={"X-API-Key": "pck_live_your_key_here"},
params={
"variables": "pop_total,income_median_household",
"geo": "state:48,state:06,state:36",
"vintage": "2024",
"dataset": "acs5",
},
)

Response:

{
"vintage": 2024,
"dataset": "acs5",
"columns": [
"geography", "geo_id",
"pop_total", "pop_total_moe",
"income_median_household", "income_median_household_moe"
],
"rows": [
{
"geography": "Texas",
"geo_id": "state:48",
"pop_total": 30503301,
"pop_total_moe": null,
"income_median_household": 73035,
"income_median_household_moe": 312
},
{
"geography": "California",
"geo_id": "state:06",
"pop_total": 38965193,
"pop_total_moe": null,
"income_median_household": 84097,
"income_median_household_moe": 289
},
{
"geography": "New York",
"geo_id": "state:36",
"pop_total": 19571216,
"pop_total_moe": null,
"income_median_household": 75157,
"income_median_household_moe": 404
}
]
}

Each requested variable gets its own column, plus a corresponding _moe column. To exclude MOE columns in your own processing, just filter on column names that don't end in _moe.

Real-World Use Case: Highest-Income Texas Counties

Question: Which Texas counties have the highest median household income?

Texas has 254 counties (FIPS codes 48001–48507 in odd increments). Rather than hardcoding all of them, use the geography catalog to fetch the list first:

import requests

API_KEY = "pck_live_your_key_here"
HEADERS = {"X-API-Key": API_KEY}
BASE = "https://api.prairiecloud.io"

# 1. Get all Texas counties
geo_resp = requests.get(
f"{BASE}/v1/geographies",
headers=HEADERS,
params={"type": "county", "state": "48"},
)
counties = geo_resp.json()["data"]
geo_ids = ",".join(c["geo_key"] for c in counties)

# 2. Compare median income across all of them
compare_resp = requests.get(
f"{BASE}/v1/compare",
headers=HEADERS,
params={
"variables": "income_median_household",
"geo": geo_ids,
"vintage": "2024",
"dataset": "acs5",
},
)
rows = compare_resp.json()["rows"]

# 3. Rank by income, drop suppressed values
ranked = sorted(
[r for r in rows if r["income_median_household"] is not None],
key=lambda r: r["income_median_household"],
reverse=True,
)

for rank, row in enumerate(ranked[:10], 1):
print(f"{rank}. {row['geography']}: ${row['income_median_household']:,}")

Output:

1. Collin County: $107,432
2. Fort Bend County: $103,218
3. Williamson County: $99,871
4. Rockwall County: $97,603
5. Hays County: $89,240
...
tip

When querying many geographies at once, check the suppressed count in the response metadata. Counties with very small populations may return null for income variables — filter those out before ranking.

Tips

Maximum geographies per request: The /v1/data endpoint supports up to 5,000 geographies per request on Pro, 25,000 on Business, and 50,000 on Enterprise. The /v1/compare endpoint is limited to 10 geographies per request across all tiers. For state-level analysis with all 3,000+ U.S. counties, paginate using the offset and limit parameters on /v1/geographies to batch your geo IDs.

Finding the right variable names: Not sure what api_name to use? Search the variable catalog:

curl -G "https://api.prairiecloud.io/v1/variables" \
-H "X-API-Key: pck_live_your_key_here" \
--data-urlencode "q=median income"

This returns matching variables with their api_name, description, and Census source code — copy the api_name directly into your variables parameter.

Consistent vintage across geographies: All geographies in a single /v1/compare request must use the same vintage. Cross-vintage comparison is what /v1/timeseries is for.


Next Steps