Population and areas
Each H3 cell at resolution 9 has a population count and the administrative areas it belongs to. Where a country publishes small-area census data, the cell also has demographics. This page explains where the numbers come from and how to query them per cell, per area and per administrative division.
Sources
The Data API lists two population sources in GET /v1/catalog.
- WorldPop. The population count on every cell comes from
WorldPop, 2020, on a 100 m grid. We add up the grid
squares in each resolution 9 cell. Every inhabited cell in every country has a count.
The
pop_sourcefield on each cell readsworldpop_2020. - Kontur Population. A second population dataset, 2023, on H3 cells at resolution 8. We use it to choose where we sample places for the accuracy comparison. It is not returned by any endpoint on this page. See Accuracy.
Demographics come from each country's official small-area census and small-area income
data. A cell's rates are the population-weighted share of the source areas that overlap
it. A cell with no census coverage has a population count and a null demographics
block. We never write a zero in place of a missing value.
Census coverage differs by country. Within Great Britain it comes from three publishers:
ONS Census 2021 for England and Wales, NRS Census 2022 for Scotland, and NISRA Census
2021 for Northern Ireland. Read census_source on each cell rather than assuming one.
Scotland and Northern Ireland publish no small-area income series, so their cells carry a
demographics block with null income figures. A null means the publisher has no figure
for that cell. It never means zero.
Query population
GET /v1/h3/population returns one row per cell. Send exactly one of these.
bbox=south,west,north,eastreturns every cell whose centre is inside the box. Addmin_population=1to drop empty cells. We recommend this for a viewport heatmap.cells=<csv>returns the cells you list. A single id is a list of one. A cell with no data is left out of the result. There is no404. The ids can be resolution 6 to 9, mixed, with the case, overlap and area rules of Total an area. A coarser cell comes back as one row: its counts are added up from the resolution 9 cells inside it, its rates are population-weighted, and itsbreadcrumbkeeps only the areas every one of those cells sits in.resolutionisnullwhen the ids mix resolutions.division=<id>returns every resolution 9 cell inside an administrative area. The id comes fromGET /v1/divisions. See Administrative areas.
bash
curl -H "Authorization: Bearer $POI_API_KEY" \
"$POI_BASE/v1/h3/population?country=GB&bbox=51.50,-0.15,51.55,-0.10&min_population=1"bash
curl -H "Authorization: Bearer $POI_API_KEY" \
"$POI_BASE/v1/h3/population?country=GB&division=R51800"The second request returns the 52 cells of the City of London. Each cell carries
population, pop_source, a breadcrumb of the areas it belongs to, from country down
to district, and a demographics block where the census covers it.
Every response has the same shape: country, resolution, count, limit,
truncated, generated_at and cells. limit defaults to 100 and the maximum is
10,000. Add fields=slim to cut each row to h3 and population.
Full parameter and response shapes are in the API reference.
Total an area
POST /v1/h3/population/aggregate returns one summary for an area instead of one row per
cell. Population, household and resident counts are added up. Every rate is the
population-weighted mean. Use it for a catchment: a travel time, a drawn shape or a
division all reduce to a set of cells.
Send exactly one area: cells, a list of up to 100,000 ids, or division_id. The ids can be
resolution 6 to 9, mixed in one list, so the output of h3.compact_cells goes in as it is.
A coarser cell counts everything in the resolution 9 cells inside it, up to 1,000,000 of
those per request, and no cell may lie inside another in the list. We recommend
division_id for anything the size of a county. The API resolves the division's cells for
you in one call, instead of paging through GET /v1/divisions/{division_id}/cells.
Add include_per_cell=true to also get one AggregatePerCell row per cell, at the
resolution you sent it.
bash
curl -X POST -H "Authorization: Bearer $POI_API_KEY" \
-H 'content-type: application/json' \
"$POI_BASE/v1/h3/population/aggregate?country=GB" \
-d '{"cells": ["89195da4a93ffff", "89195da4a97ffff"]}'bash
curl -X POST -H "Authorization: Bearer $POI_API_KEY" \
-H 'content-type: application/json' \
"$POI_BASE/v1/h3/population/aggregate?country=GB" \
-d '{"division_id": "R51800"}'The summary carries matched_cells and cells_with_census, so you can see how much of
the area the census covers. Each rate has its denominator in rate_denominators.
Public transport coverage
GET /v1/h3/transit-coverage returns the cells within a 15 minute walk of a public
transport station. Each cell carries its population, its station and line counts and the
modes that serve it. Send a bbox, cells or division, as for population.
bash
curl -H "Authorization: Bearer $POI_API_KEY" \
"$POI_BASE/v1/h3/transit-coverage?country=GB&bbox=51.48,-0.20,51.56,-0.05&res=9"By default only covered cells are returned. Add include_uncovered=true to get the gap as
well. Populated cells that no station reaches come back with covered: false and their
uncovered_pop. At res=6 or res=7, each parent cell carries the summed uncovered
population beneath it. Use this for a country or region view.
limit defaults to 500. With include_uncovered=true it defaults to the maximum of
10,000. At res=7 a box around the whole of Great Britain holds more populated parent
cells than that, so the response has truncated: true. Narrow the box, or use res=6,
which fits in one page.
bash
curl -H "Authorization: Bearer $POI_API_KEY" \
"$POI_BASE/v1/h3/transit-coverage?country=GB&bbox=50.0,-6.0,58.7,1.8&res=7&include_uncovered=true"GET /v1/h3/transit-coverage/divisions returns the share of each division's population
within a 15 minute walk of a station. Set admin_level to choose the tier. The default
sort is best covered first.
bash
curl -H "Authorization: Bearer $POI_API_KEY" \
"$POI_BASE/v1/h3/transit-coverage/divisions?country=GB&admin_level=6&limit=3"The stations come from the public transport dataset. Full parameter and response shapes are in the API reference.
Administrative areas
A division is an administrative area from OpenStreetMap, such as a nation, a county or a
district. Divisions form a tree. Every cell's breadcrumb lists the divisions it belongs
to, from the country down.
| Endpoint | What to use it for |
|---|---|
GET /v1/divisions | The divisions in a country. Filter by admin_level. Sort by population, cells or name. |
GET /v1/divisions/{division_id} | One division, with its parent, its ancestors, its children and its population. |
GET /v1/divisions/{division_id}/children | The divisions one tier down. |
GET /v1/divisions/{division_id}/cells | The cells holding data, at res 9, 7 or 6, paged. ?compact=true: the whole area in one go. |
admin_level is the OpenStreetMap level, and its meaning differs by country. In Great
Britain, level 4 is the four nations and level 6 is counties and council areas.
bash
curl -H "Authorization: Bearer $POI_API_KEY" "$POI_BASE/v1/divisions?country=GB&admin_level=4"Pass a division's cells to any endpoint that takes cells: population, public transport
coverage, places or green space.
For a large division, ask for res=7 or res=6, or send division_id to an aggregate
endpoint instead.
bash
DIVISION=R51800
curl -H "Authorization: Bearer $POI_API_KEY" \
"$POI_BASE/v1/divisions/$DIVISION/cells?country=GB&res=7"To go the other way, from cells to their divisions, use POST /v1/h3/divisions. Send
cells at resolution 6 to 10 and an optional admin_level. The default is the finest
level any of the cell's indexed resolution 9 cells has, so every division listed for a cell sits at that one level. A cell coarser than resolution 9 can cross a boundary, so each row
lists every division it touches with the share of its indexed resolution 9 cells inside, and
division names the largest. A cell with no indexed resolution 9 cell, such as empty
countryside, comes back with division: null.
bash
curl -X POST -H "Authorization: Bearer $POI_API_KEY" -H "Content-Type: application/json" \
"$POI_BASE/v1/h3/divisions?country=GB" \
-d '{"cells": ["89195da4a93ffff", "87195da4affffff"], "admin_level": 8}'You can browse the tree on a map in the playground's Areas tool. Pick a division and it fills its cells. Full parameter and response shapes are in the API reference.