SagaToll API
Calculate Nordic toll, bridge, ferry, and congestion-zone costs for commercial routes straight from your own systems. JSON in, an explainable cost breakdown out.
Base URL
All endpoints are served under the versioned API path.
https://sagatoll.sagabuild.com/api/v1Authentication
Every request needs a tenant API key sent as a Bearer token.
Authorization: Bearer st_live_your_key_hereCreate and revoke keys under Dashboard → API & access. Keys are shown in plaintext only once at creation; SagaToll stores a salted hash. Use the st_test_ prefix for sandbox keys and st_live_ for production.
Quick start
Calculate the toll cost of a single truck trip.
curl -X POST https://sagatoll.sagabuild.com/api/v1/trips/calculate \
-H "Authorization: Bearer st_live_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"origin": { "latitude": 59.2058, "longitude": 10.9552, "address": "Fredrikstad" },
"destination": { "latitude": 59.9132, "longitude": 10.6379, "address": "Bærum" },
"departureAt": "2026-04-13T06:45:00+02:00",
"vehicle": {
"type": "truck",
"weightKg": 18000,
"axleCount": 3,
"euroClass": "euro6",
"fuelType": "diesel",
"hasAutopass": true,
"lengthCm": 1200
},
"options": { "currency": "NOK", "includeRoute": true }
}'Amounts are returned in minor units (øre/cent) plus a localized formatted string. A per-line breakdown explains which rule priced each passage.
Price your own route
Already planned the run in your TMS? Send the geometry and we price that exact path — no re-routing.
// inside the calculate request body
"route": {
"coordinates": [
{ "latitude": 59.2058, "longitude": 10.9552 },
{ "latitude": 59.7400, "longitude": 10.8000 },
{ "latitude": 59.9132, "longitude": 10.6379 }
]
}Add an optional route object. When present, SagaToll skips its own routing and prices your geometry directly. Send the full path (not just the stops) so passage matching is accurate. Without it, SagaToll routes through up to 25 waypoints itself.
Endpoints
The full v1 surface. Reference data and trips need read; key management needs admin.
| POST | /api/v1/trips/calculate | Calculate toll cost for one trip. |
| POST | /api/v1/trips/bulk | Calculate a batch of trips in one call. |
| GET | /api/v1/trips | List previously calculated trips. |
| GET | /api/v1/trips/{id} | Retrieve one trip with its full breakdown. |
| GET | /api/v1/vehicles | List vehicle profiles. |
| POST | /api/v1/vehicles | Create a vehicle profile. |
| GET | /api/v1/vehicles/{id} | Retrieve one vehicle profile. |
| PUT | /api/v1/vehicles/{id} | Update a vehicle profile. |
| DELETE | /api/v1/vehicles/{id} | Delete a vehicle profile. |
| GET | /api/v1/toll-points | Browse passage points by country and type. |
| GET | /api/v1/rates | Browse published rate rules. |
| GET | /api/v1/api-keys | List API keys. admin |
| POST | /api/v1/api-keys | Create an API key. admin |
| DELETE | /api/v1/api-keys/{id} | Revoke an API key. admin |
| GET | /api/v1/health | Operational health check. |
| GET | /api/v1/ready | Container/database readiness check. |
Responses & errors
Every response uses a consistent envelope.
Success
{
"data": { /* ... */ },
"meta": { "requestId": "..." }
}Error
{
"error": {
"code": "UNAUTHORIZED",
"message": "...",
"details": null
},
"meta": { "requestId": "..." }
}Common codes: UNAUTHORIZED (401), FORBIDDEN (403), INVALID_TRIP_PAYLOAD (400), RATE_LIMIT_EXCEEDED (429).
Rate limits
Limits apply per plan and per key. Responses expose remaining quota in headers.
| Plan | Per hour | Per day | Bulk size |
|---|---|---|---|
| free | 100 | 1,000 | 10 |
| standard | 1,000 | 25,000 | 100 |
| premium | 5,000 | 100,000 | 500 |
| enterprise | 10,000 | 250,000 | 1000 |
Read remaining quota from X-RateLimit-Remaining-Hour and X-RateLimit-Remaining-Day. On a 429, wait the number of seconds in Retry-After.
Cross-origin (CORS)
The API can be called directly from a browser dashboard.
All /api/v1 endpoints send permissive CORS headers and answer preflight OPTIONS requests, so your frontend can call SagaToll with an API key. Treat live keys as secrets — for public browser apps, proxy requests through your own backend.
Next step