Welcome to the Optimiciti Developer Portal
The developer portal is your starting point for accessing our public-facing API. This portal is for developers and users who want to integrate Optimiciti's optimization solvers directly into their own applications and workflows by making requests to our back-end.
Click here to visit our main site, where you can solve problems interactively in your browser without writing any code.
Our API Reference has the most up-to-date endpoint documentation, and you can download the full OpenAPI spec to generate a client or import into Postman/Insomnia.
What is the Optimiciti API?
The Optimiciti API gives you programmatic access to the same optimization engine that powers our web application.
Submit a problem, receive a problemId, then poll for results — all over standard HTTPS requests authenticated with your API key.
Optimization Models
The API currently supports two problem types, specified via the problemType field in your request body.
Vehicle Routing (vehicle-routing)
The classic Vehicle Routing Problem (VRP). One or more vehicles depart from a depot and must visit a set of locations using the lowest-cost routes. Supports:
- Capacity constraints — assign demand values to locations and enforce vehicle load limits
- Time windows — restrict arrival times at individual locations
- Multiple vehicle types — model fleets with different capacities, speeds, or cost profiles
- Bring your own travel costs —
POST /problemnever computes travel times or distances; build the matrix withPOST /cost_matrix(straight-line by default, road network on the Professional plan) and submit it asproblemData.travelCosts. See Submit a problem below.
Pickup & Delivery Routing (pickup-delivery)
An extension of the VRP where locations are paired into trips. Each trip has a pickup location that must be visited before its corresponding delivery location, and both stops must be served by the same vehicle. Useful for:
- Ride-hailing and shuttle services
- Courier and last-mile delivery with paired collections
- Airport and logistics transfer networks
API Workflow
Optimization runs are asynchronous. The typical flow is:
-
Submit a problem
POST /problemwith auserIdand aproblemobject containing theproblemType, locations, vehicles, vehicle types, and time settings. The response is202 Acceptedwith arun_id— the solver runs in the background; the call does not wait for it. (run_idand theproblemIdyou poll with are the same value.)POST /problemdoes not compute travel costs, andproblemData.travelCostsis required: a complete matrix with a cost for every pair of locations. Build one withPOST /cost_matrix, then copy itsdata.travelCostResponsesarray intoproblemData.travelCostsunchanged. In the/cost_matrixrequest,costSettings.costMetricis required (duration,distanceorcustom; without it the call returns422). SetautoCalculateDistanceandautoCalculateDurationtotrue, and setdurationMethodexplicitly as well asdistanceMethod, so every pair gets a duration:CodeYou can also supply your own matrix if you already have distance or duration data.
What happens when the matrix is wrong:
- Missing or empty
travelCosts: rejected with400 MISSING_TRAVEL_COSTS. - An all-zero matrix: solves normally. Every leg is free, so the routes are unlikely to be useful.
- Pairs with a missing duration or cost: the run can still come back
succeededwith 0 stops assigned.
So don't rely on the run's
status: check how many stops were actually assigned (see Fetch results below).Code - Missing or empty
-
Poll for status
GET /problem/{problemId}(orPOST /run_status) returns the run'sstatusonly —queued,running, or a terminalsucceeded/failed/cancelled. It does not return the routes. Poll until the status is terminal. -
Fetch results
Once the run has
succeeded,POST /run_results_by_run_idreturns the plan. Read the route fromrouteStopsFull, and before trusting the result, comparedata.resultSummary.assignmentSummary.locationsAssigned.assignedwith the number of stops you submitted, counted from your own request: your locations whereisDepotis not true (for pickup-delivery, the distinct pickup and delivery locations of your trips). Asucceededrun can still have assigned nothing.Also check that
data.problemResult.unassignedLocationsFullanddata.problemResult.unassignedTripsFullare both empty. The count alone can look right while a location was only partly served, or while a trip was dropped at a location that another trip still visits.Don't rely on
locationsAssigned.totalalone for this. It is computed from the result (assigned plus the unassigned lists), not from your request. For pickup-delivery it adds unassigned trips to assigned locations, and it isnullwhen both are empty. -
Cancel if needed
POST /problem/cancel/{problemId}cancels a queued or running run. It has no effect once the run has reached a terminal state.
Additional Endpoints
| Endpoint | Description |
|---|---|
POST /cost_matrix | Calculate a travel time and distance matrix for a set of locations |
GET /health | Returns the operational status of the API |
Plans & Limits
Access to the API is tied to your subscription plan. The following limits apply to each optimization run.
| Starter | Professional | |
|---|---|---|
| Price | Free | $20 / month |
| Locations per run | 50 | 250 |
| Trips per run | — | 250 |
| Vehicles per run | — | 50 |
| Vehicle types per run | — | 20 |
| Saved scenarios | 5 | Unlimited |
| API access | — | — |
| Auto-populate travel times | — | ✓ |
During the beta, API access is issued directly: email [email protected].
Developer plan coming soon. A dedicated Developer plan will offer bigger limits on problem size, up to 10 API solves per minute, and full REST API access. Join the waitlist to be notified at launch.
Authentication
For programmatic access — scripts, servers, SDKs, anything without a browser — authenticate with an API key. Send it as a bearer credential in the Authorization header on every request:
Code
For example, to list your past runs:
Code
The body still needs a user_id field, but its value is ignored: the runs returned are always the ones belonging to the account your key identifies. A few endpoints need no key at all, such as GET /health and GET /plans; the API Reference marks which ones do.
An API key replaces the Clerk token used by the web app — you send one or the other, never both. The gateway resolves your key to your account and takes your identity from it, so (just like a token) your identity comes from the credential and is never read from a userId in the request body. Keys are issued by us during the beta rather than from a self-service page — email [email protected] if you need one, or another for a second environment. Keys are scoped to your account. Rate limits are not counted per key — see Rate Limits below.
Requests with no
Originheader — curl, scripts, and any server-to-server call — must carry an API key. Without one, authenticated endpoints return401. That's expected, not a broken key. Note that a gateway401/403is an RFC 7807problem+jsonbody, not the standard{ success, error }envelope the API itself returns.
Rate Limits
Rate limits vary by endpoint, and they are the same on every plan. Solving and other expensive calls (/problem, /cost_matrix, the exports) are limited far more tightly than reads and status polls.
Limits are counted per IP address, not per API key, per user or per plan. Two things follow, and they surprise people in opposite directions:
- Several servers calling with the same key each get their own allowance, because they are different addresses.
- Everything behind one address shares an allowance — a NAT'd office, a CI runner, or several services on one host — even with different keys.
A request over the limit gets a 429 Too Many Requests from the gateway, as an RFC 7807 problem+json body rather than the API's { success, error } envelope, with a retry-after header in seconds. Email [email protected] if you need a higher limit for your use case.
Contact
Questions about the API or your subscription? Reach us at [email protected] or visit the terms of service.
