Offendersearch
Map API Reference

The viewport request

Every parameter, how zoom picks the detail level, and the caching headers.

Base URL https://api.offendersearch.app

The request

GET /v1/map/viewport answers one map view. Call it when the map first loads and each time it stops moving. One call is one map request.

GET /v1/map/viewport
curl -G https://api.offendersearch.app/v1/map/viewport \
  -H "X-API-Key: $OFFENDERSEARCH_KEY" \
  -d bbox=-83.2,39.8,-82.8,40.1 \
  -d zoom=10

Parameters

ParameterMeaning
bboxwest,south,east,north in degrees. Any size. A box that crosses the date line has west greater than east.
center + radiusMilesInstead of bbox: lat,lng and a distance from 0.1 to 100 miles. See Near an address.
zoomOptional. 0 to 22; a fraction is rounded down. Send the map’s current zoom.
formatjson (the default) or geojson.
detailfull (the default) or count for numbers only.
housinghoused, homeless, incarcerated. Separate several with commas.
precisionstreet returns only people with an exact street address. any (the default) returns everyone.
sandboxtrue puts the request in test mode.

Send bbox, or center with radiusMiles. Not both.

The optional X-End-User header carries any id you choose for the person using the map. It is read on this route and on POST /v1/map/area. See Limits & errors.

How the zoom picks the detail level

Effective zoommodeReturned
3–12clustersA count for each grid cell, with its extent and a breakdown.
13–14pinsOne pin for each address: a position, with no name.
15+markersPins with a name label and basic fields.

The effective zoom is worked out like this:

  1. It starts as the zoom you sent. If you sent none, it is the zoom at which the box is about four map tiles wide.
  2. If the box covers more than 64 map tiles at that zoom, the zoom is lowered until it fits.
  3. It is never lower than 3.

The response states the zoom that was used, in zoom, and the level, in mode. Draw from mode, not from your own zoom.

Any size of area

There is no largest box. A view of a whole country is answered as clusters, in one map request, at the same price as a view of one street. A wide box is simply answered at a lower zoom.

The 300-pin rule

A response holds at most 300 pins. A view at zoom 13 or closer with more than that is answered as clusters on a finer grid, with mode: "clusters". Each cluster’s expansionZoom says how far to zoom for it to come apart.

The response

200 OK
{
  "mode": "clusters",
  "zoom": 10,
  "bbox": [-83.2, 39.8, -82.8, 40.1],
  "datasetVersion": "2026-10-12",
  "total": 508,
  "breakdown": {
    "street": 508, "housed": 247, "homeless": 96, "incarcerated": 0
  },
  "clusters": [
    {
      "id": "c10_1103_1551",
      "lat": 39.9612, "lng": -82.9988,
      "count": 412,
      "bbox": [-83.06, 39.91, -82.97, 39.98],
      "expansionZoom": 11,
      "breakdown": {
        "street": 412, "housed": 201, "homeless": 77, "incarcerated": 0
      }
    }
  ],
  "markers": [],
  "areas": [],
  "unplaced": {"count": 96, "states": ["OH"]},
  "notice": "A pin shows a registered address, not a person's current location.",
  "sandbox": true
}
FieldMeaning
modeclusters, pins, markers, or count when detail=count.
zoomThe effective zoom the view was answered at.
bboxThe box that was answered.
datasetVersionThe version of the map data that answered.
totalPeople in the view. In a clusters answer: in the clusters returned. With a radius: people inside the circle.
breakdownOf total: how many have an exact street address, and the housing status where the registry states it.
clusters, markers, areasWhat to draw. See Clusters & markers.
unplacedPeople in the states this view touches who have no mappable address.
noticeA sentence to show on the map.
center, radiusMiles, withinRadiusOnly when the request used a radius. withinRadius is exact.

Markers are sorted by distance from the centre of the view.

Caching

Every answer carries Cache-Control: private, max-age=300 and an ETag.

  • Send the ETag back in If-None-Match. An unchanged view answers 304, with no body. A 304 is not billed.
  • The ETag changes when the map data changes, and at least every 12 hours, so marker ids are never kept past their life.
  • Keep the body with its ETag. On a 304, draw the body you kept.
  • The answer is private to your account. Do not put it in a shared cache.

Errors

A bad parameter answers 422 with a code that names it: invalid_bbox, invalid_center, invalid_radius, invalid_zoom, invalid_filter, invalid_format, invalid_detail. All codes are in Limits & errors.

Product overview