worldtolls

worldtolls API

Calculate toll costs for routes across Turkey, Greece, Bulgaria, Albania, North Macedonia, Serbia and Montenegro directly from your own app, dispatch system or fleet-management platform — send us two points (or a full multi-stop itinerary) and vehicle class, we return the toll cost, broken down by country and gate, in both the original currency and TRY. worldtolls runs its own lightweight gateway in front of the underlying calculation engine, so you get a simple, stable request/response shape without needing to know anything about the engine behind it.

How do I get a key? Pick an API plan and request access from My account — tell us briefly how you'll use it. We review requests by hand (usually same-day) and, the moment we approve, worldtolls generates your key automatically — no separate key-creation step, it's ready in your account panel immediately.
📌 15.09.2026: worldtolls is a global service, so the Trial and Basic API plans no longer lock you to a fixed country. When you request access on either plan you choose which countries you need (1 for Trial, 2 for Basic) from all 7 above — see Pricing. Pro and Enterprise still include all 7 countries with nothing to choose.

Quickstart

The fastest path from zero to a priced route, once you have a key (see the box above):

curl -X POST https://worldtolls.com/gateway/toll \
  -H "X-API-Key: wtk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "from": { "lat": 41.0082, "lon": 28.9784 },
    "to":   { "lat": 40.6401, "lon": 22.9444 }
  }'

That's it — from/to are the only required fields. Everything else (vehicle_class, vehicle_profile) has a safe default. Read result.toplam_genel_tl for the grand total in TRY, and check result.eksiksiz — see POST /toll below for the full response shape and what every field means.

Base URL

Base URLhttps://worldtolls.com/gateway
FormatJSON, both request and response (application/json; charset=utf-8)
TransportHTTPS only. Plain HTTP requests are not supported — use an HTTPS URL from the start rather than relying on a redirect.

Authentication

Send your key in an X-API-Key header on every request that needs one (see the endpoint table below). Your key looks like wtk_... — it's generated by worldtolls itself the moment your API access is approved, and you can view/copy it any time from My account. It is not a ucretliyol.com key: it's issued and validated entirely by worldtolls' own database, and it only works against worldtolls' own endpoints below.

curl -H "X-API-Key: wtk_your_key_here" https://worldtolls.com/gateway/status
⚠️ Treat your key like a password. It's meant for server-to-server use — keep it on your own backend, never embedded in a distributed mobile app or browser JavaScript where anyone could extract it. If you think your key has been exposed, email us and we'll rotate it for you. Note that unlike some APIs, Access-Control-Allow-Origin is open on this gateway (so a direct browser call won't be blocked by CORS) — that's a convenience for quick testing, not a recommendation to ship a key in client-side code.

Rate limits & country coverage

Each plan has an hourly and a monthly request limit, and access to a specific set of countries — see Pricing for the exact numbers for your plan, or check your live usage any time in My account (it shows your limit and how much you've used this hour and this month, updated in real time). Requesting a country your plan doesn't include simply won't appear in the response — it isn't an error, the relevant fields (and that country's share of the total) are just omitted. Exceeding your limit returns 429 with "error": "rate_limit_exceeded".

Limits use fixed windows, not a rolling clock: the hourly counter resets at the top of each hour (e.g. 14:00, 15:00, …) and the monthly counter resets on the 1st of each calendar month — both in the gateway server's time zone. There's no burst/grace allowance beyond your plan's number.

Versioning & stability

The gateway doesn't use a version number in the URL (no /v1/) — there's one live version of the API, and we intend to keep changes to it backward-compatible: existing fields keep their name, type and meaning, and new fields (like vehicle_profile, dikkat and eksiksiz — all added 16.09.2026) are additive. Code that only reads the fields it needs and ignores unknown ones will keep working across changes. If a genuinely breaking change is ever needed, we'll announce it and give existing integrations a migration window rather than changing behavior underneath you silently.

Vehicle classes

Send the class that matches the vehicle as vehicle_class (an integer, 1–6). It's applied consistently across every country in the response. Omit it and it defaults to 1.

ValueVehicle
1Car / SUV
2Van / Minibus
3Bus
4Truck (4–5 axle)
5Truck / Semi (6+ axle)
6Motorcycle
Each country maps this generic 1–6 class onto its own local category scheme automatically (a Bulgarian heavy-vehicle bucket, an Albanian 4- or 5-category scheme, a Greek 1–4 category, and so on) — you don't need to know each country's local scheme. The category actually used for each country is always echoed back in that country's own response object as kullanilan_kategori (see Full response field reference), so you can always see exactly what was applied.

Vehicle profile (optional)

16.09.2026: some routes cross toll structures that price by more than just the generic vehicle_class — for example a multi-category bridge with its own height/axle/seat brackets (Greece's Rio–Antirrio bridge has 9 such categories), or Bulgaria's heavy-vehicle e-vignette system, which prices by emission class and tonnage bracket rather than a single generic class. If you omit vehicle_profile entirely, nothing changes for you — the engine falls back to its existing safe defaults for your vehicle_class, exactly as before this field existed. Send whichever of these fields you actually know; every field is independently optional.

FieldTypeMeaning
height_mnumberVehicle height in metres (e.g. 3.9). Used for height-based brackets on multi-category structures like Rio–Antirrio.
axlesintegerNumber of axles.
seatsintegerPassenger seat count — the usual way a bus/coach category is identified.
trailerbooleanWhether the vehicle is towing a trailer.
disabled_permitbooleanWhether the vehicle holds a disabled-driver permit (some structures apply a reduced/zero rate).
bg_categorystring (enum)Bulgaria heavy-vehicle category. One of: kamyon_3_5_12, kamyon_12_2_3aks, kamyon_12_4aks, otobus_3_5_12, otobus_12.
bg_emission_classstring (enum)Bulgaria emission class. One of: euro_0_i_ii, euro_iii_iv, euro_v, euro_vi_eev, zev.
bg_co2_classintegerBulgaria CO₂ class, 1–5. Only meaningful when bg_emission_class is euro_vi_eev (it selects the sub-bracket within that emission class).

Example — a 45-seat coach crossing Rio–Antirrio

{
  "from": { "lat": 38.325, "lon": 21.782 },
  "to":   { "lat": 38.335, "lon": 21.740 },
  "vehicle_class": 3,
  "vehicle_profile": { "seats": 45, "height_m": 3.9 }
}

An invalid bg_category, bg_emission_class or bg_co2_class value is rejected up front with HTTP 400 and a valid_values array listing exactly what's accepted — see Errors. There's nothing to validate for the other fields; any number/boolean is accepted as given.

Not sending enough detail for a structure that genuinely needs it doesn't cause an error — it's priced as far as it can be, and the gap is reported back to you explicitly via the response's dikkat array and eksiksiz flag (see POST /toll below), so a fee is never silently dropped from your total without you being told.

Endpoints

Method & pathAuthDescription
GET /NoService info and endpoint list
GET /statusNoHealth check — good for uptime/monitoring tooling
POST /tollYesToll cost between two points
POST /toll/multiYesToll cost across 3+ waypoints (multi-stop) — available on every API plan

POST /toll

Calculates the toll cost of driving from one point to another, across every country your plan covers along the way. This is the endpoint for the common case: you already know (or your own map/navigation stack already computed) the start and end point of a trip, and just need the toll cost for it.

cURL

curl -X POST https://worldtolls.com/gateway/toll \
  -H "X-API-Key: wtk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "from": { "lat": 41.0082, "lon": 28.9784 },
    "to":   { "lat": 40.6401, "lon": 22.9444 },
    "vehicle_class": 1
  }'

Node.js (fetch)

const res = await fetch('https://worldtolls.com/gateway/toll', {
  method: 'POST',
  headers: {
    'X-API-Key': process.env.WORLDTOLLS_API_KEY,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    from: { lat: 41.0082, lon: 28.9784 },
    to:   { lat: 40.6401, lon: 22.9444 },
    vehicle_class: 1,
  }),
});
const data = await res.json();
if (!data.ok) throw new Error(`${data.error}: ${data.message}`);
console.log(data.result.toplam_genel_tl);

Python (requests)

import os, requests

resp = requests.post(
    "https://worldtolls.com/gateway/toll",
    headers={"X-API-Key": os.environ["WORLDTOLLS_API_KEY"]},
    json={
        "from": {"lat": 41.0082, "lon": 28.9784},
        "to":   {"lat": 40.6401, "lon": 22.9444},
        "vehicle_class": 1,
    },
    timeout=10,
)
data = resp.json()
if not data["ok"]:
    raise RuntimeError(f"{data['error']}: {data['message']}")
print(data["result"]["toplam_genel_tl"])

A real response has more fields than most integrations need day-to-day — the table below covers the ones you'll use for a typical domestic + one-foreign-country trip. Full response field reference below documents every field this endpoint can return, including the ones omitted here for brevity (per-country breakdowns for all 6 non-Turkey countries, the route polyline, alternate routes, and the multi-category facility fields).

{
  "ok": true,
  "result": {
    "mesafe_km": 690.4,
    "sure_dk": 428.6,
    "toplam": 210.50,
    "giseler": [ ... ],
    "yurtdisi": { "toplam_tl": 38.20, ... },
    "toplam_genel_tl": 248.70,
    "izinli_ulkeler": ["TR", "GR"],
    "eksiksiz": true,
    "dikkat": []
  }
}
FieldMeaning
mesafe_kmTotal route distance in kilometres
sure_dkEstimated driving duration in minutes for this route
toplamTurkish (domestic) toll total for this route, in TRY
giselerArray of the individual Turkish toll gate/bridge crossings — see field reference
yurtdisiPresent only if the route crosses into Greece and your plan covers it — Greece's own toll cost and detail, see field reference
toplam_genel_tlGrand total across every country your plan covers for this route, converted to TRY
izinli_ulkelerEchoes back exactly which country codes your key is authorized for
eksiksiztrue when toplam_genel_tl reflects the whole route with nothing left out. false means at least one thing along the route could not be priced (see dikkat) and is NOT included in the total.
dikkatArray of human-readable items needing your attention — empty when eksiksiz is true. Each item has a tip (vehicle_profile_needed or gate_unmatched) and a message. A vehicle_profile_needed item also has facility, field_needed and options — send the missing vehicle_profile field and retry to get that structure priced.

Country-specific sections (e.g. the Greece leg above, under yurtdisi) are simply omitted from the response if your plan doesn't include that country, rather than erroring. The same applies to Bulgaria/Albania/North Macedonia/Serbia/Montenegro's own sections (bulgaristan/arnavutluk/makedonya/sirbistan/karadag) — see the field reference for their exact shape.

Example — dikkat when a multi-category structure needs more detail

{
  "ok": true,
  "result": {
    "toplam_genel_tl": 210.50,
    "eksiksiz": false,
    "dikkat": [
      {
        "tip": "vehicle_profile_needed",
        "facility": "Rio-Antirrio Köprüsü",
        "message": "This route crosses \"Rio-Antirrio Köprüsü\", which needs more vehicle detail to price accurately for your vehicle class. Its fee is NOT included in toplam_genel_tl below. Send it via vehicle_profile and retry.",
        "field_needed": "yukseklik",
        "options": ["<=2m", ">2m"]
      }
    ]
  }
}

In this example the bridge's fee is simply missing from toplam_genel_tl until you resend the request with, e.g., "vehicle_profile": {"height_m": 3.9} (or whichever field field_needed names).

worldtolls' gateway builds the route between from and to for you server-side — you only ever send two coordinate pairs, never a full route geometry/polyline. If your own app already has a detailed multi-point itinerary and you want the cost priced along that exact path rather than the shortest route we compute, email us — that's a more advanced integration than most customers need, and we can help set it up.

POST /toll/multi

Same idea as /toll, but for a route with one or more stops in between — send an ordered array of 3 or more points (start, one or more stops, destination) and each consecutive pair is priced as its own leg, then summed into one total. Useful for delivery routes, multi-city trips, or any itinerary you build one stop at a time. Available on every plan, no separate "multi-stop" add-on needed.

cURL

curl -X POST https://worldtolls.com/gateway/toll/multi \
  -H "X-API-Key: wtk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "waypoints": [
      { "lat": 41.0082, "lon": 28.9784 },
      { "lat": 40.1885, "lon": 29.0610 },
      { "lat": 39.9334, "lon": 32.8597 }
    ],
    "vehicle_class": 1
  }'

Python (requests)

resp = requests.post(
    "https://worldtolls.com/gateway/toll/multi",
    headers={"X-API-Key": os.environ["WORLDTOLLS_API_KEY"]},
    json={
        "waypoints": [
            {"lat": 41.0082, "lon": 28.9784},
            {"lat": 40.1885, "lon": 29.0610},
            {"lat": 39.9334, "lon": 32.8597},
        ],
        "vehicle_class": 1,
    },
    timeout=10,
)
data = resp.json()
{
  "ok": true,
  "result": {
    "leg_count": 2,
    "total_distance_km": 471.8,
    "toplam_genel_tl": 238.10,
    "eksiksiz": true,
    "dikkat": [],
    "legs": [ { "mesafe_km": 95.4, "toplam_genel_tl": 40.00, ... }, { ... } ]
  }
}
FieldMeaning
leg_countNumber of legs priced (waypoints.length − 1)
total_distance_kmSum of all legs' distance
toplam_genel_tlGrand total across all legs and all countries your plan covers
eksiksiztrue only if every leg was fully priced with nothing left out — see the same field under POST /toll.
dikkatEvery leg's dikkat items merged into one array, each tagged with a leg index (0-based) so you know which leg it came from.
legsOne entry per leg, each shaped exactly like a single /toll result (every field in the full reference below, including its own dikkat/eksiksiz)

vehicle_profile is accepted on /toll/multi exactly like on /toll — send it once at the top level and it's applied to every leg.

Full response field reference

Every field POST /toll can return (a /toll/multi leg is shaped identically). Field names are in Turkish — worldtolls is built for both Turkish and international customers and the calculation engine underneath predates the English docs — but every field's meaning and unit is given here in full, so you never have to guess. Fields not listed weren't found in any real response and shouldn't be relied on.

Top level (result)

FieldTypeMeaning
mesafe_kmnumberTotal route distance, km
sure_dknumberEstimated driving duration, minutes
rotaarraySimplified route polyline used for the calculation, as [longitude, latitude] pairs — see the callout below, the order is the opposite of the {lat, lon} objects you send in the request.
giselerarrayTurkish toll gate/bridge crossings — see the giseler[] table below
toplamnumber (TRY)Turkey-only toll total
yurtdisiobject | absentGreece leg — see yurtdisi table below. Absent if the route doesn't enter Greece or your plan doesn't cover GR.
bulgaristanobject | absentBulgaria leg — see table below
arnavutlukobject | absentAlbania leg — see table below
makedonyaobject | absentNorth Macedonia leg — see table below
sirbistanobject | absentSerbia leg — see table below
karadagobject | absentMontenegro leg — see table below
tesis_gecisleriarrayMulti-category special-facility charges (e.g. the Rio–Antirrio bridge) — a top-level field, not nested inside yurtdisi or any other country object, even when the facility is geographically in that country. See table below.
tesis_toplam_tlnumber (TRY)Sum of tesis_gecisleri[].tl
uyarilararray of stringsRaw Turkish warning sentences for gate pairs that only matched one-sided. This is the internal source that dikkat (tip: "gate_unmatched") is derived from — for a stable, structured, English field to build UI/logic against, prefer dikkat over parsing this array's text.
alternatiflerarrayUp to 2 alternate routes considered, each shaped like the top-level result itself (its own giseler/toplam/yurtdisi/etc.), plus a rozet field ("en_ekonomik" on whichever alternative is cheapest, null otherwise). The route actually used for every other top-level field is always the cheapest candidate.
tr_izin_yoktrue | absentInternal implementation detail describing worldtolls' own central API key, not your plan's Turkey access (that's governed separately by izinli_ulkeler). In practice this is effectively always absent for gateway customers — safe to ignore.
toplam_genel_tlnumber (TRY)Grand total across every country your plan covers, added by the gateway
izinli_ulkelerarray of stringsYour key's authorized country codes, added by the gateway
eksiksizbooleanAdded by the gateway — see POST /toll above
dikkatarrayAdded by the gateway — see POST /toll above
⚠️ rota coordinate order. Every coordinate you send in a request is {"lat": ..., "lon": ...}. The rota array in the response is the opposite: each point is [longitude, latitude] (this matches the GeoJSON/most mapping-library convention, which is why it's shaped that way, but it's easy to plot backwards if you're not expecting the flip). Double-check this if you're drawing the route on a map.

giseler[] item — Turkish toll gate/bridge crossing

FieldTypeMeaning
tipstring enum"GIRIS_CIKIS" (entry+exit gate pair), "TEK_NOKTA" (single-point bridge/tunnel toll), "eksik" (a known gate only matched one side of its pair — usually a start/end point dropped mid-road), "TARIFE_YOK" (no tariff defined for this crossing)
otoyolstringHighway/road name
giris_gisestringEntry gate name (the only gate name for TEK_NOKTA)
cikis_gisestring | nullExit gate name; null except for GIRIS_CIKIS
ucretnumber (TRY) | nullToll fee for this crossing; null when tip is "eksik" or "TARIFE_YOK"
aciklamastringTariff-rule description text
notstringHuman-readable explanation — only present on tip: "eksik" items

yurtdisi — Greece (GR)

Present only if the route enters Greece and your plan includes GR.

FieldTypeMeaning
toplam_normalnumber (EUR)Total at the normal (cash) rate
toplam_passnumber (EUR)Total at the transponder/pass rate
para_birimistringAlways "EUR"
pass_farki_varbooleanWhether the normal and pass rates differ anywhere on this route
gecislerarrayEach crossing: otoyol, tip ("single" | "cift" | "eksik"), giris/cikis (string | null), normal/pass (number, EUR — 0.0 on "eksik"), not (string, only on "eksik")
toplam_tlnumber (TRY)toplam_normal converted with eur_kur — see Currency conversion
eur_kurnumberEUR→TRY rate used for this conversion
kullanilan_kategoriinteger 1–4Greek vehicle category actually used
otomatik_kategoriinteger 1–4What the category would be from vehicle_class alone, before any override

bulgaristan — Bulgaria (BG)

Present only if the route enters Bulgaria's tolled network and your plan includes BG. Absent for vehicle_class: 6 (motorcycle — exempt). Has two different shapes depending on tip: heavy vehicles use distance-based tolling, everyone else uses a flat e-vignette.

tip: "toll" (heavy vehicle, distance-based e-toll)

FieldTypeMeaning
detayarrayPer road-class breakdown: {yol_adi, km, birim_eur, tutar_eur}
toplam_eurnumberTotal, EUR
toplam_tlnumber (TRY)Total converted with eur_kur
toll_kmnumberTolled kilometres covered
ucretsiz_kmnumberToll-free kilometres covered (present on the primary calculation path only)
dagilimobject{AM, I, II} — kilometres split by road class (motorway / class I / class II)
arac_tipistringBulgarian heavy-vehicle category used (e.g. kamyon_12_4aks)
emisyonstringEmission class used
co2_sinifiinteger 1–5CO₂ sub-class used
eur_kurnumberEUR→TRY rate used
kaynakstringWhich internal calculation path produced this result ("wayid", "segment" or "eski_motor") — informational, doesn't affect the fee

tip: "vinyet" (light vehicle, flat e-vignette)

FieldTypeMeaning
paketlerarrayEvery active vignette package: {paket, ad, aciklama, fiyat_eur, fiyat_bgn}
varsayilan_paketstringWhich paket code is pre-selected by default (weekend package if the trip falls Fri 12:00–Sun 23:59, otherwise daily)
toplam_tlnumber (TRY)TRY value of the default package only — if your own UI lets the end user pick a different package from paketler, compute that package's own total from its fiyat_eur and eur_kur rather than using this field
eur_kurnumberEUR→TRY rate used
mesajstringFixed advisory sentence (Turkish)

arnavutluk — Albania (AL)

FieldTypeMeaning
toplam_eurnumberTotal, EUR
toplam_leknumberTotal, Albanian Lek
para_birimistringAlways "EUR"
gecislerarrayEach: otoyol, tip (single|cift|eksik), giris/cikis, eur, lek, kategori (integer), sema (integer 4 or 5 — which category scheme applies; a route can mix both if it crosses a multi-bracket facility like the Kalimash or Llogara tunnels alongside standard main-road gates), not (on eksik)
toplam_tlnumber (TRY)EUR-based conversion; falls back to a Lek-based conversion if the EUR total is unavailable
eur_kur / lek_kurnumberEUR→TRY and Lek→TRY rates used
kullanilan_kategori / kategori_semaintegerCategory and scheme of the first crossing on the route — check each gecisler[] item's own kategori/sema for a route that mixes schemes

makedonya — North Macedonia (MK)

FieldTypeMeaning
toplam_eurnumberTotal, EUR
toplam_denarnumberTotal, Macedonian Denar
para_birimistringAlways "EUR"
gecislerarrayotoyol, tip, giris/cikis, eur, denar, kategori, not (on eksik)
toplam_tlnumber (TRY)EUR-based conversion, Denar-based fallback
eur_kur / denar_kurnumberRates used
kullanilan_kategoriintegerCategory of the first crossing

sirbistan — Serbia (RS)

FieldTypeMeaning
toplam_eurnumberTotal, EUR (EUR is Serbia's authoritative tariff currency)
toplam_rsdnumberTotal, Serbian Dinar (fallback source only)
para_birimistringAlways "EUR"
gecislerarrayotoyol, tip (cift|single|eksik), giris/cikis, eur, rsd, kategori, not
toplam_tlnumber (TRY)EUR-based conversion, RSD-based fallback
eur_kur / rsd_kurnumberRates used
kullanilan_kategoriintegerCategory of the first crossing

karadag — Montenegro (ME)

Montenegro's tariffs are EUR-native — unlike AL/MK/RS there's no local-currency field.

FieldTypeMeaning
toplam_eurnumberTotal, EUR
para_birimistringAlways "EUR"
gecislerarrayotoyol, tip, giris/cikis, eur, kategori, not
toplam_tlnumber (TRY)EUR-based conversion
eur_kurnumberRate used
kullanilan_kategoriintegerCategory of the first crossing

tesis_gecisleri[] item — multi-category special facility

E.g. Greece's Rio–Antirrio bridge. Top-level, not nested in any country object — see the note in the top-level table above.

FieldTypeMeaning
tesis_idintegerFacility ID
tesis_adistringFacility name
kategoristringMatched category's display name
kategori_nointeger | nullMatched category's numeric index, if any
ucretnumberFee in the facility's native currency
para_birimistringNative currency code (e.g. EUR)
tlnumber (TRY)Converted amount

A facility that can't yet be auto-resolved for your vehicle appears in dikkat (tip: "vehicle_profile_needed") instead — see POST /toll above and Vehicle profile.

Currency conversion

Every non-TRY total in the response (toplam_tl fields, and by extension toplam_genel_tl) is converted to TRY server-side, using the rate returned alongside it (eur_kur, lek_kur, denar_kur, rsd_kur) — you don't need your own exchange-rate source to get a TRY total, but if you show the original-currency amount too (e.g. toplam_eur), use the accompanying rate field for consistency rather than a rate from elsewhere.

Rates are refreshed roughly once a day, not on every request — they're not a live tick-by-tick feed. EUR/USD/GBP-class rates come from the Turkish Central Bank's daily bulletin; Albanian Lek, Macedonian Denar and Serbian Dinar (not published by the Central Bank) are derived as a cross-rate through a secondary source. If you need the precise rate used for a specific calculation for reconciliation purposes, read it directly from that response's own *_kur field rather than looking it up separately — it's the exact figure that produced the TRY total you received.

Errors

Every error response has the shape { "ok": false, "error": "...", "message": "..." }.

HTTPerrorMeaning
401invalid_or_missing_api_keyMissing/incorrect X-API-Key header
429rate_limit_exceededHourly or monthly limit reached
400invalid_points / invalid_waypointsMissing or non-numeric coordinates
400invalid_vehicle_profilevehicle_profile was sent but isn't an object
400invalid_bg_category / invalid_bg_emission_class / invalid_bg_co2_classOne of the Bulgaria vehicle_profile fields has a value outside its enum — response includes valid_values
400calculation_failedThe route couldn't be calculated (see message)
502upstream_unavailableTemporary issue reaching the calculation engine — retry shortly

Recommended integration flow

  1. Request access from My account and wait for approval — your wtk_... key appears there automatically once approved.
  2. Call GET /status first (no key needed) to confirm you can reach the gateway from your network/server before wiring up authentication.
  3. Call POST /toll with a route you already know the answer for (e.g. a short domestic trip) and sanity-check the response shape and totals against what you expect.
  4. Handle the error shapes from the Errors table explicitly — at minimum 401 (bad key), 429 (back off and retry later), and 502 (transient, safe to retry with backoff).
  5. Check eksiksiz/dikkat on every response rather than only reading toplam_genel_tl — see POST /toll.
  6. Switch to real traffic, and keep an eye on My account for your live hourly/monthly usage so you can see a plan upgrade coming before you hit 429s.

Frequently asked questions

What happens if my route doesn't touch a tolled road at all?
You still get a normal 200 response — the relevant totals are just 0.

Is there a free/sandbox key for testing?
The Trial plan (see Pricing) is meant for exactly this — a real key with a small country selection and a low request limit, no separate sandbox mode.

Can I request a country that isn't in my plan just to see what happens?
Yes — it's not an error. The response simply won't include that country's section (see POST /toll above), so it's safe to always send the full trip and let the gateway filter it.

Does upgrading or changing my plan change my existing key?
No — your wtk_... key stays the same; only your limits and country access change once an admin approves the new plan.

My plan changed but I still see the old limits — why?
A plan or country change takes effect once our team approves the request in the panel, not the moment you submit it — check My account for the current status.

Can toplam_genel_tl ever be missing a fee without me knowing?
No — check eksiksiz on every response. It's false whenever anything (a multi-category structure that needed more vehicle_profile detail, or a gate pair that didn't match) was left out of the total, and dikkat tells you exactly what and why. When eksiksiz is true, the total is complete.

Do I have to send vehicle_profile on every request?
No. It's entirely optional — omit it and the engine uses its existing safe defaults for your vehicle_class, exactly like before this field was added. Only send it when you actually know the detail (e.g. a fleet system that already tracks vehicle height/seats), or when a previous response's dikkat asked for it.

Why are the response field names Turkish (toplam, giseler, mesafe_km) when the docs are in English?
worldtolls is built for both Turkish and international integrators, and the calculation engine's field names predate this English documentation. Rather than adding a translation layer that could drift out of sync with the real API, the full field reference above documents every Turkish field name's exact English meaning and unit — treat that table as the authoritative glossary.

Can I get the route as a polyline to draw on a map?
Yes — result.rota. Note its coordinate order is [longitude, latitude], the opposite of the request format — see the callout in the field reference.

Need a higher limit, more countries, or help integrating? Email info@worldtolls.com.