API quickstart
Create an API key, send one request, read the routes. About five minutes.
1. Create an API key
In the portal open Connect AI, then API keys, and create a key. It starts with airo_sk_ and is shown once. Store it in your secrets manager, never in source control.
curl https://ai-route-optimization.com/v1/me -H "Authorization: Bearer $AIRO_API_KEY"2. Create an optimization
Send the depot, the fleet and the orders. Give each order an address or latitude and longitude. The Idempotency-Key header is required.
curl -X POST https://ai-route-optimization.com/v1/optimizations \
-H "Authorization: Bearer $AIRO_API_KEY" \
-H "Idempotency-Key: so-2026-10-14-0001" \
-H "Content-Type: application/json" \
-d '{
"name": "Tuesday deliveries",
"depot": { "label": "Main warehouse", "address": "100 King St W, Toronto, ON M5X 1A9" },
"fleet": { "count": 2, "capacity": 60, "shift_start": "08:00", "shift_end": "17:00" },
"orders": [
{ "id": "SO-1001", "address": "255 Bremner Blvd, Toronto, ON", "demand": 12, "service_minutes": 10,
"time_window_start": "09:00", "time_window_end": "12:00" },
{ "id": "SO-1002", "address": "1 Blue Jays Way, Toronto, ON", "demand": 7 },
{ "id": "SO-1003", "latitude": 43.6677, "longitude": -79.3948, "demand": 20 }
]
}'import os, uuid, requests
API = "https://ai-route-optimization.com"
HEADERS = {"Authorization": f"Bearer {os.environ['AIRO_API_KEY']}"}
payload = {
"name": "Tuesday deliveries",
"depot": {"label": "Main warehouse", "address": "100 King St W, Toronto, ON M5X 1A9"},
"vehicles": [
{"id": "van-1", "capacity": 60, "driver": "Sam", "shift_start": "08:00", "shift_end": "17:00"},
{"id": "van-2", "capacity": 40},
],
"orders": [
{"id": "SO-1001", "address": "255 Bremner Blvd, Toronto, ON", "demand": 12,
"time_window_start": "09:00", "time_window_end": "12:00"},
{"id": "SO-1002", "address": "1 Blue Jays Way, Toronto, ON", "demand": 7},
],
}
# Reuse the same key when retrying the SAME request: it can never run or bill twice.
key = f"erp-run-{uuid.uuid4()}"
r = requests.post(f"{API}/v1/optimizations", json=payload,
headers={**HEADERS, "Idempotency-Key": key}, timeout=30)
r.raise_for_status()
opt = r.json()
print(opt["id"], opt["status"])const API = "https://ai-route-optimization.com";
const headers = { Authorization: `Bearer ${process.env.AIRO_API_KEY}` };
const res = await fetch(`${API}/v1/optimizations`, {
method: "POST",
headers: { ...headers, "Content-Type": "application/json", "Idempotency-Key": crypto.randomUUID() },
body: JSON.stringify({
depot: { address: "100 King St W, Toronto, ON M5X 1A9" },
fleet: { count: 2, capacity: 60 },
orders: [{ id: "SO-1001", address: "255 Bremner Blvd, Toronto, ON", demand: 12 }],
}),
});
if (!res.ok) throw new Error((await res.json()).error.message);
const optimization = await res.json();{
"id": "0d1f0c3e-5b64-4c0e-9a7a-2f6d3d5c9a10",
"name": "Tuesday deliveries",
"status": "queued",
"stage": "queued",
"progress": 0,
"strategy": "optimized",
"orders": 3,
"vehicles": 2,
"error_code": null,
"error_message": null,
"created_at": "2026-10-14T13:02:11Z",
"finished_at": null,
"links": {
"self": "https://ai-route-optimization.com/v1/optimizations/0d1f0c3e-5b64-4c0e-9a7a-2f6d3d5c9a10",
"result": "https://ai-route-optimization.com/v1/optimizations/0d1f0c3e-5b64-4c0e-9a7a-2f6d3d5c9a10/result",
"web": "https://ai-route-optimization.com/app/jobs/0d1f0c3e-5b64-4c0e-9a7a-2f6d3d5c9a10"
}
}3. Wait for the result
Optimizations are asynchronous: geocoding and solving take a few seconds. Poll the self link every 2 to 5 seconds, or register a webhook and skip polling.
import time
while opt["status"] in ("queued", "running"):
time.sleep(2)
opt = requests.get(opt["links"]["self"], headers=HEADERS, timeout=30).json()
if opt["status"] != "completed":
raise RuntimeError(opt["error_message"])
result = requests.get(opt["links"]["result"], headers=HEADERS, timeout=30).json()
for route in result["routes"]:
print(route["vehicle_ref"], [s["order_ref"] for s in route["stops"]])Next
- Request and response details: /docs/api/optimizations
- Keys, scopes and rate limits: /docs/api/authentication
- Get a signed callback instead of polling: /docs/api/webhooks
- Errors, limits and billing: /docs/api/errors