---
title: "Up to 50 routes in one call, answered all or nothing"
method: POST
path: "/v1/map/routes"
tags: ["map"]
---

# Up to 50 routes in one call, answered all or nothing

`POST /v1/map/routes`

## Request

- Base URL: `https://api.offendersearch.app`
- URL: `https://api.offendersearch.app/v1/map/routes`
- Auth: API key in header `X-API-Key`

Up to 50 routes, each shaped like a `POST /v1/map/route` body, answered in
one call. Every route answers, or none does: one invalid route is a `422`
naming its `routeIndex`, one route over 300 pins (with `detail=full`) is
`422 route_too_many_people` naming its `routeIndex`, and a failed read is a
`503`. Nothing is billed unless the whole batch answers.

All the markers share one response, so a person on three routes opened from
this answer is one record open. A batch answers in JSON only.

**Billing.** The sum of the routes' units, each one map request.

## Headers

- `X-End-User` string

## Request body

- MapRoutesRequest
  - `routes` MapRouteRequest[], required: 1 to 50 routes, each a `POST /v1/map/route` body (JSON format only).
    - `path` union: The line: a GeoJSON LineString (or a Feature holding one), or a list of `[lng, lat]` points. Up to 50,000 points; a longer line is simplified to within about 5 metres when it has more than 2,000.
      - object
      - array[]
        - number[]
    - `points` array[]: The line as `[[lng, lat], …]`: the same list `POST /v1/map/area` takes.
      - number[]
    - `polyline` string: The line as an encoded polyline: Google Directions `overview_polyline.points`, or a Mapbox, OSRM or Valhalla `polyline6` geometry.
    - `polylinePrecision` 5 | 6: 5 for Google, 6 for Mapbox, OSRM and Valhalla.
    - `stops` MapRouteStopInput[]: The stops, in route order: `{id, lng, lat}` objects, or `[lng, lat]` pairs. Walk: up to 50. Bus: up to 150.
      - `id` string: Your id for the stop, unique in the route. Left out: `s1`, `s2`, …
      - `lng` number, required
      - `lat` number, required
      - `label` string: Accepted and never echoed: answers name stops by `id` only.
    - `profile` 'walk' | 'bus': Sets the limits and the default stop radius: walk 15 miles, 50 stops, 500 feet; bus 60 miles, 150 stops, 1,000 feet.
    - `measure` 'corridor' | 'stops' | 'both': What is counted. Left out: `both` when `stopRadiusFeet` is sent (or `stops` when there is no line), otherwise `corridor`.
    - `corridorMiles` number: The distance either side of the line, in miles.
    - `stopRadiusFeet` number: The radius around each stop, in feet. Left out: 500 for walk, 1,000 for bus.
    - `parcelAllowanceFeet` number: Feet added to the stop radius, for the distance between a geocoded address point and its property line.
    - `detail` 'full' | 'count': `full` for counts and pins, `count` for counts only. The same price.
    - `format` 'json' | 'geojson': `geojson` answers a single route as a FeatureCollection.
    - `housing` union: As on `GET /v1/map/viewport`. A comma-separated string or a list.
      - string
      - string[]
    - `precision` 'street' | 'any': `street` leaves out the people counted under `approximate`.
    - `sandbox` boolean: `true` answers in test mode.
    - `externalId` string: Your own id for the route, echoed in the answer.
  - `sandbox` boolean: `true` answers every route in test mode.

## Response `200`

Every route's answer, in the order sent.

- MapRoutesResponse
  - `shape` 'routes'
  - `complete` boolean: Always `true`: the batch is answered whole or refused.
  - `datasetVersion` string, date
  - `units` integer: The sum of the routes' units.
  - `uniquePeople` integer: Distinct people across every route.
  - `routes` MapRouteResponse[]
    - `routeIndex` integer: In a `POST /v1/map/routes` answer only: the route's position in the request, from 0.
    - `shape` 'route'
    - `profile` 'walk' | 'bus'
    - `measure` 'corridor' | 'stops' | 'both'
    - `detail` 'full' | 'count'
    - `datasetVersion` string, date
    - `complete` boolean: Always `true`: a route is answered whole or refused.
    - `lengthMiles` number
    - `corridorMiles` number, nullable: `null` when the corridor is not measured.
    - `stopRadiusFeet` number, nullable: `null` when stops are not measured.
    - `parcelAllowanceFeet` number, nullable: `null` when stops are not measured.
    - `units` integer: Route units: the map requests this route is billed as.
    - `tilesRead` integer: Zoom-14 map tiles the route touches.
    - `total` integer: Distinct people with a street address in the corridor or a stop's reach.
    - `corridorTotal` integer, nullable: People in the corridor. `null` when it is not measured.
    - `stopsTotal` integer, nullable: People within reach of any stop. `null` when stops are not measured.
    - `breakdown` MapBreakdown: Of a count: how many have an exact street address, and housing status where the registry states it.
      - `street` integer: People with an exact street address.
      - `housed` integer
      - `homeless` integer
      - `incarcerated` integer
    - `legs` MapRouteLeg[]
      - `leg` integer: From 1.
      - `fromStop` string, nullable
      - `toStop` string, nullable
      - `lengthMiles` number
      - `count` integer: People in the corridor whose nearest point of the line is on this leg.
    - `stops` MapRouteStop[]
      - `stopId` string
      - `alongMiles` number, nullable: Miles from the start of the line. `null` with no line.
      - `count` integer, nullable: People within the stop's reach. `null` when stops are not measured.
      - `nearestFeet` integer, nullable: Feet to the closest of them. `null` when there is none.
      - `markerIds` string[]: With `detail=full` when stops are measured: the marker and group ids within reach.
    - `markers` MapRouteMarker[]
      - `id` string: Marker id (`mk_…`). Opens with `GET /v1/map/records/{markerId}`. Works for 24 hours.
      - `lat` number
      - `lng` number
      - `precision` 'street'
      - `addressType` 'home' | 'work' | 'school' | 'other'
      - `distanceMiles` number: Straight-line miles from the address to the line.
      - `leg` integer: The leg the nearest point of the line is on, from 1.
      - `alongMiles` number: Miles from the start of the line to its nearest point.
      - `nearestStop` string: The `id` of the closest stop.
      - `stopDistanceFeet` integer: Straight-line feet to that stop.
    - `groups` MapRouteGroup[]
      - `groupId` string: Group id (`mg_…`) for `GET /v1/map/groups/{groupId}`.
      - `lat` number
      - `lng` number
      - `precision` 'street'
      - `count` integer: People at this address.
      - `distanceMiles` number
      - `leg` integer
      - `alongMiles` number
      - `nearestStop` string
      - `stopDistanceFeet` integer
    - `approximate` object: People mapped only to a ZIP code, city or county. Never in `total`.
      - `count` integer
      - `areas` MapRouteArea[]
        - `groupId` string: Group id (`mg_…`) for `GET /v1/map/groups/{groupId}`.
        - `lat` number
        - `lng` number
        - `precision` 'zip' | 'city' | 'county'
        - `place` string: The area's name.
        - `count` integer
    - `unplaced` MapUnplaced: People in the states this view touches who have no mappable address.
      - `count` integer
      - `states` string[]
    - `externalId` string: Your `externalId`, when sent.
    - `sandbox` boolean: Present and `true` on a test-mode answer.
    - `notice` string
    - `routeNotice` string: How distances and counts are measured, as a sentence to show.
  - `sandbox` boolean: Present and `true` on a test-mode answer.
  - `notice` string

## Other responses

- `401`: Missing or invalid credential.
- `403`: `map_not_enabled` — the Map API is off for this account. `map_paused` — paused by us. `origin_not_allowed` — a browser key used from a site not on its list.
- `422`: The batch was refused, with nothing charged. `route_batch_too_large` (more than 50 routes), `invalid_route` (no routes), `invalid_format` (a batch answers in JSON), or any `POST /v1/map/route` code, with `routeIndex` naming the route.
- `429`: `map_sweep_limit` — the account's daily area budget is used; clusters still work. `map_end_user_limit` — one end user's daily area budget is used. `map_end_user_route_limit` — one end user has checked their daily number of different routes. `map_open_limit` — the daily limit on record opens. `map_busy` — too many map requests at once (`Retry-After: 1`). `overloaded` — the service is at capacity (`Retry-After` set).
- `503`: `map_index_not_ready`, `map_unavailable` or `map_not_configured`. Retry.

## Changes

- **2026-10-18** `ba0f53153e6e`: 1 info
  - endpoint added

[Operation history](https://skmtc.dev/offendersearch/apis/offendersearch-api/changes/v1/map/routes/post.md)

---

[API](https://skmtc.dev/offendersearch/apis/offendersearch-api.md) · [All operations](https://skmtc.dev/offendersearch/apis/offendersearch-api/llms.txt) · [OpenAPI document](https://skmtc.dev/offendersearch/apis/offendersearch-api/revisions/9b2562318a07?raw)
