map

What to draw for one map view — clusters, pins or named pins

You draw the map; this returns what to draw for one view. Send the view as bbox, or as center with radiusMiles — one or the other, not both.

The detail returned depends on the effective zoom. That is the zoom you send, or — when you send none — the zoom at which the box is about four tiles wide. If the box covers more than 64 tiles at that zoom, the zoom is lowered until it fits, never below 3. The response states the zoom used.

Effective zoommodeReturned
3–12clustersA count per grid cell
13–14pinsOne pin per address: position only, no name
15+markersPins with a name label and basic fields

A view at zoom 13 or closer that holds more than 300 pins is answered as clusters on a finer grid (mode: "clusters"). With detail=count the mode is count.

Billing. One map request per answer, whatever the view holds. Clusters and pins are never charged per person. A 304, test mode and any error response are free.

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 and is not billed. The ETag changes when the data changes, and at least every 12 hours.

Test mode. sandbox=true returns made-up people (every label starts with "Sample") around eight US cities, in the real response shape. Group and marker ids from a test-mode response open test-mode data. It needs no plan, is never billed and counts against no budget. The response carries "sandbox": true.

Auth. A secret API key from any server, a dashboard session token, or a map browser key (os_map_pk_…) sent from a site on that key's list.

get/v1/map/viewport

Request

  • Base URL: https://api.offendersearch.app
  • URL: https://api.offendersearch.app/v1/map/viewport
  • Auth: one of:
    • API key in header X-API-Key
    • HTTP bearer

Query parameters

bboxstring

west,south,east,north in degrees. Any size. A box crossing the date line has west greater than east.

centerstring

Instead of bbox: lat,lng in degrees. Goes together with radiusMiles.

radiusMilesnumber

With center: the radius in miles, 0.1 to 100.

zoomnumber

Optional. The map's current zoom, 0 to 22. Fractions are floored.

format'json' | 'geojson'

json (default) or geojson.

detail'full' | 'count'

full (default) or count (numbers only).

housingstring

Keep only people with this housing status. One or more of housed, homeless, incarcerated, comma-separated.

precision'street'

street keeps only people with an exact street address.

sandboxboolean

true answers in test mode.

Headers

X-End-Userstring

Optional. Any id you use for the end user behind this request. The daily area budget is then also counted per end user (2,000 distinct areas a day), so one heavy user is limited before the account is.

If-None-Matchstring

The ETag from an earlier answer for the same view. An unchanged view answers 304 with no body and is not billed.

Response

The answer for this view.

mode'clusters' | 'pins' | 'markers' | 'count'

clusters at zoom 3–12, or for a closer view holding more than 300 pins. pins at zoom 13–14. markers at zoom 15+. count with detail=count.

zoominteger

The effective zoom this view was answered at.

bboxnumber[]

[west, south, east, north] of the view answered.

datasetVersionstring date

The version of the data that answered.

totalinteger

People in the view (in clusters mode: in the clusters returned). In radius mode: people inside the circle.

centernumber[]

Radius mode only: [lat, lng].

radiusMilesnumber

Radius mode only.

withinRadiusinteger

Radius mode only: people inside the circle. Exact.

shape'polygon' | 'route'

POST /v1/map/area only.

totalIsEstimateboolean

POST /v1/map/area in clusters mode only: true.

sandboxboolean

Present and true on a test-mode answer.

noticestring

Example response

{
  "mode": "clusters",
  "zoom": 9,
  "bbox": [
    -83.5,
    39.6,
    -82.4,
    40.3
  ],
  "datasetVersion": "2026-10-12",
  "total": 1840,
  "breakdown": {
    "street": 1502,
    "housed": 310,
    "homeless": 96,
    "incarcerated": 41
  },
  "clusters": [
    {
      "id": "c9_551_775",
      "lat": 39.9612,
      "lng": -82.9988,
      "count": 412,
      "bbox": [
        -83.06,
        39.91,
        -82.97,
        39.98
      ],
      "expansionZoom": 11,
      "breakdown": {
        "street": 350,
        "housed": 80,
        "homeless": 22,
        "incarcerated": 9
      }
    }
  ],
  "markers": [],
  "areas": [],
  "unplaced": {
    "count": 96,
    "states": [
      "OH"
    ]
  },
  "notice": "A pin shows a registered address, not a person's current location."
}

Changes

No recorded changes to this operation across all 1 revision of this API.