Use with an AI agent
A coding agent can write your integration, as long as it starts from the facts instead of guessing them.
- Paste the brief first. Put the block below into your agent before you ask it for any code.
- Tell it what you're building. The brief describes the API, not your product.
- Give it one real coordinate pair from your own data, longitude first. It will write code that fits your data and notice right away if a location is outside coverage.
- Ask it to handle the errors, not just the happy path. The brief lists every rejection; say "handle these" explicitly.
The integration brief
Endpoints, limits and error shapes, written for an agent. It is also published at www.waymatrix.io/llms.txt.
# WayMatrix
> The routing API your client library already knows how to call.
WayMatrix — integration brief for a coding agent.
Everything below is generated from the API's own description. Follow it exactly;
do not infer endpoints, parameters or behaviour that is not listed here.
BASE URL
--------
https://api.waymatrix.io
AUTHENTICATION
--------------
Send the API key raw in the "Authorization" header.
There is NO "Bearer " prefix. This is deliberate — it matches the
openrouteservice client libraries, so their code works unchanged.
A "Bearer " prefix is tolerated if you already send one.
On GET requests ?api_key=<key> also works, because openrouteservice
accepts it and this API is a drop-in for it. PREFER THE HEADER. A key
in a URL lands in server access logs — ours, and any proxy between you
and us — which is a property of URLs rather than a setting. If you have
used the query form with a real key, treat that key as disclosed.
ENDPOINTS — this is all of them
-------------------------------
POST /v2/matrix/driving-car Travel-time and distance matrix
POST /v2/matrix/driving-car/json Travel-time and distance matrix (explicit JSON form)
GET /v2/directions/driving-car Route between two points
POST /v2/directions/driving-car Route with full options
POST /v2/directions/driving-car/json Route, JSON form
POST /v2/directions/driving-car/geojson Route, GeoJSON form
POST /v2/directions/driving-car/gpx Route, GPX form
POST /v2/isochrones/driving-car Reachability polygons
POST /v2/isochrones/driving-car/geojson Reachability polygons (explicit GeoJSON form)
GET /v2/health Liveness
HEAD /v2/health Liveness, headers only
GET /v2/status Engine and graph provenance
HEAD /v2/status Engine and graph provenance, headers only
CAUTION, python + gpx: The Python client calls response.json() unconditionally in every released version, so a GPX body raises "HTTP Error: 200". The fix exists upstream but has never been released. It fails the same way against the API we are compatible with, so it is not a difference between us — but a reader would reasonably conclude it was our fault. Show GPX with curl or plain HTTP instead.
WHAT THIS API DOES NOT HAVE — do not attempt these
--------------------------------------------------
- No geocoding and no place search. Send longitude and latitude.
- No live traffic. Durations come from the road network, not from current conditions.
- Nothing outside North America, and no profile except driving.
- No route optimization. Sequencing stops is a different problem and we do not solve it.
LIMITS
------
The largest request THE SERVICE accepts is 90,000 elements
(a 300 x 300 square matrix). YOUR tier's cap may be lower, and the
per-tier figure below is the one that applies to your key. Split larger
jobs into several requests; chunking costs exactly the same.
Free 5 requests/min, 50,000 elements/month, 2,500 per request (50 x 50)
Starter 60 requests/min, 1,000,000 elements/month, 90,000 per request (300 x 300)
Growth 120 requests/min, 5,000,000 elements/month, 90,000 per request (300 x 300)
Scale 180 requests/min, 20,000,000 elements/month, 90,000 per request (300 x 300)
ROUTING AROUND AN AREA, OR WITH LIVE WEIGHTS, CAPS THE ROUTE AT 100 km.
A plain route runs far further — Chicago to Denver, 1,613 km, is fine.
Add avoid_polygons and the same route is refused with a 400 and code
2004. dynamic_weights has the identical cap. Do not assume one is safe
because you read about the other.
DO NOT BRANCH ON 2004 ALONE. That code is REUSED: it is also what you
get for a route with more than 50 waypoints. Only the message separates
them. Branch on the code and you will tell someone to shorten a route
when the fix was to send fewer waypoints — wrong advice, from code that
reviews as correct. Code 2003 is a third thing again: the POLYGON is too
large, not the route too long. Read the message, every time.
BILLING
-------
The billed unit is the element: one origin paired with one destination.
A 200 x 150 matrix costs 30,000 of them whether sent as one request or thirty.
EVERY tier stops at its allowance. Nothing bills for overage, so
exceeding the quota is a hard 403 rather than a larger invoice. Budget
against the allowance, not against a spend cap.
Requests that fail are not charged.
ERRORS — write retry logic against these, not against assumptions
-----------------------------------------------------------------
401 No key at all
{"error":"Authorization field missing","code":5001}
403 Key present but rejected
{"error":"Access to this API has been disallowed","code":5002}
429 Rate limited
{"error":"Rate Limit Exceeded","code":5003}
403 Monthly quota exhausted
{"error":"Quota exceeded","code":5004}
400 Request larger than your tier allows
{"error":{"code":6004,"message":"…Only a total of N routes are allowed."}}
404 A path we do not serve
{"error":"Not found","code":5010}
403 Your plan may not send that field [checked 2026-09-11]
{"error":"Access to this API has been disallowed","code":5011}
502 Routing engine unreachable
{"error":"The routing engine is unavailable","code":5008}
IMPORTANT: this API never returns 503. An unreachable engine is 502.
BRANCH RETRIES ON THE STATUS, NOT ON THE CODE.
429 is always worth retrying with exponential backoff and jitter. It is
either a per-minute rate limit or a concurrency refusal, and both clear
on their own. Do not inspect the code to decide — that is how you miss
one of them.
403 is terminal. Never retry it. A monthly allowance is deliberately a
403 rather than a 429, because it does not clear until the month does;
a client that backed off and retried would hammer a wall for weeks.
Read the code to decide WHAT TO SAY, never whether to retry. More than
one rejection shares a status in both directions, and new codes may be
added within the range. Code that branches on the status keeps working
when they are; code that branches on the code does not.
Two codes can also share ONE MESSAGE. Branch on the code, never on the
text -- the text is identical and the fixes are opposite:
"Access to this API has been disallowed"
5002 Key present but rejected
5011 Your plan may not send that field
(Read off the running service on 2026-09-04.)
ATTRIBUTION — a licence condition on the application you build
--------------------------------------------------------------
The map data is licensed under the Open Database License.
If your application shows these results to people, it must credit
"© OpenStreetMap contributors",
linking https://www.openstreetmap.org/copyright.
This is a legal requirement, not a preference. Include it.
PROMPTS FOR A CODING AGENT
--------------------------
Each is self-contained and generated from this API description:
https://www.waymatrix.io/agents/prompts
https://www.waymatrix.io/agents/prompts.json (the same, as JSON)
first-call Start here. Sets up the credential and proves the connection with one request.
big-matrix Your origin and destination lists multiply out past your tier's per-request cap.
directions You need the path, distance and duration for one journey rather than a grid.
isochrones Coverage questions: what is inside a 30-minute drive of this depot.
migrate-ors You already call openrouteservice and want the same code pointed here.
migrate-google You call Google computeRouteMatrix. This is a rewrite, not a URL change.
errors Before you ship. Retrying the wrong status is how a quota outage becomes an outage.
cost Before you commit. Turns a description of your job into a number.
PAGES WORTH READING
-------------------
https://www.waymatrix.io/agents this brief, rendered for a person
https://www.waymatrix.io/agents/prompts copy-paste prompts, one per task
https://www.waymatrix.io/guides/from-openrouteservice moving an existing openrouteservice integration
https://www.waymatrix.io/guides/from-google-distance-matrix field by field from Google Distance Matrix
https://www.waymatrix.io/pricing what a workload costs
https://www.waymatrix.io/how-the-graph-is-built the map data, its licence, and how the graph was built
Prompts for common tasks
The brief lists these prompts by name only. Here each one is in full, ready to paste.
Get a key and make the first call
Start here. Sets up the credential and proves the connection with one request.
You are integrating WayMatrix, an HTTP routing API.
BASE URL https://api.waymatrix.io
AUTH the API key raw in the "Authorization" header. No "Bearer " prefix.
A Bearer prefix is tolerated. ?api_key=<key> works on GET too,
but prefer the header: a key in a URL is written to access logs.
KEY read it from an environment variable. Never commit it.
Use only the endpoints named in this prompt. Do not infer others, and do not
guess at parameters — if something you need is not here, say so rather than
inventing it.
TASK
----
Add WayMatrix to this project and prove it works.
1. Read the key from an environment variable named WAYMATRIX_API_KEY.
Add it to the project's env example file with an empty value. Do not
commit a real key, and do not print it in logs or error messages.
2. Write one function that calls:
POST /v2/matrix/driving-car
with a body of {"locations": [[lon, lat], [lon, lat]], "metrics": ["duration"]}.
NOTE THE ORDER: longitude first, then latitude. This is the opposite of
what Google Maps shows, and getting it backwards produces valid-looking
coordinates in the wrong place rather than an error.
3. Call it once with two real coordinates from the region this project
serves, and print the duration in minutes.
4. Confirm the number is plausible for that pair before you call it done.
The response contains "durations" as an array of arrays in the order you
sent, in seconds, plus a metadata block. Do not reorder it — there is
nothing to reindex.Split a job bigger than one request allows
Your origin and destination lists multiply out past your tier's per-request cap.
You are integrating WayMatrix, an HTTP routing API.
BASE URL https://api.waymatrix.io
AUTH the API key raw in the "Authorization" header. No "Bearer " prefix.
A Bearer prefix is tolerated. ?api_key=<key> works on GET too,
but prefer the header: a key in a URL is written to access logs.
KEY read it from an environment variable. Never commit it.
Use only the endpoints named in this prompt. Do not infer others, and do not
guess at parameters — if something you need is not here, say so rather than
inventing it.
THE CONSTRAINT
--------------
An element is one origin paired with one destination, so a request costs
origins x destinations.
THE CAP IS PER TIER, AND IT IS NOT THE SAME NUMBER FOR EVERY KEY.
The service will not accept more than 90,000 elements from anyone
(300 x 300 square), and the smallest published tier cap is
2,500 (50 x 50). Which one applies depends on the key you are
holding, and it changes when the account changes plan.
So DO NOT hardcode a chunk size. Take it as a parameter, default it to
the smaller figure, and let the caller raise it. A splitter sized to the
service ceiling fails every single request on a smaller tier, and the
failure looks like a broken endpoint rather than a chunk size.
Over the cap the request is refused before any work happens, so there
is no partial result to salvage. The rejection names the limit it hit, so
read the cap off the first refusal rather than guessing it.
TASK
----
Write a function that takes any number of origins and destinations and
returns the full matrix, splitting into several requests when it must.
1. Compute origins x destinations. If it fits, send one request.
2. If not, split along DESTINATIONS first and keep origins whole where you
can — it keeps each response's rows aligned with your origin list and
makes reassembly a concatenation rather than a join.
3. Send the chunks with limited concurrency, not all at once. Respect 429
by backing off; see the error-handling prompt.
4. Reassemble into one array of arrays in the original order.
5. Assert the final shape is origins x destinations before returning it.
Billing is per element, not per request, so splitting a job costs exactly
the same as sending it whole. Split for the ceiling, never to save money.
Send each coordinate once: the request takes one "locations" array plus
"sources" and "destinations" arrays of INDICES into it. A point that is
both an origin and a destination does not need to appear twice.Get a route between points
You need the path, distance and duration for one journey rather than a grid.
You are integrating WayMatrix, an HTTP routing API.
BASE URL https://api.waymatrix.io
AUTH the API key raw in the "Authorization" header. No "Bearer " prefix.
A Bearer prefix is tolerated. ?api_key=<key> works on GET too,
but prefer the header: a key in a URL is written to access logs.
KEY read it from an environment variable. Never commit it.
Use only the endpoints named in this prompt. Do not infer others, and do not
guess at parameters — if something you need is not here, say so rather than
inventing it.
ENDPOINT
--------
POST /v2/directions/driving-car
TASK
----
Add a function that returns the route between an ordered list of points.
1. Send {"coordinates": [[lon, lat], [lon, lat], ...]}. Longitude first.
2. The points are visited in the order given. This endpoint does not
reorder them for you — it is not a travelling-salesman solver.
3. Read the distance and duration from the response's summary.
If you need the geometry as GeoJSON, the same operation has a GeoJSON form;
ask for it by path rather than by a format parameter.
KNOWN TRAP, and it is not ours:
python + gpx: The Python client calls response.Find everything reachable within a time
Coverage questions: what is inside a 30-minute drive of this depot.
You are integrating WayMatrix, an HTTP routing API.
BASE URL https://api.waymatrix.io
AUTH the API key raw in the "Authorization" header. No "Bearer " prefix.
A Bearer prefix is tolerated. ?api_key=<key> works on GET too,
but prefer the header: a key in a URL is written to access logs.
KEY read it from an environment variable. Never commit it.
Use only the endpoints named in this prompt. Do not infer others, and do not
guess at parameters — if something you need is not here, say so rather than
inventing it.
ENDPOINT
--------
POST /v2/isochrones/driving-car
TASK
----
Add a function that returns the area reachable from a point within a given
travel time, as a polygon you can test other points against.
1. Send {"locations": [[lon, lat]], "range": [1800], "range_type": "time"}.
Range is in SECONDS when range_type is time, and metres when it is
distance. 1800 is thirty minutes.
2. Several ranges in one call return nested polygons — ask for [900, 1800]
rather than making two calls, which costs the same and is one round trip.
3. The result is GeoJSON. Use a point-in-polygon test against it rather than
re-querying the API per candidate point.
Use this instead of a matrix when the question is "what is within reach"
rather than "how far apart are these". It is one call instead of hundreds.Move an openrouteservice integration across
You already call openrouteservice and want the same code pointed here.
You are integrating WayMatrix, an HTTP routing API.
BASE URL https://api.waymatrix.io
AUTH the API key raw in the "Authorization" header. No "Bearer " prefix.
A Bearer prefix is tolerated. ?api_key=<key> works on GET too,
but prefer the header: a key in a URL is written to access logs.
KEY read it from an environment variable. Never commit it.
Use only the endpoints named in this prompt. Do not infer others, and do not
guess at parameters — if something you need is not here, say so rather than
inventing it.
TASK
----
This codebase calls openrouteservice. Point it here instead.
1. Change the base URL to https://api.waymatrix.io. Change nothing else about
the request: paths, bodies, field names and response shapes match.
2. The auth header is the same — the raw key, no Bearer prefix — so an
existing openrouteservice client library works unmodified.
3. Then fix these four behaviours, which are the whole of the difference:
a. THIS API NEVER RETURNS 503. An unreachable engine is 502. Find every
retry that triggers on 503 and add 502, or retry only on 429.
b. Retry on the STATUS, not on the error code. A 429 is always worth
retrying with backoff — it is a per-minute limit or a concurrency
refusal, and both clear on their own. A 403 is never worth retrying,
and that is where an exhausted monthly allowance arrives: it is a 403
by design, precisely so it cannot be mistaken for a throttle.
c. Only matrix, directions and isochrones exist. No geocoding, no places,
no elevation, no optimisation. If this project calls those, it is
splitting across two providers rather than moving — say so.
d. Driving only, North America only.
4. Run one real request and compare the durations against what the old
endpoint returned for the same pair. They will not be identical; they
should be close.Move a Google Distance Matrix integration across
You call Google computeRouteMatrix. This is a rewrite, not a URL change.
You are integrating WayMatrix, an HTTP routing API.
BASE URL https://api.waymatrix.io
AUTH the API key raw in the "Authorization" header. No "Bearer " prefix.
A Bearer prefix is tolerated. ?api_key=<key> works on GET too,
but prefer the header: a key in a URL is written to access logs.
KEY read it from an environment variable. Never commit it.
Use only the endpoints named in this prompt. Do not infer others, and do not
guess at parameters — if something you need is not here, say so rather than
inventing it.
TASK
----
This codebase calls Google's Routes API computeRouteMatrix. Move it here.
This is a real rewrite, not a base URL change. Map it field by field:
origins[].waypoint.location.latLng -> locations[], as [lon, lat]
destinations[]... -> "destinations": indices into locations
travelMode: "DRIVE" -> the profile in the path
routingPreference -> NO EQUIVALENT. There is no traffic.
X-Goog-Api-Key header -> the "Authorization" header
X-Goog-FieldMask header -> no equivalent; the whole matrix returns
element.duration ("123s") -> durations[i][j], a number of seconds
element.distanceMeters -> distances[i][j], in metres
element.originIndex/destinationIndex-> not needed; see below
THE TWO THAT WILL BITE:
1. THE COORDINATE ORDER FLIPS. Google names its numbers in an object.
This API takes [longitude, latitude] as an array. A pair the wrong way
round is usually still a valid coordinate, so nothing errors — you get
durations for the wrong place. Check one result against a road you know.
2. GOOGLE'S RESPONSE IS UNORDERED and yours is not. Their documentation
says elements are not guaranteed to arrive in any order, which is why
each carries originIndex and destinationIndex. This API returns arrays
of arrays in the order you sent. DELETE the reindexing code rather than
porting it, and delete the parser that strips the trailing "s" from
their duration strings.
Also remove any per-element status handling: a failed pair is null in the
matrix, and a failed request is an HTTP error.Write the retry and error handling
Before you ship. Retrying the wrong status is how a quota outage becomes an outage.
You are integrating WayMatrix, an HTTP routing API.
BASE URL https://api.waymatrix.io
AUTH the API key raw in the "Authorization" header. No "Bearer " prefix.
A Bearer prefix is tolerated. ?api_key=<key> works on GET too,
but prefer the header: a key in a URL is written to access logs.
KEY read it from an environment variable. Never commit it.
Use only the endpoints named in this prompt. Do not infer others, and do not
guess at parameters — if something you need is not here, say so rather than
inventing it.
TASK
----
Write error handling for the calls in this project. Branch on these, and
do not add retries for anything not listed as retryable.
429 Rate limited, or too many at once.
RETRY with exponential backoff and jitter.
Both causes clear on their own. Retry on the
status; do not branch on the code to decide.
403 Key rejected, or the monthly quota is spent.
TERMINAL. Do not retry — it will not help.
Surface it; someone has to act.
400 The request is too large, or malformed.
TERMINAL. Fix the request; see the splitting
prompt if it is a size problem.
401 No key was sent. TERMINAL. A configuration bug.
404 A path that is not served. TERMINAL.
502 The routing engine did not answer.
RETRY a small number of times, then give up.
THIS API NEVER RETURNS 503. If your client library retries 503 by default,
that path is dead code here and 502 is what you must handle instead.
Errors come back as JSON with a "code" field alongside the HTTP status.
Log the code — it distinguishes cases that share a status, and 403 covers
both "this key is wrong" and "this month is spent", which need different
human responses.
Requests that fail are not billed. Do not build a reconciliation path for
charges on failed calls; there are none.Estimate what a workload will cost
Before you commit. Turns a description of your job into a number.
You are integrating WayMatrix, an HTTP routing API.
BASE URL https://api.waymatrix.io
AUTH the API key raw in the "Authorization" header. No "Bearer " prefix.
A Bearer prefix is tolerated. ?api_key=<key> works on GET too,
but prefer the header: a key in a URL is written to access logs.
KEY read it from an environment variable. Never commit it.
Use only the endpoints named in this prompt. Do not infer others, and do not
guess at parameters — if something you need is not here, say so rather than
inventing it.
THE BILLING MODEL
-----------------
The billed unit is the element: one origin paired with one destination.
A 200 x 150 matrix costs 30,000 of them whether you send it as one request
or as thirty. Requests that fail are not billed.
The free tier includes 50,000 elements a month and stops rather than billing.
Every tier stops at its allowance rather than billing, so the ceiling on
spend is the subscription itself. Exceeding the quota is a hard refusal, not
a larger invoice — forecast against the allowance, not against a budget.
TASK
----
Work out what this project will actually spend.
1. Find every call this codebase makes to a routing or distance API.
2. For each, work out the element count per call: origins x destinations
for a matrix; a directions call has a flat cost. An isochrone costs more
the further it reaches — flat at short range, climbing with the square of
the range — so price those from the ranges this code actually requests.
3. Multiply by how often each runs. Be honest about retries and about
anything inside a loop — that is where the surprise usually is.
4. Report the monthly total, then say which tier it lands in.
Two things worth checking while you are in there. If the same matrix is
computed repeatedly from unchanged inputs, cache it — results may be stored
indefinitely here, which is not true of every provider. And if a loop calls
the API once per candidate point, one isochrone call may replace all of it.