SpeedMap API — User Documentation
User documentation · v1.3

Speed limits, looked up in milliseconds.

SpeedMap is a lookup API that returns the speed limit for any coordinate across the UK, Northern Ireland and the Republic of Ireland. Two endpoints. One clean JSON response.

GET /Speed POST /Speed/bulk UK · NI · ROI coverage
Getting started

From zero to first lookup

Four steps to your first response. No SDKs to install — SpeedMap is a plain HTTPS/JSON API.

Authenticate with your bearer token

Every request needs a bearer token, issued to authorised customers under your service agreement. Send it on every call.

Authorization: Bearer <your_token>

Call the Speed endpoint

Pass a single latitude/longitude pair to get the speed limit at that point. The production base URL is https://api.iw-slu.com.

curl -X GET "https://api.iw-slu.com/Speed?latitude=51.5074&longitude=-0.1278" \
  -H "Authorization: Bearer <your_token>"

Read the response

Every response — success or error — comes back in the same envelope: a message, an error, and a data payload.

{
  "message": "Success",
  "error": null,
  "data": {
    "id": 184522,
    "latitude": 51.5074,
    "longitude": -0.1278,
    "roadName": "Whitehall",
    "speedLimit": 30, // local unit
    "distance": 4.2, // metres
    "errorMessage": null
  }
}

Move to bulk when you need scale

Looking up more than one point? Use POST /Speed/bulk instead of looping single calls — see the endpoint reference below.

Authentication

Securing your requests

Both endpoints are protected and return nothing without a valid credential. Tokens are issued to authorised customers under the terms of their service agreement.

Authorization: Bearer <your_token>

Send this header on every request. A missing or invalid token returns 401 Unauthorized with a Problem Details error body — see errors & rate limits.

Tokens are issued, rotated, and administered manually by Insight Warehouse — there's no self-serve portal. Expiry and access level are set according to the subscription tier purchased. For a new token, a rotation, or any token issue, contact info@insight-warehouse.co.uk.

API reference

Two endpoints, nothing else

SpeedMap is deliberately small in surface area: a single lookup and a batch lookup. Coverage is currently limited to the UK, Northern Ireland and the Republic of Ireland — coordinates outside these territories will not return a match. Prefer to explore the live schema and try calls directly? Use the interactive Swagger UI.

GET /Speed

Returns the speed limit for a single coordinate.

Query parameters
ParameterTypeRequiredDescription
latitudenumberYesBetween -90 and 90.
longitudenumberYesBetween -180 and 180.
Response fields (data)

The field that matters is speedLimit — that's the match. Everything else on this object (road name, class, IDs, distance) is supporting context rather than the primary result.

FieldTypeDescription
idnumberIdentifier of the nearest road segment found, whether or not it's within the match buffer.
latitude / longitudenumberCoordinates of the matched point.
overtureIdstring (UUID)Overture identifier of the nearest road.
fClassstringFunctional road class — e.g. motorway, primary, residential.
roadNamestringName of the nearest road.
speedLimitnumberThe match. Posted speed limit, in the unit used by that country. 0 when the point is outside the match buffer (see below) — not a real zero limit.
distancenumberDistance from the queried point to the nearest road, in metres.
errorMessagestringPopulated with a human-readable explanation when the point is outside the 20m match buffer; null on a normal match.
Confirmed

A coordinate is matched to the nearest road within a 20 metre buffer. If the nearest road with a speed limit is further than 20m away, the lookup is treated as a non-match: speedLimit comes back as 0 (not a genuine zero limit — read it as "no valid match"), and errorMessage explains why. The rest of the object (roadName, fClass, distance, etc.) still describes the nearest road found, even though it fell outside the buffer — useful context, but don't treat it as a confirmed match.

Example — normal match
// Request
curl 'https://api.iw-slu.com/Speed?latitude=51.5074&longitude=-0.1278' \
  -H 'Authorization: Bearer <your_token>'
// Response — 200 OK
{
  "message": "Success",
  "error": null,
  "data": {
    "id": 184522,
    "longitude": -0.1278,
    "latitude": 51.5074,
    "overtureId": "3fa85f64-…",
    "fClass": "primary",
    "roadName": "Whitehall",
    "speedLimit": 30,
    "distance": 4.2,
    "errorMessage": null
  }
}
Example — outside the 20m buffer
// Request
curl 'https://api.iw-slu.com/Speed?latitude=51.3976&longitude=-0.10382' \
  -H 'Authorization: Bearer <your_token>'
// Response — 200 OK, no valid match
{
  "message": "Success",
  "error": null,
  "data": {
    "id": 3375342,
    "longitude": -0.10382,
    "latitude": 51.3976,
    "overtureId": "9e7c7b2f-…",
    "fClass": "secondary",
    "roadName": "Brigstock Road",
    "speedLimit": 0,
    "distance": 21.01,
    "errorMessage": "The point requested is more than 20m away from any road in our network."
  }
}
POST /Speed/bulk

Returns speed limits for a list of coordinate pairs. Results come back paginated — each result uses the same fields as GET /Speed above.

Request body
FieldTypeDescription
coordinatesarrayList of { latitude, longitude } pairs. Required.
pagenumberPage of results to return.
pageSizenumberResults per page.
searchstringOptional filter applied to results.
Pagination fields (response)
FieldTypeDescription
totalnumberTotal results across all pages.
page / pageSizenumberEchoes the request.
totalPagesnumberTotal number of pages.
hasNextPage / hasPreviousPagebooleanWhether more pages exist either direction.
Example
// Request
curl -X POST 'https://api.iw-slu.com/Speed/bulk' \
  -H 'Authorization: Bearer <your_token>' \
  -H 'Content-Type: application/json' \
  -d '{
    "page": 1,
    "pageSize": 100,
    "coordinates": [
      { "latitude": 51.5074, "longitude": -0.1278 },
      { "latitude": 53.4808, "longitude": -2.2426 }
    ]
  }'
// Response — 200 OK
{
  "message": "Success",
  "error": null,
  "data": [
    { "id": 184522, "roadName": "Whitehall", "speedLimit": 30, … },
    { "id": 207781, "roadName": "Deansgate", "speedLimit": 20, … }
  ],
  "pagination": {
    "total": 2, "page": 1, "pageSize": 100,
    "totalPages": 1, "hasNextPage": false, "hasPreviousPage": false
  }
}
Confirmed

500 coordinate pairs is the maximum accepted per /Speed/bulk request. Send more than that and expect a rejection — split larger jobs into multiple requests (see below).

Building a bulk request properly
Send coordinates as objects, not tuples

Each entry in the coordinates array needs explicit latitude/longitude keys — not [lat, lon] pairs or a flattened list.

"coordinates": [
  { "latitude": 51.5074, "longitude": -0.1278 },
  { "latitude": 53.4808, "longitude": -2.2426 },
  { "latitude": 55.9533, "longitude": -3.1883 }
]
Chunk large jobs into batches of 500

500 pairs is the maximum accepted per request — confirmed as a hard limit, not just a recommendation. For anything bigger, split into batches rather than sending one large array.

// Simple chunking, JS
function chunk(coords, size = 500) {
  const out = [];
  for (let i = 0; i < coords.length; i += size) {
    out.push(coords.slice(i, i + size));
  }
  return out;
}
Always check hasNextPage

The bulk endpoint paginates its response independently of how many coordinates you sent. If you set a pageSize smaller than your batch, or the API applies its own cap, you won't get everything back in one call — loop on pagination.hasNextPage until it's false, incrementing page each time.

Errors & rate limits

What can go wrong, and what it costs

One thing worth knowing up front: you are not charged for requests that return no match or a server error. Every error response — 4xx or 5xx — uses the same Problem Details format (RFC 7807).

StatusMeaning
200Lookup successful. Standard envelope with the data object above.
400Bad request — missing or out-of-range coordinates.
401Missing or invalid bearer token.
429Rate limit exceeded. Applies per account, on both Speed and Speed/bulk — the mechanism is real and was triggered during performance testing. Confirmed: there is currently no fixed requests-per-minute or per-day threshold defined.
500Unexpected server error — not billed.

Problem Details error body

Every error response — 4xx or 5xx — comes back in this shape (RFC 7807).

// Example — 401 Unauthorized
{
  "type": "https://api.iw-slu.com/problems/unauthorized",
  "title": "Unauthorized",
  "status": 401,
  "detail": "Missing or invalid credentials.",
  "instance": "/Speed"
}
FAQ

Common questions

Am I charged for a coordinate that has no matching speed limit?

No. No-match results and server errors are not billed.

What's the largest batch I can send to /Speed/bulk?

500 coordinate pairs — confirmed as the maximum per request. For very large jobs, split into multiple requests.

What happens if I exceed the rate limit?

You'll receive a 429 Too Many Requests response with a Problem Details error body. Rate limiting applies per account, on both endpoints — but there's currently no fixed requests-per-minute or per-day threshold defined.

What does it mean if errorMessage isn't null?

The point is more than 20 metres from the nearest road with a speed limit — confirmed as the match buffer. speedLimit comes back as 0 in this case (not a real zero limit) and errorMessage explains the distance. The call still returns 200 OK; check errorMessage on the data object rather than the HTTP status.

Does the API cover live or temporary speed restrictions (e.g. roadworks)?

No. SpeedMap API returns permanent speed limits.