Index points to H3 cells
Index points to H3 cells. Send "points" as [longitude, latitude] positions in EPSG:4326 and get each one's containing H3 cell at one or several resolutions, optionally with the cell's centre and its closed boundary ring. Use it to bucket points into a fixed hexagonal grid so datasets that share no keys can be joined, aggregated or deduplicated on cell id. This route indexes positions and nothing else. It does not accept polygons: a body carrying "geojson" is refused rather than served, and because "points" is required here that refusal costs you nothing -- it happens before a price is quoted. To cover a Polygon or MultiPolygon with the cells that tile it, POST /v1/h3/polyfill instead. That is a separate route at a higher price because it is a materially more expensive job: measured on this runtime, indexing one point takes about 0.003 ms and returns 168 bytes, while the cheapest possible polyfill takes about 1.5 ms -- the cell count has to be estimated before any cell is generated -- and a polyfill at its cell cap is about 200 ms and 1.8 MB. This route is priced with POST /v1/transform, which does the same class of per-position work, and below POST /v1/h3/polyfill. Every call needs exactly one of "resolution" or "resolutions". Input is EPSG:4326, longitude then latitude; out-of-range values are refused rather than normalised, because H3 will happily index a latitude of 950 and answer with a real, wrong cell. Limits: 10000 points, 4 resolutions per call, and a 1 MiB request body.
Provider
api.geoprimitives.dev · discovered, not yet claimed by its owner
Payment (x402 accepts[])
[
{
"scheme": "exact",
"network": "eip155:8453",
"payTo": "0x76A51b5Dd3729950B3b60c17f19252d221968FCe",
"asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
"amount": "5000",
"maxTimeoutSeconds": 300
}
]Output schema
{
"bazaar": {
"info": {
"input": {
"body": {
"points": [
[
-122.4184,
37.7752
]
],
"resolution": 9
},
"bodyType": "json",
"method": "POST",
"type": "http"
},
"output": {
"example": {
"cellCount": 1,
"mode": "index",
"notes": [],
"pointCount": 1,
"resolutions": [
9
],
"results": [
{
"cells": [
{
"cell": "8928308280fffff",
"resolution": 9
}
],
"point": [
-122.4184,
37.7752
]
}
]
},
"type": "json"
}
},
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"input": {
"additionalProperties": false,
"properties": {
"body": {
"additionalProperties": false,
"oneOf": [
{
"required": [
"resolution"
]
},
{
"required": [
"resolutions"
]
}
],
"properties": {
"includeBoundary": {
"description": "Include each cell's centre and its closed boundary ring, in GeoJSON [longitude, latitude] order, ready to paste into a Polygon. Defaults to false. It multiplies the response by about eight, so 10000 points at four resolutions goes from 2.1 MB to 15.8 MB.",
"type": "boolean"
},
"points": {
"description": "Positions to index, each [longitude, latitude] in EPSG:4326. At least 1, at most 10000. Required: this route takes nothing else. To cover a polygon with cells, POST /v1/h3/polyfill.",
"items": {
"description": "[longitude, latitude] or [longitude, latitude, elevation]. GeoJSON order, longitude first. Out-of-range values are rejected rather than normalised: H3 will happily index a latitude of 950 and answer with a real, wrong cell.",
"items": {
"type": "number"
},
"maxItems": 3,
"minItems": 2,
"prefixItems": [
{
"maximum": 180,
"minimum": -180,
"type": "number"
},
{
"maximum": 90,
"minimum": -90,
"type": "number"
}
],
"type": "array"
},
"maxItems": 10000,
"minItems": 1,
"type": "array"
},
"resolution": {
"description": "H3 resolution, 0 (about 4.4 million square kilometres per cell) to 15 (about 0.9 square metres). Send this or \"resolutions\", not both.",
"maximum": 15,
"minimum": 0,
"type": "integer"
},
"resolutions": {
"description": "Several H3 resolutions at once, 0 to 15, distinct. At most 4, because each one multiplies the response.",
"items": {
"maximum": 15,
"minimum": 0,
"type": "integer"
},
"maxItems": 4,
"type": "array"
}
},
"required": [
"points"
],
"type": "object"
},
"bodyType": {
"enum": [
"json",
"form-data",
"text"
],
"type": "string"
},
"method": {
"enum": [
"POST"
],
"type": "string"
},
"type": {
"const": "http",
"type": "string"
}
},
"required": [
"type",
"method",
"bodyType",
"body"
],
"type": "object"
},
"output": {
"properties": {
"example": {
"type": "object"
},
"type": {
"type": "string"
}
},
"required": [
"type"
],
"type": "object"
}
},
"required": [
"input"
],
"type": "object"
}
}
}Use it
curl
curl "https://api.geoprimitives.dev/v1/h3/index" # -> 402 Payment Required, accepts[] lists how to pay # retry with a PAYMENT-SIGNATURE (or PAYMENT header) once paid
JavaScript
const res = await fetch("https://api.geoprimitives.dev/v1/h3/index");
if (res.status === 402) {
const { accepts } = await res.json();
// pay one of accepts[] via an x402 client, then retry with the payment header
}Python
import httpx
res = httpx.get("https://api.geoprimitives.dev/v1/h3/index")
if res.status_code == 402:
accepts = res.json()["accepts"]
# pay one of accepts[] via an x402 client, then retry with the payment headerMachine-readable
Everything on this page is also available as clean JSON at /resources/6755.json, and this resource appears in /discovery/resources and /discovery/search.