Offender Map API · v1.0.0

Platform integration

Keys, limits for each of your users, caching, and count-first design.

Livehttps://api.offendersearch.appSandboxhttps://sandbox.offendersearch.app

Map API platform integration: which key

A platform puts the Map API in front of many users of its own. The questions are which key makes the call, how each user is told apart, and how to keep calls few.

Where the call is madeKeyNotes
Your server (server to server)Your secret keyWorks on every route. Send X-End-User if you want per-user limits.
The browser, on your own siteA map browser key, os_map_pk_…, with its list of allowed sitesX-End-User is required on POST /v1/map/route and POST /v1/map/routes.
A phone appYour secret key, on your serverA phone app has no web origin, so it calls through your server.

See Access & keys for creating a browser key and its allowed sites.

X-End-User and limits for each end user

X-End-User is any stable id of yours for the person behind a call, such as a hashed user id. It is never shown to anyone. With it, each of your users has a share of the account, and one heavy user is stopped before the account is.

Limit for each end userDefaultPast it
Different map squares read for pins, each day2,000429 map_end_user_limit
Different routes checked, each day20429 map_end_user_route_limit
Different records opened, each dayOff; set on request429 map_open_limit

Other users of the account are not affected when one user reaches a limit. The account’s own limits are in Daily limits.

Map API caching: ETag, If-None-Match and 304

  • Send If-None-Match. Views and single routes carry an ETag. The same request with it answers 304, with no body, and a 304 is free.
  • Cache-Control: private, max-age=300. Keep an answer for 5 minutes for the user who asked. Do not put it in a shared cache.
  • Marker ids live 24 hours, for your account only. Keep the ids of the answer you drew and reuse them: re-opening the same marker is free.
  • Counts for a fixed place may be kept for 24 hours and shown to everyone. Pins and ids belong to one answer.
cache.js
// Keep each answer with its ETag; ask again with If-None-Match.
const kept = new Map();  // route body -> { etag, answer }

async function callRoute(body) {
  const key = JSON.stringify(body);
  const seen = kept.get(key);
  const headers = { 'X-API-Key': KEY, 'X-End-User': userId, 'Content-Type': 'application/json' };
  if (seen) headers['If-None-Match'] = seen.etag;
  const res = await fetch(API + '/v1/map/route', { method: 'POST', headers, body: key });
  if (res.status === 304) return seen.answer;  // unchanged: free
  if (!res.ok) throw await res.json();
  const answer = await res.json();
  kept.set(key, { etag: res.headers.get('ETag'), answer });
  return answer;
}

Count-first map API design: open on tap

  • Count first. detail=count costs the same as a full answer, returns no pins and no ids, and is never refused by the area limits. It is the right call for a badge or a summary line.
  • Pins when asked. Ask for detail=full when the user wants to see the map.
  • Open on tap. Open a record only when a user taps a pin. Each person in each answer is one record open.
count-first.js
// Count first: one route call with detail=count, no pins, the same price.
const count = await callRoute({ ...route, detail: 'count' });
showBadge(count.total);

// Pins only when the user asks to see them; records only when one is tapped.
showPinsButton.onclick = async () => {
  const full = await callRoute({ ...route, detail: 'full' });
  full.markers.forEach((m) => drawPin(m, () => openRecord(m.id)));
};

Platform integration billing

Everything your users do is billed to your account: map requests and route units at $2.00 per 1,000 map requests, and record opens at your account’s rate, $0.035 by default. Daily limits for the account and for each end user can be set on request. See Billing.

Product overview