WayMatrixWayMatrix
WayMatrix
  • Getting started
  • API reference
  • Your API keys
  • Your usage
  • Billing
  • Back to waymatrix.io
Getting startedUse with an AI agent
powered by Zuplo
Getting started

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.

Integration brief
# 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.

First call prompt
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.

Big matrix prompt
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.

Directions prompt
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.

Isochrones prompt
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.

Openrouteservice migration prompt
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.

Google migration prompt
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.

Error handling prompt
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.

Cost estimate prompt
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.
Last modified on October 23, 2026
Getting started
On this page
  • The integration brief
  • Prompts for common tasks