Address to Census Tract & Demographics (ACS)
Pricing
from $1.00 / 1,000 location matched to a census tracts
Address to Census Tract & Demographics (ACS)
Geocode US addresses or coordinates to Census tract and block group GEOIDs with the U.S. Census Geocoder, then append ACS 5-year demographics: population, median income, median age, poverty rate, education, housing and unemployment. Batch CSV or list input, per-row status, built for AI agents.
Pricing
from $1.00 / 1,000 location matched to a census tracts
Rating
0.0
(0)
Developer
Killis Haraway
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
3 hours ago
Last modified
Categories
Share
Address to Census Tract & Demographics
Turn US addresses or latitude/longitude points into Census tract and block group IDs (GEOIDs), then append American Community Survey (ACS) 5-year estimates for that area: population, median age, median household income, per capita income, poverty rate, education, housing and unemployment.
All data comes from free U.S. Census Bureau services: the Census Geocoder for matching and the Census Data API for ACS tables. The Actor handles the parts that take time to get right: batch limits, retries, matching the tract year to the data year (Connecticut changed its county codes in 2022), the ACS "no estimate" codes, and a clear status on every row.
It was built to be called by AI agents as well as people. You get one dataset item per input row, in input order, with a status and a message.
What you can do
| Operation | Input | Output per row |
|---|---|---|
geocode | Addresses: one-line strings, structured records, or a CSV file | Matched address, coordinates, state/county/tract/block group codes, GEOIDs, demographics |
coordinates | Latitude/longitude points | Tract/block group and demographics for the area containing each point |
demographics | Tract (11-digit) or block group (12-digit) GEOIDs | Demographics only |
Typical uses: append neighborhood income and poverty data to a customer or donor list, check eligibility rules that are set by census tract, site selection, grant applications, and research datasets.
Input examples
A few addresses:
{"operation": "geocode","addresses": ["125 N Main St, Memphis, TN 38103", "4600 Silver Hill Rd, Washington, DC 20233"]}
A CSV file (columns such as address, or street/city/state/zip, plus an optional id, are detected automatically):
{"operation": "geocode","csvUrl": "https://example.com/customers.csv","csvColumns": {"street_column": "Addr 1", "zip_column": "Postal", "id_column": "Account"},"fields": ["income", "housing"]}
Structured records from an agent, block group level, with margins of error:
{"operation": "geocode","addressRecords": [{"id": "c1", "street": "165 Capitol Ave", "city": "Hartford", "state": "CT", "zip": "06106"}],"geoLevel": "block_group","includeMoe": true}
Coordinates:
{"operation": "coordinates", "points": [{"id": "store-12", "latitude": 35.1495, "longitude": -90.049}]}
IDs only (cheaper, no demographics): add "demographics": false.
Output
One dataset item per input row. Example (demographic values shortened; real values come from the ACS release you choose):
{"row": 1,"id": "c1","input": "125 N Main St, Memphis, TN, 38103","status": "matched","message": null,"match_type": "Exact","matched_address": "125 N MAIN ST, MEMPHIS, TN, 38103","latitude": 35.148562911373,"longitude": -90.05155413782,"state_fips": "47","county_fips": "157","tract": "004200","block_group": null,"tract_geoid": "47157004200","block_group_geoid": null,"geo_name": "Census Tract 42; Shelby County; Tennessee","total_population": 2154,"median_age": 34.1,"median_household_income": 51234,"poverty_rate": 21.7,"pct_bachelors_or_higher": 38.2,"median_home_value": 245000,"median_gross_rent": 1180,"unemployment_rate": 5.4,"demographics_notes": null}
(The demographic numbers above are illustrative, not real ACS values.)
Rows that don't match still appear, so you can join results back to your list by row or id:
status | Meaning | message tells you |
|---|---|---|
matched | Found; IDs (and demographics) filled in | Nothing, or a note |
no_match | The Census address database has no match | Check the house number and street; add city + state or ZIP |
tie | Several addresses match equally | Add the ZIP or city and state |
invalid | Not usable as given (empty, PO Box, no house number, over 100 characters) | What's wrong |
error | The geocoder returned nothing for this row | Retry later |
Also saved:
OUTPUT(key-value store): totals per status, match rate, ACS release used, warnings, and an error code if the run failed.results.csv: the same rows as a spreadsheet, demographics as columns.
Demographic fields
| Field | Unit | ACS variables |
|---|---|---|
total_population | people | B01003_001 |
median_age | years | B01002_001 |
median_household_income | USD (inflation-adjusted to the end year) | B19013_001 |
per_capita_income | USD | B19301_001 |
poverty_rate | % of people below the poverty level | C17002_001–003 |
population_25_plus | people | B15003_001 |
pct_high_school_or_higher | % of people 25+ | B15003_017–025 |
pct_bachelors_or_higher | % of people 25+ | B15003_022–025 |
housing_units | units | B25001_001 |
vacancy_rate | % of housing units | B25002_001, _003 |
owner_occupied_rate, renter_occupied_rate | % of occupied units | B25003_001–003 |
median_home_value | USD | B25077_001 |
median_gross_rent | USD per month | B25064_001 |
unemployment_rate | % of civilian labor force | B23025_003, _005 |
Use fields to pick groups (population, income, education, housing, employment) or individual field names.
includeMoe adds *_moe columns (90% margin of error) for the direct estimates.
When the Census publishes no estimate for an area (too few survey responses, or a tract with no residents such as a park or an airport),
the value is null and demographics_notes explains why. It never contains the Census placeholder codes like -666666666.
When a run fails
The run ends as Failed, the status message starts with an error code, and OUTPUT.error has details. You are not charged for failed runs.
| Code | Meaning |
|---|---|
NO_VALID_INPUT | No usable rows (empty input, or all rows invalid) |
NO_MATCHES | Nothing matched; check the per-row messages in the dataset |
COLUMN_NOT_FOUND | The CSV has no address/street column, or a column you named doesn't exist. The message lists the available columns |
CSV_PARSE_ERROR | The CSV couldn't be read |
ACS_YEAR_UNAVAILABLE | That ACS release isn't published yet |
CENSUS_KEY_INVALID | Only if you supplied your own censusApiKey and the Census rejected it |
UPSTREAM_RATE_LIMITED, UPSTREAM_UNAVAILABLE | The Census service is busy or down. Rows already finished are kept |
Pricing (pay per event)
| Event | Price |
|---|---|
| Location matched to a Census tract (primary event) | $0.001 ($1.00 per 1,000) |
| ACS demographics appended | $0.0006 ($0.60 per 1,000) |
| Actor start (set by Apify) | $0.00005 |
You pay only for matched rows. no_match, tie and invalid rows are free.
1,000 addresses with a 92% match rate and demographics cost $1.47. IDs only would cost $0.92.
The Actor never spends past your maximum cost per run. It processes rows in chunks of 1,000, sizes each chunk to the remaining budget, and stops cleanly with a status message when the budget runs out.
Limits and accuracy
- Up to 100,000 rows per run. CSV files up to 50 MB. Each address part up to 100 characters (a Census Geocoder limit).
- US addresses only (50 states, DC, Puerto Rico and the Island Areas). The Census Geocoder matches by interpolating along address ranges.
It's good at street addresses and weak at new construction, rural routes and PO Boxes. Typical match rates for clean lists are high, but check
match_rateinOUTPUT. - Speed: roughly 25 seconds per 1,000 addresses for the geocoding step (one measurement; the Census service speed varies). Block groups add a second pass.
- Default data year: ACS 5-year 2020–2024 (
acsYear: 2024). Tract codes are matched to the same year's boundaries. Older releases back to 2017 are available. - Tract estimates are more reliable than block group estimates. Block group values can have large margins of error, so use
includeMoewhen the numbers matter. - These are area estimates for the tract or block group, not facts about any address or household.
Use it from an AI agent (MCP)
claude mcp add --transport http apify "https://mcp.apify.com/?actors=errandkit/census-tract-demographics" \--header "Authorization: Bearer <APIFY_TOKEN>"
Agent tips: pass small lists in addresses or addressRecords; read the dataset items; check status per row; read OUTPUT.summary.match_rate.
Use it from the API
curl -X POST "https://api.apify.com/v2/acts/errandkit~census-tract-demographics/run-sync-get-dataset-items?token=<APIFY_TOKEN>" \-H "Content-Type: application/json" \-d '{"operation":"geocode","addresses":["125 N Main St, Memphis, TN 38103"],"fields":["income"]}'
Data sources, attribution and acceptable use
- U.S. Census Bureau Geocoder (geocoding.geo.census.gov) and Census Data API, ACS 5-year detailed tables (api.census.gov). Both are free public services.
- This product uses the Census Bureau Data API but is not endorsed or certified by the Census Bureau.
- Census terms prohibit using these data, alone or combined with other data, to identify any individual person, household or business. Don't use the output to do that.
- Your addresses are sent to the Census Geocoder to be matched. The Actor stores results only in your own Apify run storage and keeps nothing after the run.
Support
Open an issue on the Issues tab with the run ID. We usually reply within 1–2 business days.