# Bus stop notices

> Check every school bus stop for registered addresses within 1,000 feet in one batch, open records for the notice letters, and word the notice correctly.

- **HTML:** https://offendersearch.app/docs/map/bus-stop-notices
- **Base URL (live):** https://api.offendersearch.app — live keys (`os_live_…`)
- **Base URL (sandbox):** https://sandbox.offendersearch.app — test keys (`os_test_…`), never billed; see https://offendersearch.app/docs/map/sandbox.md
- **Authentication:** `X-API-Key` request header
- **Map API reference as markdown:** https://offendersearch.app/docs/map.md

## Bus stop notice check: what it answers

A district that tells families about registered addresses near their child’s bus stop needs, for each stop, the people registered within a set distance. One batch call answers every route of a run, stop by stop; a record is opened only for the people a letter will name.

> This check supports a district’s notice process; it is not a legal determination. Distances are from the geocoded registered address to the stop point. It shows registered addresses, not where anyone is.

## Bus stop radius: 1,000 feet on the bus profile

- `"profile": "bus"`: routes up to 60 miles with up to 150 stops.
- `"stopRadiusFeet": 1000`, the bus default. Use the distance your policy names, from 100 to 2,640 feet.
- `"measure": "stops"` counts the stops only. Use `"both"` to count a corridor along the line as well.
- Use your own stop numbers as stop `id`s and your route number as `externalId`: the answer comes back in your own terms.
- Send the route line from your routing system as a `polyline` or `points`, or leave it out: with stops only, each stop is answered on its own.

## Check every bus route in one batch

POST /v1/map/routes

```bash
curl -X POST https://api.offendersearch.app/v1/map/routes \
  -H "X-API-Key: $OFFENDERSEARCH_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "routes": [
      {
        "externalId": "bus-12-AM",
        "profile": "bus",
        "polyline": "o|{rFnuqyN_v@oi@o}@g^",
        "stops": [
          {"id": "12-01", "lng": -82.9988, "lat": 39.9612},
          {"id": "12-02", "lng": -82.9920, "lat": 39.9700},
          {"id": "12-03", "lng": -82.9870, "lat": 39.9800}
        ],
        "measure": "stops",
        "stopRadiusFeet": 1000,
        "parcelAllowanceFeet": 50
      }
    ],
    "sandbox": true
  }'
```

- Up to 50 routes a call. A district with more sends several calls.
- All or nothing: if one route is refused, the call says which in `routeIndex`, and nothing is billed. Fix it and send the call again.
- `uniquePeople` counts each person once across the whole call, however many stops they are near.

## Parcel allowance for a bus stop radius

A registered address is one geocoded point, usually inside the property rather than on its edge. When a policy measures from the property line, add an allowance with `parcelAllowanceFeet`, 0 to 500 feet. It is added to every stop radius, and the answer states it in `parcelAllowanceFeet`, so a letter can say exactly what was measured.

## Bus stop notice letters: opening records

Each stop lists the pins within its reach in `markerIds`. Open each one through `GET /v1/map/records/{markerId}` for the details a letter needs. Each person is one record open, $0.035 by default, and a person near two stops in the same answer is opened once.

letters.py

```python
# For each stop with people in reach, open each record once for the letter.
opened = {}  # marker id -> record, so a person near two stops is opened once
for answer in body["routes"]:
    for stop in answer["stops"]:
        for marker_id in stop.get("markerIds", []):
            if marker_id.startswith("mg_"):
                continue  # several people at one address: page GET /v1/map/groups/{id}
            if marker_id not in opened:
                r = requests.get(
                    f"https://api.offendersearch.app/v1/map/records/{marker_id}",
                    headers={"X-API-Key": os.environ["OFFENDERSEARCH_KEY"]},
                    timeout=30,
                )
                r.raise_for_status()
                opened[marker_id] = r.json()["record"]
            write_letter_line(answer["externalId"], stop["stopId"], opened[marker_id])
```

- An id starting `mg_` is several people registered at one address. List them with the groups route, then open each.
- Marker ids work for 24 hours. Keep the `recordRef` of each opened record for your file, not the marker id.
- People under `approximate` are mapped only to a ZIP code, city or county, so their distance to a stop is not known. Say so in the notice rather than leaving them out silently.

## Wording for a bus stop notice

- “This check supports the district’s notice process. It is not a legal determination.”
- “Distances are measured from the geocoded registered address to the bus stop point.”
- “It shows registered addresses, not where anyone is.”
- Show the `routeNotice` sentence from the answer next to the counts.

## Bus route check cost

A 17.42-mile bus route with a 0.1-mile corridor and 35 stops at 1,000 feet is 18 + 4 = 22 units, $0.044. Checking 300 such routes in each of 3 terms is 19,800 units, $39.60 a year in map requests.

Records opened for letters are billed at the account’s record-open rate, $0.035 by default. See [Billing](https://offendersearch.app/docs/map/billing.md).

---

## Related

- Previous: [Filters & lists](https://offendersearch.app/docs/map/filters-and-lists.md)
- Next: [Walking routes](https://offendersearch.app/docs/map/walking-routes.md)
- Index: [Map API reference](https://offendersearch.app/docs/map.md)
