Approximate H3 hex index
This approximate H3 hex index tool assigns one latitude and longitude coordinate to a stable cell in a global, pointy-topped hexagonal grid.
Run — free
Choose a resolution from 0 to 15 to move from broad regions to increasingly fine cells. The result includes a readable cell identifier, axial grid coordinates, an estimated center, and approximate edge length. It is designed for lightweight grouping, bucketing, caching, and demonstrations where deterministic hex cells matter more than exact interoperability with the official H3 system.
Choose a resolution that matches the grouping task
Resolution controls the scale of the grid. Resolution 0 produces very broad cells, and every higher level halves the cell edge in angular units. That makes the progression predictable: nearby points that separate at one level may share a cell at a coarser level, while increasing the resolution creates more detailed buckets. Start with a lower resolution when summarizing activity across large regions, and increase it for neighborhood-scale grouping or a dense visualization. The returned approximate edge length provides a practical scale hint, but it is not a geodesic guarantee. Because the grid uses a simple equirectangular projection, east-west ground distances shrink toward the poles even though the angular cell geometry stays regular. Test representative coordinates from the actual area you plan to analyze instead of selecting a level solely from the edge estimate. Most importantly, store the resolution beside every index. A cell identifier at one resolution cannot be compared as though it represented the same footprint at another resolution.
Understand how the approximate index is produced
The algorithm places longitude on a horizontal axis and latitude on a vertical axis, overlays a pointy-topped hexagonal lattice, converts the coordinate into fractional axial coordinates, and rounds those coordinates with cube-coordinate rounding. Cube rounding preserves the defining relationship among the three axes of a hex grid and gives each valid input exactly one deterministic cell. The identifier contains an ah3 prefix, the selected resolution, and signed base-36 tokens for the axial q and r coordinates. The same input therefore produces the same output without network access, stored state, randomness, or time-dependent data. The returned center is obtained by converting the rounded axial position back into latitude and longitude. It is useful for labeling, debugging, and placing a representative marker. Boundary points deserve care: a tiny coordinate change near a hex edge can select an adjacent cell, which is expected behavior for any spatial index. Use full-precision source coordinates when stable repeatability around boundaries matters.
Know when to use this grid and when to use official H3
This capability deliberately resembles the workflow of H3 without claiming binary, textual, geometric, or hierarchical compatibility with H3 indexes. It is a compact analytic grid for situations where you need reproducible spatial buckets but cannot or do not want to load a specialized geospatial library. It works well for grouping sample locations, partitioning cache keys, producing coarse density counts, teaching axial hex coordinates, and creating stable test fixtures. The compatible_with_h3 field is always false so downstream code cannot quietly mistake the result for an official H3 address. Use the official H3 library when you must exchange indexes with another H3 application, traverse true H3 parents and children, obtain canonical cell boundaries, handle pentagons, or rely on H3's icosahedral Earth model. Also remember that this approximation uses planar latitude and longitude. Its cells distort in ground area with latitude and do not wrap across the antimeridian as neighboring official global cells would. Treat it as a deterministic bucketing system, not a substitute for geodetic analysis.
What you can do with it
Bucket map events
Assign incoming coordinates to stable spatial groups before counting or summarizing events.
Build location cache keys
Use the deterministic cell string as part of a cache key for approximate location-based results.
Prototype a hex map
Explore resolution choices and axial coordinates before adopting a full geospatial indexing library.
FAQ
Is the returned cell a valid H3 index?
No. It is an approximate, independently defined hex-grid identifier and is explicitly marked as incompatible with H3.
What does the resolution mean?
It selects grid detail from 0 through 15. Each increase halves the angular edge size and creates finer spatial buckets.
Why does cell size vary on the ground?
The calculation uses an equirectangular latitude-longitude plane, so the same longitude span covers less ground toward the poles.
Is the result deterministic?
Yes. Identical latitude, longitude, and resolution values always produce the same result.
What does one request cost?
One API request costs $0.002. The browser implementation can run the same pure calculation locally.
Can I use the center as an exact geographic centroid?
No. It is the planar center of the approximate grid cell and should be treated as a representative coordinate.
For developers — API access
Everything on this page is available programmatically. This section is for teams who want to wire it into their own systems; everyone else can just use the tool above.
API endpoint
Prefer to automate it? One authenticated POST creates the task; the result comes back by webhook or a signed link. The same capability also runs here on the web, by email and from Telegram — and soon from our app too.
Call it from your stack
curl -X POST https://api.kit.forhosting.com/geo/h3-cell-approx \
-H "Authorization: Bearer $KIT_KEY" \
-H "Content-Type: application/json" \
-d '{"lat":37.7749,"lon":-122.4194}'const res = await fetch("https://api.kit.forhosting.com/geo/h3-cell-approx", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.KIT_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
"lat": 37.7749,
"lon": -122.4194
})
});
const { task_id } = await res.json();import os, requests
res = requests.post(
"https://api.kit.forhosting.com/geo/h3-cell-approx",
headers={"Authorization": f"Bearer {os.environ['KIT_KEY']}"},
json={
"lat": 37.7749,
"lon": -122.4194
},
)
task_id = res.json()["task_id"]<?php
$res = file_get_contents("https://api.kit.forhosting.com/geo/h3-cell-approx", false, stream_context_create([
"http" => [
"method" => "POST",
"header" => "Authorization: Bearer " . getenv("KIT_KEY") . "\r\nContent-Type: application/json",
"content" => '{"lat":37.7749,"lon":-122.4194}',
],
]));
$task = json_decode($res, true);body := bytes.NewBufferString(`{"lat":37.7749,"lon":-122.4194}`)
req, _ := http.NewRequest("POST", "https://api.kit.forhosting.com/geo/h3-cell-approx", body)
req.Header.Set("Authorization", "Bearer "+os.Getenv("KIT_KEY"))
req.Header.Set("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)Example request
{
"lat": 37.7749,
"lon": -122.4194
}Example response
{
"task_id": "tsk_a1b2c3d4e5f6a1b2c3d4e5f6",
"type": "geo.h3_cell_approx",
"status": "queued",
"_links": {
"result": "/tasks/tsk_…/result"
}
}The API is asynchronous: the call returns a task_id immediately and the result arrives by webhook. Polling is capped at 1 req/s per task.
Pricing
Published price — no tokens, no invented credits. A failed task is never charged.
Errors
| HTTP | Code | Meaning |
|---|---|---|
401 | unauthorized | Missing or invalid API key. |
402 | insufficient_balance | Your balance doesn't cover the task price. |
404 | unknown_type | That task type doesn't exist. |
429 | rate_limited | Too many requests. Use the webhook instead of polling. |