Gis, Qgis, ArcGisΒ  Experts Just a Click Away

A county boundary layer tells QGIS where counties are. A Census table tells QGIS what population, income, housing, poverty, or demographic value belongs to each county. To make a Census county map, you connect those two kinds of data through a shared geographic identifier, then symbolize the joined value as a choropleth map.

This tutorial demonstrates the complete workflow with a reproducible example: mapping median household income by U.S. county. The example uses the 2020–2024 American Community Survey (ACS) 5-year estimates, released as the 2024 ACS 5-year product, and Census table B19013, Median Household Income in the Past 12 Months. The mapped estimate is B19013_001E, reported in 2024 inflation-adjusted dollars. The boundary layer is a county-level TIGER/Line Shapefile from a compatible Census vintage. The same procedure works for population, poverty, education, housing, age, and other county-level statistics, provided that the geography and join key match.

Important: ACS values are estimates, not exact enumerations. If you publish an ACS map, identify the ACS product, year, geography, variable, andβ€”when interpretation depends on precisionβ€”the margin of error.

How do you join Census data to a county map in QGIS?

Download a county polygon layer, obtain a Census table containing county-level statistics, and match both datasets with the same geographic identifier, normally a five-character county GEOID or an equivalent state-and-county key. Load the table into QGIS as a non-spatial table, add it through the county layer’s Layer Properties > Joins panel, verify that the joined values are present, and use the joined field in Layer Properties > Symbology > Graduated to create the choropleth.

What you are joining: spatial data and tabular data

The county map is spatial data. Each feature has polygon geometry representing a county, together with attributes such as a county name, state code, county code, and a geographic identifier. The Census download is tabular data. Each row represents a geography and contains identifiers, a name, one or more estimates, margins of error, and sometimes annotation or metadata fields.

The join does not redraw a boundary. It associates a statistical row with the matching county feature: the county map tells QGIS where each county is; the Census table tells QGIS what value belongs to each county.

Dataset Role
TIGER/Line county layer Supplies polygons, identifiers, and CRS
ACS or Decennial Census table Supplies statistics
QGIS project Connects, verifies, styles, and exports the result

GEOID: the key to a reliable Census join

A GEOID is a geographic identifier used to uniquely identify Census geographic entities. The Census Bureau explains that GEOIDs support the organization, exchange, analysis, and mapping of data across states, counties, tracts, block groups, and other geographies.1

For a county in the 50 states, the familiar five-character county code is made from a two-digit state FIPS code followed by a three-digit county FIPS code. For example, 01001 identifies Autauga County, Alabama in products that use the five-character county key: 01 is Alabama and 001 is the county code. The structure matters because 001 by itself is not unique nationally; many states have a county whose local code ends in 001.1

The exact field name varies. A TIGER/Line layer may expose GEOID, or separate STATEFP and COUNTYFP fields. An API response normally returns separate state and county columns. A data.census.gov file may contain a longer GEO.ID such as 0500000US01001, which is not automatically identical to a five-character TIGER field.

Do not join on county name alone. Names can repeat, punctuation can differ, and names such as β€œWashington County” occur in multiple states. Use a full identifier that includes the state context, and ensure the two fields have identical values and compatible data types.

The following values are illustrative, not statistics used by this tutorial:

County GEOID Population
Example County 01001 58,000
Example County 01003 230,000

The leading-zero problem: keep GEOIDs as text

Geographic identifiers should generally be treated as strings, not numbers. If 01001 is imported as a number, software may convert it to 1001. The join then fails because the string 01001 and the number-like value 1001 are not the same key.

This issue is especially common when a CSV has been opened and resaved in spreadsheet software. A spreadsheet may infer that a GEOID is numeric and strip the leading zero. It may also change long identifiers into scientific notation. If you use a spreadsheet, format the identifier column as text before importing, but the safest approach is to preserve the original Census file and inspect it directly in QGIS.

In QGIS, inspect the field type in the attribute table or the layer’s field information. If one dataset uses a text field and the other uses an integer field, create a normalized text key before joining. QGIS expressions can convert a value to text and pad it to five characters:

lpad(to_string("STATEFP") || to_string("COUNTYFP"), 5, '0')

Use this expression only when STATEFP and COUNTYFP are the appropriate component fields and their values are already clean. If the layer already has a valid GEOID, use that field instead. For an API table with separate state and county fields, create the equivalent key in a spreadsheet, text editor, database, or QGIS field calculator:

lpad(to_string("state") || to_string("county"), 5, '0')

The padding operation is a repair for a known format, not a substitute for checking the source. Never assume that every Census geography uses five digits. Tracts, block groups, places, congressional districts, ZCTAs, and county-equivalent areas have different identifier structures.

Choose the data before opening QGIS

The example dataset

This tutorial uses the following inputs.

Item Selection
Statistical program American Community Survey
Product 2020–2024 ACS 5-year estimates, commonly identified as 2024 ACS 5-year
Geography County and county equivalent
Table B19013, Median Household Income in the Past 12 Months
Estimate variable B19013_001E
Units 2024 inflation-adjusted dollars
Boundary County-level TIGER/Line Shapefile from a compatible vintage
Join key Five-character county key, normally state FIPS plus county FIPS
Expected output A county choropleth of estimated median household income

The official variable definition identifies B19013_001E as the estimate for median household income in the past 12 months, in 2024 inflation-adjusted dollars.4 The associated table also has a margin-of-error variable, typically identified by an M suffix rather than E. Map the estimate for the main choropleth, and retain the margin of error for documentation and interpretation.

ACS versus the Decennial Census

The Decennial Census is the source for official ten-year enumeration results and products. The ACS is a continuous survey that produces estimates for characteristics such as income, poverty, housing, education, and demographics. ACS 1-year and 5-year products differ in release rules and geographic coverage; the 2024 ACS 5-year product supports county geography. ACS values include sampling uncertainty, and multi-year comparisons should use Census comparability guidance and document the vintage.3

Download U.S. county boundaries

Census TIGER/Line files are a reliable starting point for county polygons. The Census Bureau states that core TIGER/Line files contain geographic entity codes that can be linked to demographic data, but do not themselves include demographic statistics.2

Use the official TIGER/Line Shapefiles page, not an unverified third-party mirror. The page provides the current time-series entry point and identifies Shapefile availability from 2007 onward.2

  1. Open the official TIGER/Line Shapefiles page.
  2. Select the boundary year you intend to document. For this tutorial, choose a county boundary vintage compatible with the 2024 ACS geography. If the page offers a direct 2024 county download, use it; if your project requires the newest boundaries, document that you used the newer vintage and explain the choice.
  3. Choose the County geographic layer. Use a national download for a nationwide map or a state-level download when a smaller file is more convenient.
  4. Download the Shapefile archive and save it in a project folder.
  5. Extract the archive. A Shapefile is a group of files, not just the .shp file. Keep the .shp, .shx, .dbf, and .prj files together.
  6. In QGIS, add the .shp file with Layer > Add Layer > Add Vector Layer or through the Data Source Manager.

After loading, inspect the attribute table for a county name and a key such as GEOID, STATEFP, or COUNTYFP; do not assume a field name. Record the boundary year, download date, and source URL because county-equivalent definitions and boundaries can change.

Obtain the Census statistics

There are two practical routes: the Census data website, which is easiest for browsing, and the API, which is easier to document and repeat.

Option A: data.census.gov

Open the official B19013 ACS 5-year table. Set the geography to counties and confirm that the product is ACS 5-Year Estimates for 2024. Download the table as a CSV or another available tabular format.

Inspect the downloaded file before importing it. Census downloads may contain multiple header or description rows, a geographic name, a GEO.ID field, an estimate column, and a margin-of-error column. If the download contains the full GEO.ID value, determine whether your county layer uses the full identifier or only the five-character county key. Normalize the two sides deliberately rather than assuming that a field called β€œGEOID” means the same format everywhere.

Option B: the Census API

The official 2024 ACS 5-year documentation lists county as a supported geography under state, and its examples show the for=county:*&in=state:* structure.3 The conceptual request for all counties is:

https://api.census.gov/data/2024/acs/acs5?get=NAME,B19013_001E&for=county:*&in=state:*

At the time of writing, the API documentation and live endpoint indicate that a valid API key may be required for each request. Request and use a key according to the Census Bureau’s developer guidance; never publish a private key in a tutorial, script repository, or URL.

The response contains a header row and county records with fields such as NAME, B19013_001E, state, and county. The two FIPS components must be concatenated as text to create the five-character county key. For one state, the more targeted pattern is:

https://api.census.gov/data/2024/acs/acs5?get=NAME,B19013_001E&for=county:*&in=state:06&key=YOUR_KEY

The example uses California’s two-digit state code only to illustrate a filtered request. Replace it with the state code you need, or use the wildcard request for a national file. Check the live ACS API examples, supported geography page, and variable metadata before reusing a request because endpoints, variable definitions, and key requirements can change.3 6

Save the API response as CSV and add a GEOID field with the concatenated state and county codes. Keep the original state and county fields as an audit trail.

Inspect the Census table before joining

Do not immediately add a CSV and start styling. First inspect its structure.

The table should contain one record per county, a name, an identifier, the estimate, andβ€”when downloaded from a detailed tableβ€”a margin-of-error field. For B19013, map B19013_001E; B19013_001M describes uncertainty and is not another income value.

Check the following before proceeding:

Check What to confirm
Geography Records are counties or county equivalents, not places, tracts, or states
Vintage The file is the 2024 ACS 5-year product
Estimate B19013_001E is selected, not the MOE field
Identifier The key includes state and county context
Data type The join key is text and retains leading zeros
Duplicates Each county key occurs once in the table
Special values Nulls or annotation codes are not silently treated as ordinary income

Load the county layer in QGIS

Open QGIS and create a new project. Save it immediately in the same project folder as the boundary and CSV files. This makes relative paths and future editing easier.

Add the TIGER/Line layer with Layer > Add Layer > Add Vector Layer. Select the extracted county Shapefile and click Add. In the Layers panel, right-click the layer and choose Zoom to Layer if the map canvas does not show the counties.

Open the attribute table. Confirm that the geometry is polygonal, the layer covers the intended area, and the candidate identifier has the expected values. For a national county layer, check several records in different states. If the field is split into STATEFP and COUNTYFP, either join on a pre-existing GEOID field or create a five-character text key with the Field Calculator.

Import the Census CSV as a table

Current QGIS documentation places delimited-text import in the Data Source Manager. To load a CSV without coordinates as a table:8

  1. Open Layer > Add Layer > Add Delimited Text Layer or open the Data Source Manager and choose the Delimited Text tab.
  2. Browse to the Census CSV.
  3. Check the delimiter and file encoding. Most Census CSV files are comma-delimited and use standard UTF-8-compatible text, but inspect the preview.
  4. Confirm that the header is detected correctly and that the estimate field is numeric.
  5. Set Geometry definition to No geometry (attribute only table). Do not assign X and Y fields to a table that has no coordinates.
  6. Click Add. QGIS loads the CSV as a non-spatial table.

The field-type detection controls are important. Confirm that the normalized GEOID is a string. If a leading zero is missing, stop and fix the source or create a corrected text field before joining. If an income estimate is imported as text, convert it to a numeric field in QGIS or correct the CSV. Do not map a text representation of a number and assume QGIS will classify it correctly.

Join the Census table to the county polygons

This is the central operation: the county layer remains the target layer, and the Census table supplies fields.

  1. In the Layers panel, right-click the county layer and choose Properties. You can also double-click the layer.
  2. Select the Joins section.
  3. Click the Add button, usually represented by a plus symbol.
  4. For the join layer or table, select the imported Census CSV.
  5. For the join field, select the Census table’s normalized text key, such as GEOID.
  6. For the target field, select the county layer’s matching GEOID or normalized county key.
  7. Review any join prefix option. A prefix can make it obvious that fields came from the Census table; avoid a prefix that makes the final field name unnecessarily difficult to read.
  8. Apply the join and click OK.
  9. Open the county layer’s attribute table. Scroll to the joined fields and confirm that B19013_001E is present.

QGIS’s current documentation describes this as a join based on a shared attribute, and the geometry type does not determine whether the attribute join works.7 The join is normally a project-level association until you export or save a new layer. If you need a self-contained deliverable, use Export > Save Features As after verification and save a GeoPackage or another suitable format.

What a successful join looks like

The county geometry should look exactly as it did before the join. The attribute table should now expose the Census fields alongside the county attributes.

County GEOID Joined Census value
County A 01001 58,000
County B 01003 230,000

These values are illustrative. Your table will contain the actual 2024 ACS estimates, and each county should receive the value associated with its exact key. If every joined value is null, the join did not match. If only some values appear, investigate the unmatched records before mapping.

Verify the join instead of assuming it worked

A plausible-looking map can still contain wrong values, so verification is required.

Sort the joined B19013_001E field and confirm that it contains a meaningful range rather than all nulls or one repeated value. Filter a few known GEOIDs and compare their names and values with the source table, including records from states whose codes begin with zero.

Count null values in the joined field using the attribute table’s statistics or a field filter. You can also use:

"B19013_001E" IS NULL

The joined field may include a prefix, so select it from the expression builder rather than guessing. A large number of nulls usually indicates a key, geography, vintage, or import problem. Compare record counts and check for duplicate table keys; reduce the source to one row per county before joining if necessary.

Troubleshoot failed joins

No records joined

The most common cause is a mismatch between the two key fields. Check whether one side contains 01001 while the other contains 1001, whether one side is numeric and the other text, or whether the Census download uses 0500000US01001 while the boundary uses 01001. Confirm that you selected the target and join fields in the correct order.

Also check the geography. A county boundary cannot correctly join to a table of places, tracts, block groups, or county parts merely because the records have names. Confirm the Census year, table, and geography selection. If the table contains duplicate identifiers, create a one-record-per-county input first.

Only some counties joined

This often means leading zeros were lost for a subset of states, the table is missing records, or the boundary and statistical files use different county-equivalent definitions. Export or inspect the unmatched rows and compare their raw identifiers. Do not fix the problem by joining names; repair the identifier format or document the legitimate source difference.

County names match but GEOIDs do not

Names are descriptive labels, not reliable national keys. Normalize the identifier, not the name. If a boundary file provides STATEFP and COUNTYFP, create the full key. If the downloaded table provides GEO.ID, determine whether the final five digits correspond to the county key and preserve the distinction in your metadata.

The Census values appear as text

A text field cannot always be classified as a numeric variable. Inspect the field type and remove formatting characters only when appropriate. For dollar values, do not leave commas or currency symbols embedded in a text field if you need numeric classification. Create a numeric field with the Field Calculator or correct the source CSV, then verify that null and special values are handled explicitly.

Values are missing or unusual

A null may mean that the value is unavailable, not applicable, suppressed, or lost during the import. ACS products may use special estimate or annotation values, and the current ACS documentation provides notes on estimate and annotation values.3 Preserve those distinctions. Never convert every nonnumeric or special code to zero, because zero means something different from β€œnot available.”

Create the county choropleth map

Once the joined estimate has passed verification, select the county layer and open Layer Properties > Symbology. Change the renderer to Graduated. Select the joined B19013_001E estimate as the value field, choose a sequential color ramp, and click Classify or the equivalent control to generate classes. Review the legend labels and apply the style.

For median household income, a single-hue or sequential light-to-dark ramp is usually easier to interpret than a rainbow ramp. Use a readable outline and a restrained fill transparency so county boundaries remain visible without overpowering the values.

Choose a classification method

Method How it divides values Best use Main caution
Natural Breaks (Jenks) Finds groups where values are relatively similar within classes and different between classes Showing clusters in uneven, skewed data Class limits may be harder to compare across maps or years
Quantile Places approximately equal numbers of features in each class Ensuring every color is used and emphasizing rank Class intervals can be uneven and nearby values may be split
Equal Interval Divides the numeric range into equal-width intervals Communicating fixed value bands and simple interpretation Some classes may contain very few counties

Use five or six classes as a starting point, then inspect the result. The β€œbest” method depends on the question. If the reader needs to see relative rank, quantile may be useful. If the reader needs to read clear dollar ranges, equal interval may be easier. Natural Breaks can reveal distributional groupings but should not be presented as objectively meaningful thresholds.9

Do not confuse raw counts with rates or normalized measures

A population-count map answers β€œHow many people live in each county?” It does not answer β€œWhere is population concentrated relative to area?” Large counties may have high counts simply because they cover more land. For density, divide population by area. For a percentage, use a Census numerator and denominator that represent the same population universe and geography. For poverty, map a rate or percentage when the analytical question concerns prevalence rather than the number of people.

The example variable is median household income, which is already a median statistic and should not be divided by population. It remains an estimate with a margin of error, but population normalization would change the meaning of the measure rather than improve it.

Add state boundaries and labels

A national county layer can be visually busy. Add compatible state boundaries above the county fill and below labels, using a thin neutral line for states and a subtler line for counties. Label states selectively; label counties only for a regional map, selected subset, or scale-dependent interactive map. A good map clarifies the pattern rather than displaying every attribute.

Improve cartographic design

Use a projection appropriate to the extent, a sequential color ramp with predictable lightness, and enough contrast for print and web. Keep boundaries subordinate to the thematic fill and use transparency only when it helps hierarchy. Title the legend β€œEstimated median household income, 2024 dollars,” and document the ACS table, year, boundary vintage, API variables if applicable, and retrieval date on the map.

Create a professional QGIS Layout

Open Project > New Print Layout and give the layout a descriptive name. Add the map item, then add a title, legend, scale bar, and north arrow from the Layout toolbar. A national map may not need a dramatic north arrow, but it should have a scale bar or another useful scale indication when appropriate.

Edit the legend so it names the mapped measure and removes technical fields that are not part of the map’s explanation. Add a text box containing the data source, Census program, table, year, geographic level, boundary vintage, projection, author, and date. Make sure the legend’s class labels use consistent currency or numeric formatting.

The layout should state what is mapped, where, for which period, and from which source. Check that no legend or label is clipped.

Export the finished map

From the Layout window, use Layout > Export as Image, Export as PDF, or Export as SVG as appropriate. PNG is convenient for web pages and presentations. PDF is appropriate for print and documents. SVG is useful when a vector editing workflow is required and the receiving application supports the exported symbols and text.

For web output, choose a pixel size that keeps labels legible. For print, set the intended page size and resolution before judging readability. Inspect the exported file for clipped legends, thin lines, unreadable labels, and font substitutions, and retain the project and source data for reproducibility.

Advanced workflow: retrieve the table through the Census API

The Census API request has four practical parts: the dataset endpoint, the variables requested in get, the geography returned by for, and any parent geography supplied through in. For this tutorial:

https://api.census.gov/data/2024/acs/acs5
  ?get=NAME,B19013_001E
  &for=county:*
  &in=state:*
  &key=YOUR_KEY

Remove line breaks when using the URL. NAME returns the geography name and B19013_001E returns the estimate. for=county:* requests all county codes, while in=state:* supplies the state parent for each county. The returned state and county columns together form the five-character county key. The API endpoint and variable metadata are documented by the Census Bureau.3 5

For reuse, preserve the raw columns, create a text GEOID, and save one row per county. Never hard-code a private API key in a public script, and retest the request when changing the year or table.

Reuse the workflow for other Census datasets

The join pattern is reusable: choose a table, select the correct geography, retain the estimate and any uncertainty field, normalize the geographic key, join to matching polygons, verify records, and symbolize the meaningful measure. The same approach can support population, income, poverty, education, housing, age, or employment.

Interpretation changes by variable: population is a total; poverty and educational attainment are often percentages; housing variables have different universes and denominators. Use a data dictionary rather than assuming similarly named variables have the same definition.

Census data limitations to document

The dataset vintage is part of the result. A 2024 ACS estimate is not interchangeable with a 2023 estimate without considering comparability and inflation adjustment. Boundary files also have vintages, and county-equivalent areas can have different names, boundaries, or definitions over time. When comparing years, use the Census Bureau’s comparability guidance and state whether the map compares estimates, boundaries, or both.

ACS estimates have margins of error. Small-area comparisons can be especially uncertain, and visual differences between counties may not be statistically meaningful. A choropleth is an effective communication device, but it should not imply more precision than the data support. Consider showing or discussing MOEs when the map is used for policy, research, or decision-making.

Missing and special values require explicit handling. A null may be unavailable rather than zero. A table may use annotations or special codes to indicate that an estimate is not applicable or not available. Preserve the source information, exclude invalid values from classification when appropriate, and explain exclusions in the map notes.

Common mistakes checklist

Before publishing, check that you did not join on a county name, lose leading zeros, mix Census years without explanation, combine incompatible boundary vintages, select the wrong Census geography, map an MOE as the estimate, treat an ACS estimate as an exact count, use the wrong variable universe, ignore unmatched records, choose a classification that distorts the message, or overload a national map with labels.

Frequently Asked Questions

How do I join Census data to a map in QGIS?

Load county polygons, load the Census table as a non-spatial table, and join the two layers through a shared GEOID. Then verify the joined field and style it with the Graduated renderer.

What is a Census GEOID?

A GEOID is a Census geographic identifier that uniquely identifies a geographic entity. County keys commonly combine a two-digit state code and a three-digit county code.

How do I join a CSV to a Shapefile in QGIS?

Add the Shapefile as a vector layer, add the CSV through Data Source Manager with No geometry, then use the vector layer’s Layer Properties > Joins panel to match the two key fields.

Where can I download U.S. county boundaries?

Use the official Census TIGER/Line Shapefiles page. Select the county geography and record the boundary vintage.

How do I map Census data by county?

Use a county-level table and county polygons with the same identifier, join the data in QGIS, choose a meaningful estimate field, and symbolize it with a graduated choropleth.

How do I use ACS data in QGIS?

Download an ACS table or retrieve it through the API, preserve the estimate and margin-of-error fields, normalize the county key, and join the table to matching polygons.

Why is my Census join not working?

Check leading zeros, text-versus-number field types, the exact identifier format, geography level, duplicate records, boundary vintage, and whether you selected the target and join fields in the correct order.

How do I map Census population by county?

Use a county-level population estimate or decennial count, join it through the county GEOID, and choose whether a raw count, density, or percentage answers the mapping question.

What is the difference between Census and ACS data?

The Decennial Census and ACS are different Census Bureau programs. Decennial products provide official ten-year enumeration results, while ACS products provide survey-based estimates for characteristics such as income, housing, poverty, and education.

How do I create a Census choropleth map?

Join the statistic to the county polygons, open Layer Properties > Symbology, choose Graduated, select a sequential color ramp, classify the numeric field, and build a layout with a source note and legend.

Should GEOIDs be stored as numbers or text?

Store them as text. Numeric import can remove leading zeros and cause a join to fail.

What if some counties have missing Census records?

Inspect unmatched keys and null values. Missing records may result from a bad join, a wrong geography, a source limitation, or an unavailable or inapplicable estimate. Do not replace missing values with zero without evidence.

What is an ACS margin of error?

An ACS margin of error communicates uncertainty around an estimate. It is not a second statistic to map as the primary value, and it should be considered when comparing counties, especially small areas.

Can I use the Census API without downloading data manually?

Yes. The API can return variables by geography, but the request syntax, variable definitions, and key requirements should be checked against the current official documentation.

What are county-equivalent areas?

County-equivalent areas are geographic entities treated as county-level units for Census tabulation, such as parishes, boroughs, independent cities, and certain other areas. Use the boundary and table geography definitions supplied by the Census Bureau rather than assuming every unit is legally called a county.

Should I use the newest county boundaries with older ACS data?

Only when the analytical purpose supports it and you document the choice. Boundary changes can affect comparability, so a compatible vintage is preferable when the project compares geography and statistics directly.

Gabby Jones

Typically replies within a minute

Hello, Welcome to the site. Please click below button for chating me throught WhatsApp.