API Endpoints
Complete reference for all r4m.ai REST API endpoints. All requests require a Bearer token in the Authorization header.
Optimizations
/api/v1/optimizationsCreate a new route optimization. Returns 202 Accepted immediately; the optimization is processed asynchronously. A stop sent without lat/lng is geocoded as part of the request (charged at the geocode price) and must resolve to one precise place: pass geocoding.region or geocoding.bounds to rule out homonyms, or the request is refused with 422 GEOCODING_UNRESOLVED and the candidates.
Request body
{
"addresses": [
{
"address": "Depot — 25 Kent Ave, Brooklyn, NY",
"lat": 40.7218,
"lng": -73.9615,
"isDepot": true,
"serviceTimeSeconds": 900
},
{
"address": "1155 Manhattan Ave, Brooklyn, NY",
"lat": 40.7304,
"lng": -73.954,
"serviceTimeSeconds": 600,
"timeWindowStart": "09:00",
"timeWindowEnd": "10:30",
"priority": 1
},
{
"address": "238 Bedford Ave, Brooklyn, NY",
"lat": 40.7135,
"lng": -73.967,
"serviceTimeSeconds": 900
},
{
"address": "1 Front St, Brooklyn, NY",
"lat": 40.7033,
"lng": -73.9881,
"serviceTimeSeconds": 600,
"priority": 2
},
{
"address": "445 Albee Square W, Brooklyn, NY",
"lat": 40.6926,
"lng": -73.9873,
"serviceTimeSeconds": 1200
}
],
"parameters": {
"travelMode": "driving",
"optimizeMode": "Time",
"roundTrip": true,
"routeStartAt": "2026-04-14T08:00",
"timezone": "America/New_York"
},
"webhookUrl": "https://your-server.com/webhooks/r4m"
}Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
travelMode | string | "driving" | Mode of transportation used when computing the route.("driving" | "walking" | "bicycling") |
roundTrip | boolean | false | When true, the route returns to the starting depot after the last stop. |
routeStartAt | string | — | When the route starts, as local date and time without a zone, e.g. 2026-04-14T08:00. Read in `timezone`. |
optimizeMode | string | "Time" | Optimization objective: minimize total travel time or distance.("Time" | "Distance") |
timezone | string | — | IANA timezone the local times in this request are read in, for example America/New_York. Required when any stop has a delivery window. |
maxRoutesFree —, Pro 50, more by agreement | integer | — | Maximum number of vehicles (routes) for multi-route VRP optimization. Availability and ceiling depend on your plan.(≥ 1) |
maxRouteDurationSecondsFree —, Pro 86400, more by agreement | integer | — | Maximum wall-clock duration of one route, in seconds. Availability and ceiling depend on your plan.(≥ 1) |
maxRouteDistanceMetersFree —, Pro 2000000, more by agreement | integer | — | Maximum distance of one route, in meters. Applied in steps of 1609 meters, rounded down, so a cap under 1609 becomes 1609. Availability and ceiling depend on your plan.(≥ 1) |
Address fields
| Field | Type | Required | Description |
|---|---|---|---|
address | string | Yes | The stop's address. With `lat`/`lng` it is only a label; without them it is geocoded (charged at the geocode price) and must resolve to exactly one place. |
lat | number | No | Latitude in decimal degrees. Optional: omit both `lat` and `lng` to have the address geocoded.(-90–90) |
lng | number | No | Longitude in decimal degrees. Goes together with `lat`.(-180–180) |
isDepot | boolean | No | When true, marks this stop as the route's depot (start and/or end). |
serviceTimeSeconds | integer | No | Time spent at this stop, in seconds (service dwell time).(≥ 0) |
timeWindowStart | string | No | Earliest local time this stop can be served, HH:MM. Taken as the first such time at or after the route starts, so 02:00 on a route leaving at 22:00 means the following morning. |
timeWindowEnd | string | No | Latest local time this stop can be served, HH:MM. The stop's `serviceTimeSeconds` must finish by then, not merely begin. |
priority | integer | No | Stop priority (1 = highest). Higher-priority stops are scheduled earlier.(1–9999) |
Response (202 Accepted)
{
"success": true,
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"status": "pending",
"priceMicros": 1,
"addressCount": 4,
"webhookUrl": "https://your-server.com/webhook",
"createdAt": "2026-04-14T10:00:00Z"
}
}/api/v1/optimizationsList optimizations with cursor-based pagination.
Query parameters
?cursor=<last-id>&limit=20Response
{
"success": true,
"data": {
"items": [
{ "id": "uuid", "status": "completed", "priceMicros": 1, "createdAt": "..." },
{ "id": "uuid", "status": "pending", "priceMicros": 2, "createdAt": "..." }
],
"nextCursor": "550e8400-e29b-41d4-a716-446655440001"
}
}/api/v1/optimizations/:idGet a single optimization by ID, including the result when completed.
Response (completed)
{
"success": true,
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"status": "completed",
"priceMicros": 10000,
"webhookUrl": "https://your-server.com/webhooks/r4m",
"webhookStatus": "delivered",
"result": {
"routes": [
{
"routeStartAt": "2026-04-14T08:00:00-04:00",
"totalDistanceMeters": 19473,
"totalTimeSeconds": 8285,
"polyline": "ulpwFpplbMl@qA@CLOYYIIIK…",
"waypoints": [
{
"index": 0,
"arrivalAt": "2026-04-14T08:00:00-04:00",
"departureAt": "2026-04-14T08:15:00-04:00",
"waitTimeSeconds": 0,
"cumulativeDistanceMeters": 0,
"cumulativeTimeSeconds": 0,
"polylineToNext": "ulpwFpplbMl@qA@CLOYYIIIK…"
},
{
"index": 1,
"arrivalAt": "2026-04-14T09:00:00-04:00",
"departureAt": "2026-04-14T09:10:00-04:00",
"waitTimeSeconds": 2543,
"cumulativeDistanceMeters": 1416,
"cumulativeTimeSeconds": 3600,
"polylineToNext": "}brwFhckbMI{A?IGkAIqBMkC…"
}
]
}
],
"unroutedStops": []
},
"createdAt": "2026-04-14T11:58:12Z",
"updatedAt": "2026-04-14T11:58:41Z"
}
}/api/v1/optimizations/:idPermanently delete an optimization and its result.
Response
{
"success": true,
"data": { "deleted": true }
}Geocoding
/api/v1/geocoding/forwardForward geocode a single address string to lat/lng coordinates.
Request body
{
"address": "1600 Amphitheatre Parkway, Mountain View, CA"
}Response
{
"success": true,
"data": {
"lat": 37.4220,
"lng": -122.0841,
"formattedAddress": "1600 Amphitheatre Pkwy, Mountain View, CA 94043"
}
}/api/v1/geocoding/bulkBulk geocode many addresses in a single request (up to your plan limit). One result per distinct address, in the order first sent — match by original, never by position; duplicates are charged once.
Request body
{
"addresses": [
"1600 Amphitheatre Parkway, Mountain View, CA",
"Springfield",
"asdkjh qwe 123"
]
}Response — status is ok, ambiguous or not_found; only precision "address" is exact enough to drive to
{
"success": true,
"data": {
"results": [
{ "original": "1600 Amphitheatre Parkway, Mountain View, CA", "status": "ok",
"address": "1600 Amphitheatre Pkwy, Mountain View, CA 94043, USA",
"lat": 37.4220, "lng": -122.0841, "precision": "address", "confidence": "high" },
{ "original": "Springfield", "status": "ambiguous", "candidates": [
{ "address": "Springfield, IL, USA", "lat": 39.8007, "lng": -89.6449, "precision": "city", "confidence": "medium" },
{ "address": "Springfield, MA, USA", "lat": 42.1013, "lng": -72.5893, "precision": "city", "confidence": "medium" },
{ "address": "Springfield, MO, USA", "lat": 37.2090, "lng": -93.2923, "precision": "city", "confidence": "medium" }
] },
{ "original": "asdkjh qwe 123", "status": "not_found" }
],
"priceMicros": 15000
}
}API Keys
/api/v1/keysCreate a new API key. The raw key (prefixed with acc_) is returned only once in this response -- store it securely.
Request body
{
"name": "Production Server"
}Response
{
"success": true,
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Production Server",
"prefix": "acc_a1b2c3",
"key": "acc_a1b2c3d4e5f6g7h8i9j0...",
"createdAt": "2026-04-14T10:00:00Z"
}
}/api/v1/keysList all API keys. Raw keys are never returned -- only the prefix.
Response
{
"success": true,
"data": [
{
"id": "uuid",
"name": "Production Server",
"prefix": "acc_a1b2c3",
"lastUsedAt": "2026-04-14T09:30:00Z",
"createdAt": "2026-04-10T10:00:00Z"
}
]
}/api/v1/keys/:idRevoke an API key (soft delete). The key will stop working immediately.
Response
{
"success": true,
"data": { "revoked": true }
}Usage & Account
/api/v1/usageGet the current prepaid balance (micro-dollars) and recent activity.
Response
{
"success": true,
"data": {
"balance": 4958,
"ledger": [
{
"id": "8f3a2b1c-...",
"delta": -42,
"reason": "usage",
"kind": "optimization",
"quantity": 250,
"refId": "opt_7f3a...",
"createdAt": "2026-07-09T10:00:00Z"
}
]
}
}/api/v1/accountGet current user account and plan information.
Response
{
"success": true,
"data": {
"email": "dev@example.com",
"planTier": "pro",
"balanceMicros": 4820,
"plan": {
"name": "Pro",
"maxAddressesPerRequest": 100,
"maxApiKeys": 5
}
}
}/api/v1/webhook-secretThe secrets webhook deliveries are signed with. primary signs every delivery; previous is set while a rotation is in progress — verify against both. Any API key can read this: if a key leaks, revoke it and rotate the secret in the console. Never cached.
Response
{
"success": true,
"data": {
"primary": { "secret": "whsec_…", "createdAt": "2026-11-02T23:44:20.313Z" },
"previous": null
}
}/api/v1/credits/topupPreview — machine-payment (MPP) top-up. Without a credential, returns a 402 with an MPP challenge; on payment, adds to the balance and returns a receipt. Enabled per deployment; returns 501 NOT_CONFIGURED until then. See the Machine Payments concept.
402 challenge response
{
"success": false,
"error": { "code": "INSUFFICIENT_FUNDS", "message": "Payment required to top up the balance." },
"challenge": {
"scheme": "mpp",
"status": "payment_required",
"amountCents": 500,
"currency": "usd",
"grantMicros": 5000000,
"endpoint": "/api/v1/credits/topup",
"minCents": 500
}
}Try it live
Send requests directly from your browser with the interactive API reference powered by Scalar.
Open Interactive API Reference