Quick Start

Send addresses, get optimized routes back. Get up and running in under 5 minutes.

Agent skill — fastest start

Working with Claude Code, Codex, Cursor or another coding agent? Install the r4m.ai skill and ask for a route in plain words. The agent learns how to geocode addresses, submit them, wait for the result and present it — and uses the MCP tools below when they are connected.

npx skills add r4m-ai/skills

Then put your API key in the agent's environment as R4M_API_KEY and ask, for example:

Plan the fastest round trip from 350 5th Ave, New York via 11 Wall St, Grand Central Terminal and 200 Eastern Pkwy, Brooklyn.

MCP for AI Agents

r4m.ai exposes a Model Context Protocol (MCP) endpoint so AI agents can call route optimization tools directly. Uses Streamable HTTP transport — serverless-compatible, no websocket required.

Available Tools

ToolDescription
create_optimizationSubmit addresses for route optimization
get_optimizationGet optimization status and result
list_optimizationsList all optimizations with pagination
check_usageCheck the current prepaid balance and activity
create_keyGenerate a new API key
list_keysList all API keys
forward_geocodeConvert a single address to lat/lng coordinates (status ok / ambiguous / not_found, with precision)
bulk_geocodeGeocode many addresses in one request (up to your plan limit); one result per distinct address, matched by original
request_topupMachine-payment top-up when the balance runs out (Preview)

Configuration

No key needed: add the URL to your client. The first time it connects, it opens your browser — sign in to r4m.ai with Google and allow it. A new account starts with a $1.00 balance. Where no browser is around (a server, CI), send an acc_ API key as a Bearer header instead. Rate limits and pricing are the same either way.

In a terminal:

claude mcp add --transport http r4m https://r4m.ai/api/mcp

Add --scope user to make it available in every project, or --scope project to share it via a checked-in .mcp.json.

Or from inside Claude Code — ask it, and it runs the command for you:

Add the r4m.ai MCP server: claude mcp add --transport http r4m https://r4m.ai/api/mcp

Then run /mcp, pick r4m and choose Authenticate: your browser opens to sign in. If r4m is not in the list yet, restart Claude Code.

No browser (a server, CI)? Add --header "Authorization: Bearer acc_…" to the command instead.

HTTP API

Base URL

https://r4m.ai/api/v1

1Get your API key

Sign up at r4m.ai and create an API key in the Developer Console. You can also create keys via the API.

2Submit addresses for optimization

curl -X POST https://r4m.ai/api/v1/optimizations \
  -H "Authorization: Bearer acc_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
  "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"
}'

# 202 Accepted — this call returns immediately; the solver keeps working (2-60s)
# { "success": true, "data": { "id": "550e8400-…", "status": "pending" } }
#
# That id is how you collect the finished plan. Either:
#   webhook — we POST it to your webhookUrl the moment it is ready, or
#   polling — GET /api/v1/optimizations/{id} until status is "completed"

3Receive the result

Step 2 returned status: "pending" — the solver runs in the background for roughly 2–60 seconds. When it completes, we'll POST the result to your webhookUrl, signed with HMAC-SHA256 (verify via the X-Webhook-Signature header). Delivery is retried with backoff (how many times depends on your plan).

No public endpoint to receive it? Leave webhookUrl out and poll instead — GET /api/v1/optimizations/{id} with the id from step 2, until status is completed or failed. Either way the payload below is what you get.

{
  "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"
  }
}

Shortened here — a real payload carries one waypoint per stop. waitTimeSeconds is time spent waiting for a delivery window to open, and unroutedStops lists any stop that could not be fitted into the plan at all — always check it.

Client SDKs

Auto-generated from our OpenAPI spec. Type-safe, always up to date.

Install

npm install @r4mai/sdk

Usage

import { createClient } from "@r4mai/sdk";

const r4m = createClient({ apiKey: "acc_your_api_key_here" });

// Submits, waits out the solve, and hands back the finished plan
const { data: optimization, error } = await r4m.createOptimizationAndWaitForRoutes({
  body: {
    addresses: [
      { address: "Depot", lat: 40.7128, lng: -74.006, isDepot: true },
      { address: "Stop 1", lat: 40.758, lng: -73.9855 },
      { address: "Stop 2", lat: 40.7484, lng: -73.9857 },
    ],
  },
});
if (error) throw error;

console.log(optimization.result);

Explore the full API

Try requests directly in your browser with the interactive API reference.