ÜcretliYol API iki farklı çalışma modelini destekler: (1) hazır rotayı
ücretlendirme — rotayı siz oluşturursunuz, bize geometriyi ve araç
bilgisini gönderirsiniz, biz otoyol/köprü/geçiş ücretini döneriz
(POST /toll); (2) rota çizerek hesaplama — yalnızca
başlangıç/varış noktası verirsiniz, rotayı sunucu tarafında biz çizeriz
(POST /route). Hangi endpoint'i kullanırsanız kullanın, ücret
her zaman gönderdiğiniz veya bizim çizdiğimiz gerçek güzergâh
üzerinden hesaplanır. POST /toll bugün 7 ülkenin tümünü
kapsar: Türkiye, Yunanistan, Bulgaristan, Arnavutluk, Kuzey Makedonya,
Sırbistan ve Karadağ — hepsi aynı basit istek/yanıt modelinde.
POST /classify de aynı 7 ülkenin tümü için sınıf üretir.
API anahtarı nasıl alınır? Üye girişi yapıp
API Hizmeti sekmesinden talep oluşturun.
Erişiminiz açıldıktan sonra kendi anahtarınızı oluşturabilirsiniz.
14 günlük deneme sonrası ücretli pakete geçmek için bizimle iletişime geçin.
📌 Sürüm notu (08.09.2026):POST /toll artık Arnavutluk
(AL), Kuzey Makedonya (MK) ve Sırbistan (RS) için de gerçek ücret üretir —
önceden yalnızca BG/GR/TR desteklenirdi. API Pro ve Enterprise paketleri
artık varsayılan olarak tüm ülkelere erişebilir (bkz. Limitler
bölümü). Entegrasyonunuzu sabit bir ülke listesine göre yazdıysanız,
GET /countries'i kontrol ederek yeni ülkeleri otomatik
yakalayabilirsiniz.
📌 Sürüm notu (10.09.2026): Karadağ (ME) eklendi. POST /toll,
/site-hesapla ve /site-maliyet Karadağ için gerçek
ücret üretir — ayrıntı için aşağıdaki "KARADAĞ" bölümüne bakın.
GET /countries'te status: "development" olarak
görünür (bu, canlı rota testiyle doğrulanana kadar geçici bir etikettir —
capabilities.toll: true olduğu için uç nokta bugün de
kullanılabilir).
📌 Sürüm notu (11.09.2026):POST /classify artık Karadağ
(ME) için de sınıf üretir — Karadağ'ın resmi 5 kademeli tarife kategorisini
(ME_CAT_1..ME_CAT_5) fiziksel araç profilinizden
hesaplar (bkz. aşağıdaki "Sınıflandırma özeti" tablosu). GET
/countries'te artık ME için de capabilities.classify: true
görünür.
📌 Sürüm notu (11.09.2026):POST /toll'da Arnavutluk (AL),
Kuzey Makedonya (MK), Sırbistan (RS) ve Karadağ (ME) için sinif
alanı artık koşullu zorunlu — geçerli bir ülke-kendi kategorisi
(me_kategori / rs_kategori / mk_kategori,
Arnavutluk'ta al_kategori VE al_kategori5 ikisi
birden) gönderdiğinizde sinif hiç göndermenize gerek yok. Sadece
sinif gönderen mevcut entegrasyonlar hiç etkilenmez.
Kimlik Doğrulama
Her istekte API anahtarınızı X-API-Key başlığında gönderin:
Anahtarınız yalnızca talep sırasında seçtiğiniz (veya admin tarafından
onaylanan) ülkeler için geçerlidir. İzinsiz ülke için istek atarsanız
ulke_izni_yok hatası alırsınız. Anahtarınız ayrıca aktif
olmalı, süresi dolmamış olmalı ve saatlik/günlük/aylık kullanım
limitlerini aşmamış olmalıdır.
Bir hesapta yalnızca tek bir aktif anahtar bulunabilir. Panelden
"Yeni Anahtar Oluştur" dediğinizde önceki anahtarınız hemen geçersiz olur ve
yeni bir anahtar üretilir — bu bir anahtar yenileme işlemidir, sıfırdan
başlamak değildir. Kalan saatlik/günlük/aylık kullanım hakkınız kaybolmaz:
limitler anahtar bazında değil hesap bazında sayılır, bu yüzden yenileme
kullanım sayaçlarınızı sıfırlamaz. Anahtarınızı iptal ederseniz hem panelde
hem sunucuda anında devre dışı kalır.
⚠️ Yalnızca sunucudan sunucuya kullanım için tasarlanmıştır. API
anahtarınızı tarayıcıda/mobil istemcide açığa çıkmayacak şekilde kendi
sunucunuzda saklayın ve isteği oradan atın. Ayrıca CORS (tarayıcı
Origin) yalnızca ucretliyol.com için açıktır — kendi domaininizden doğrudan
tarayıcı JavaScript'i ile çağırmayı planlıyorsanız CORS hatası alırsınız;
isteği kendi backend'inizden atın.
Uç Noktalar
Aşağıdaki iki uç nokta grubu için ana adres:
Ana adres
https://api.ucretliyol.com
Format
JSON (istek ve yanıt, application/json; charset=utf-8)
Anahtar gerektirmeyen uç noktalar
Uç nokta
Yöntem
Açıklama
/
GET
API adı, sürümü ve endpoint listesi
/status
GET
Veritabanı ve rota servisinin sağlık durumu
/countries
GET
Ülke listesi ve her ülkenin hangi uç noktaları desteklediği (aşağıda detaylı)
API anahtarı gerektiren uç noktalar
Uç nokta
Yöntem
Kullanım
/toll
POST
Gönderdiğiniz rotayı ücretlendirir — bu dokümanın odağı. TR, GR, BG, AL, MK, RS, ME
/route
POST
İki nokta arasında rota çizer, Türkiye (ve rota Yunanistan'a giriyorsa Yunanistan) ücretini döner
/classify
POST
Tek bir fiziksel araç profilinden ülkelerin kendi sınıf karşılıklarını üretir
/site-hesapla ve /site-maliyet uç noktaları da
teknik olarak aynı anahtarla çalışır (yakıt/lojistik maliyeti ve tüm
ülkeleri tek istekte birden dönen zengin site ekranı için), ancak bunlar
genel üçüncü taraf entegrasyonu için tasarlanmamıştır ve önceden haber
verilmeden değişebilir. Artık /toll tek başına 7 ülkenin
tümünü kapsadığı için kendi entegrasyonunuz için /toll,
/route ve /classify kullanmanızı öneririz.
GET /countries — ülke ve yetenek listesi
Anahtar gerektirmez. Her ülke için genel olgunluk durumunu (status)
ve o an gerçekten kullanılabilir uç noktaları (capabilities) döner.
status: "development" o ülkenin hiç çalışmadığı anlamına
gelmez — bir ülkenin bazı yetenekleri true iken durumu hâlâ
"development" olabilir; hangi uç noktanın gerçekten kullanılabilir
olduğuna her zaman capabilities alanına bakarak karar verin.
true ise o ülke POST /toll tarafından kabul edilir
capabilities.classify
true ise POST /classify çıktısında o ülke için sınıf üretilebilir
capabilities.site_hesapla / site_maliyet
ÜcretliYol'un kendi iç formatlarında bu ülke için gerçek ücret üretildiği bilgisi (üçüncü taraf entegrasyonu için referans değildir)
planned
Bu ülkede şu an hiçbiri aktif olmayan, ileride eklenmesi planlanan yetenekler için kullanılan opsiyonel bir alan (bugün 7 ülkenin de tüm yetenekleri aktif olduğundan şu an hiçbir ülkede görünmez — yeni bir ülke eklendiğinde canlıya alınana kadar bu alan üzerinden görünür olabilir)
Bu listeyi kodunuza sabit yazmak yerine /countries'i periyodik
çağırıp kontrol etmenizi öneririz — yeni bir ülke eklendiğinde entegrasyonunuzu
değiştirmeden fark edebilirsiniz. Romanya, Hırvatistan, Macaristan ve
Bosna-Hersek üzerinde çalışıyoruz/yol haritamızdadır — bu ülkeler canlıya
alındığında yine önce burada, capabilities.toll:true ile
görünecektir.
GET /status — sağlık kontrolü
Anahtar gerektirmez. İzleme/uptime araçlarınızda kullanabileceğiniz basit bir
sağlık uç noktasıdır; veritabanı ve rota servisine (Valhalla) erişilebildiğini
doğrular.
GET /status
{ "status": "ok", "database": true, "valhalla": true }
database her zaman
true'dur (veritabanına erişilemiyorsa API zaten 503
ile başka hiçbir isteği kabul etmez — bu yüzden /status'a kadar
gelebiliyorsanız veritabanı zaten çalışıyordur). valhalla:false
ise rota servisi geçici olarak yanıt vermiyor demektir; bu durumda
/route, /site-hesapla ve /site-maliyet
gibi sunucunun rota ÇİZDİĞİ uç noktalar etkilenebilir — /toll
rota çizmediği için (siz gönderirsiniz) bundan etkilenmez.
Rota Gönderimi
Rotanızı iki biçimde gönderebilirsiniz:
koordinatlar(önerilen)
Rota noktalarınız [boylam, enlem] sırasıyla. Önce boylam!
Google/Valhalla algoritmasıyla aynı yöntemle üretilmiş, precision 6
(1e6) encoded polyline.
"geometri": "quwdI..._encoded_polyline_p6_..."
⚠️ Google Directions/Maps API'sinden aldığınız overview_polyline
veya steps[].polyline değerini doğrudan geometri
alanına yapıştırmayın. Google'ın kendi API'si polyline'ı varsayılan olarak
precision 5 (1e5) ile kodlar; bu API ise precision 6 (1e6)
bekler. Precision uyuşmazlığı sessizce yanlış (tamamen alakasız) koordinatlara
çözülür — hata vermez, sadece rota yanlış yerde eşleşir. Google'dan gelen bir
rotayı kullanacaksanız ya kendi tarafınızda precision 6 ile yeniden kodlayın
ya da (çok daha güvenli ve basit) rota noktalarını çözüp
koordinatlar dizisi olarak gönderin — precision sorununu
tamamen ortadan kaldırır.
Koordinatlar WGS84 ondalık derece formatındadır ve rota en az iki nokta
içermelidir. Rotanızı ne kadar sık noktayla gönderirseniz hesap o kadar
doğru olur — ideal: yaklaşık her 50–100 metrede bir nokta. Sistem
gişe/segment kesişimini yakınlık toleransı OLMADAN, tam geometrik kesişimle
arar; çok seyrek bir rota bir gişeyi veya köprü hattını kaçırabilir.
⚠️ koordinatlar ve geometri aynı istekte birlikte
gönderilirse ülkeye göre hangisinin kullanılacağı değişebilir (Bulgaristan
için gönderilen geometri, diğer ülkeler için koordinatlar
kullanılır). İki alan aynı rotayı temsil etmiyorsa farklı ülkeler farklı
sonuç üretebilir — tek istekte yalnızca birini göndermeniz en güvenlisidir.
Ondalık sayılarda JSON standardına uygun olarak nokta kullanın
("co2_sinifi": 1, 380.5 gibi); virgüllü ondalık
string'ler ("380,5") güvenilir bir sözleşme değildir.
Kendi uygulamanızda oluşturduğunuz rotayı nasıl iletmelisiniz?
Firmanızın kendi navigasyon/harita altyapısı zaten bir rota üretiyorsa (Google
Directions, Mapbox Directions, HERE, kendi OSRM/Valhalla sunucunuz, bir filo
takip sisteminin GPS kayıtları vb.), POST /toll tam olarak bunun
için tasarlanmıştır — API kendi rotasını ÇİZMEZ, sizin gönderdiğiniz
güzergâhı ücretlendirir. Yapmanız gereken:
Kendi rota kaynağınızdan (harita SDK'sı, yön API'si, GPS logu) rotanın
izlediği gerçek yolu temsil eden, sıralı nokta listesini çıkarın —
yalnızca başlangıç ve varış noktasını değil, aradaki tüm güzergâhı.
Örneğin Google Directions'ın overview_polyline'ını
çözüp (decode) elde ettiğiniz [enlem, boylam] noktalarını
alın.
Her noktayı [boylam, enlem] sırasına çevirin (çoğu harita SDK'sı
[enlem, boylam] sırasıyla verir — ÜcretliYol'un beklediği
sıra bunun tersidir, GeoJSON standardıyla aynıdır).
Bu diziyi koordinatlar alanına, ücretini almak istediğiniz
ülkenin araç bilgileriyle birlikte POST /toll'a gönderin.
Rota birden fazla ülkeden geçiyorsa (ör. Türkiye'den çıkıp Yunanistan'a
giren bir güzergâh), aynı koordinat dizisini her ülke için ayrı bir
istekte, o ülkenin kendi ulke ve arac
alanlarıyla gönderin — /toll tek istekte yalnızca tek bir
ülkeyi ücretlendirir.
Kendi rotanız yoksa (yalnızca başlangıç/varış noktanız varsa) rotayı sizin
yerinize sunucu tarafında biz çizeriz — bkz. aşağıdaki
POST /route bölümü. Rota SİZDE varsa /toll,
rota SİZDE yoksa /route kullanın; ikisini karıştırmayın.
Fiziksel Araç Profili
Ülkeler arasında tutarlı entegrasyon için sisteminizde aracın fiziksel
bilgilerini tek bir profil olarak saklamanızı öneririz — bu profil
özellikle /classify uç noktasında kullanılır:
Alan
Tip
Açıklama
vehicle_type
string
motorcycle, car, van, bus, truck
axle_count
integer
Toplam aks/dingil sayısı
front_axle_height_cm
number
Ön aks yüksekliği (cm) — toplam araç yüksekliğiyle karıştırılmamalıdır
total_height_cm
number
Toplam araç yüksekliği (cm)
max_weight_tons
number
Azami ağırlık (ton)
seat_count
integer
Otobüs/minibüs ayrımı için koltuk sayısı
emission_class
string
euro_3, euro_4, euro_5, euro_6 gibi
Önemli kural: Ücret sınıfını etkileyen bir fiziksel bilgiyi
bilmiyorsanız tahmini bir değer göndermeyin — alanı boş bırakın ve
gerekiyorsa kullanıcıdan isteyin. Bir ülkenin sınıf numarasını (örn.
Türkiye sinif) başka bir ülkeye doğrudan kopyalamayın; her
ülkenin kendi sınıflandırma mantığı vardır (bkz. aşağıda).
Para Birimi ve Döviz Kuru
Sonuç hangi para biriminde döner, bir ülkeden diğerine değişir. Aşağıdaki
tablo hangi ülkenin yanıtında hangi para birimi alan(lar)ının bulunduğunu
gösterir — bir "para birimi seçme" parametresi yoktur, her ülke
kendi motorunun ürettiği alanların TAMAMINI her zaman döner; hangisini
kullanacağınıza siz karar verirsiniz.
Ülke
Yerel para birimi
Yanıttaki alan(lar)
TL karşılığı
Türkiye (TR)
TRY
toll.try
zaten TL, ayrı alan yok
Yunanistan (GR)
EUR
toll.eur
toll.tl
Bulgaristan (BG)
EUR
toll.eur
toll.tl
Arnavutluk (AL)
ALL (Lek)
toll.eurvetoll.lek (ikisi birden, her zaman)
toll.tl
Kuzey Makedonya (MK)
MKD (Denar)
toll.eurvetoll.denar (ikisi birden, her zaman)
toll.tl
Sırbistan (RS)
RSD (Dinar)
toll.eurvetoll.rsd (ikisi birden, her zaman)
toll.tl
Karadağ (ME)
EUR
toll.eur
toll.tl
EUR istemiyorsanız, sadece yerel para biriminde tutar mı istiyorsunuz?
Arnavutluk, Kuzey Makedonya ve Sırbistan için yanıt her zaman hem
EUR (toll.eur) hem de o ülkenin kendi yerel para birimini
(toll.lek / toll.denar / toll.rsd)
içerir — ekstra bir parametre göndermenize gerek yoktur, sadece
ihtiyacınız olan alanı okuyun. Yunanistan, Bulgaristan ve Karadağ'da
(Karadağ resmen euroize edilmiş bir ülkedir) yerel para birimi zaten EUR
olduğu için ayrı bir "yerel para birimi" alanı yoktur (toll.eur
her üçü için de zaten yereldir). Türkiye'de sonuç zaten TL'dir.
⚠️ Bir geçiş/tesis için eur alanı 0 ama yerel para
birimi alanı 0'dan büyükse — bu bir hata değildir, o spesifik
gişe/tesisin EUR tarifesi henüz veri tabanımıza girilmemiş demektir; o
kayıt için yerel para birimi alanı (lek/denar/
rsd) geçerli/yetkili değerdir, EUR'u değil onu kullanın.
Bu durumu fark ederseniz bize bildirin, ilgili
tarife satırını tamamlarız.
Döviz kuru alanları (eur_kur, lek_kur,
denar_kur, rsd_kur) o spesifik yanıtta TL
karşılığını hesaplamak için kullanılan güncel kuru gösterir. Kur, Türkiye
Cumhuriyet Merkez Bankası günlük bülteninden (EUR ve diğer majör para
birimleri için) ve tamamlayıcı bir kaynaktan (ALL/MKD/RSD gibi TCMB
bülteninde yer almayan para birimleri için çapraz kur olarak) periyodik
güncellenir; taze bir kur alınamazsa sistem son bilinen geçerli kuru
kullanır — tl alanı bu yüzden neredeyse hiçbir zaman boş
kalmaz. Kendi tarafınızda kur önbelleklemeyin — her yanıtın kendi
*_kur alanını okuyun, farklı isteklerde kur küçük farklarla
değişebilir.
Ülkeye Göre Zorunlu Bilgiler ve Yanıt Alanları
POST /toll için her ülkenin ücret mantığı farklıdır. Hangi
ülke için hesap istiyorsanız, o ülkenin gerektirdiği araç bilgilerini
göndermelisiniz. Eksik bilgi gönderirseniz API hesaplama yapmaz; hangi
alanın eksik olduğunu ve geçerli değerleri döner — böylece yanlış ücret
hesaplanmaz.
BGBulgaristan — mesafe bazlı ücretli yol (ağır araç)
Ücret, aracın kat ettiği gerçek yol mesafesine ve emisyon sınıfına göre hesaplanır.
1–4 (yalnız euro_vi_eev'de fark eder); zev için motor otomatik 5 kullanır; eksik/aralık dışı değer 1'e çekilir
Tonaj/aks bilgisi kategoriye gömülüdür — ayrıca göndermeyin. Kategori isimleri metin olarak birebir gönderilmelidir; Türkiye sinif numarası Bulgaristan'a gönderilmez.
Hesaplanan geçiş ücreti (EUR ve güncel kurla TL karşılığı)
toll.toll_km / toll.ucretsiz_km
Ücretli sınıflı yol ve ücretsiz yol kilometresi
toll.dagilim_km
Otoyol (AM/FRC0), FRC1, FRC2 kilometre kırılımı
toll.birim_eur_km
Sabit sırayla [AM/FRC0, FRC1, FRC2] birim fiyat dizisi
eur_kur
TL karşılığında kullanılan güncel EUR kuru
kaynak
Genellikle wayid; servis erişilemezse segment veya eski_motor olabilir
Binek araçlar ve motosikletler için /toll BG'de yukarıdaki ağır araç kategorilerini kabul eder; binek araç vinyet modeli bu genel API sözleşmesinin bir parçası değildir.
GRYunanistan — Otoyol + Köprü
Otoyol geçişleri için kategori zorunludur. Rota, çok kategorili özel bir tesisten (ör. Rio–Antirrio köprüsü) geçiyorsa, doğru kategoriyi belirleyebilmemiz için ek araç bilgileri gerekebilir.
binek, motosiklet, kamyon, otobus — tesis veritabanının kanonik tipi
koltuk_sayisi
Otobüs/minibüs ise
Gerçek koltuk sayısı — köprü ücreti buna göre değişebilir
aks_sayisi
Kamyon ise
2, 3, 4, 5…
yukseklik_cm
Gerekebilir
Aracın toplam yüksekliği, santimetre (API içeride metreye çevirir — 3.80 göndermek "3,80 cm" olarak yorumlanır)
romork_var
Gerekebilir
0 / 1
engelli
Opsiyonel
true / false — binek araç için engelli/mavi kart kategorisi talep eder
arac_tipi gönderilmezse sistem Türkiye sınıfından veya aks sayısından kaba bir tahmin üretir; doğru tesis kategorisi için açıkça göndermeniz önerilir.
// Otoyol isteği
{
"ulke": "GR",
"koordinatlar": [[21.3200, 38.3200], [21.7800, 38.2500]],
"arac": { "kategori": 3 }
}
// Özel tesis (köprü) için tam araç profili
{
"ulke": "GR",
"koordinatlar": [[21.7600, 38.3000], [21.7800, 38.2500]],
"arac": {
"kategori": 3, "arac_tipi": "otobus", "koltuk_sayisi": 50,
"aks_sayisi": 2, "yukseklik_cm": 380, "romork_var": 0
}
}
⚠️ Önemli — Yunanistan köprüleri: Rio–Antirrio gibi bazı köprülerde
ücret 9'a varan kategoriye ayrılır (araç tipi, koltuk sayısı, aks,
yükseklik, römork durumuna göre) — bu liste tesis veritabanı içeriğine
bağlıdır ve değişebilir, sabit varsaymayın. Eksik bilgi gönderirseniz
aşağıdaki gibi bir yanıt alırsınız:
secenekler tesis veritabanındaki gerçek aktif kategorilerden üretilir. Akış: (1) eksik alanını kullanıcıya sorun, (2) gerçek fiziksel değeri arac içine koyun, (3) aynı isteği tam profille tekrar gönderin. secenekler[].ucret değerini kendi başınıza toplama eklemeyin — yetkili sonuç başarılı isteğin toll.koprular alanıdır.
Binek (otomobil) araçlar için sistem hiçbir zaman ek soru sormaz:engelli alanını göndermezseniz (veya bilinmiyorsa) otomatik
olarak standart/engelsiz kategori uygulanır; gerçekten engelli/mavi kartlı
bir araç için engelli:true gönderirseniz doğru (indirimli)
kategori seçilir.
ucretnull döner — ücretsiz geçiş olarak yorumlamayın
Türkiye toplamı toll.try alanındadır, para birimi TL'dir.
ALArnavutluk — Gişe/tesis (tek nokta)
Kategori Türkiye sinif'inden türetilir — motor, geçilen her
gişe/tesisin kendi tarife satırının 4'lü mü 5'li şemalı mı olduğunu
(ör. Llogara Tüneli gibi bağımsız tesisler) kendisi tespit edip doğru
kategoriyi seçer. Elle seçim isterseniz al_kategori (1-4)
ve/veya al_kategori5 (1-5) gönderebilirsiniz — ikisini de
geçerli gönderirseniz sinif hiç gerekmez (rota hangi
şemalı tesisten geçerse geçsin doğru kategori bu ikisinden doğrudan
belirlenir; yalnızca biri gönderilirse — rota diğer şemadaki bir
tesisten de geçebileceği için — sinif yine zorunludur).
Alan
Zorunlu
Değerler
sinif
Şartlı*
1 = Otomobil · 2 = Ara sınıf/minibüs · 3 = 2-3 aks · 4 = 4-5 aks · 5 = 6+ aks · 6 = Motosiklet (Türkiye ile aynı sınıflandırma — motor buradan Arnavutluk'un kendi kategorisini türetir)
al_kategori
Opsiyonel
1-4 — tesisin 4'lü şemalı tarifesinde elle kategori seçimi
Toplam ücret; her ikisi de her zaman doludur (bkz. yukarıdaki "Para Birimi ve Döviz Kuru")
toll.tl
EUR varsa ondan, yoksa Lek'ten hesaplanan TL karşılığı
arac.kullanilan_kategori / kategori_sema
Motorun otomatik seçtiği (veya elle override edilen) kategori ve şema — birden fazla geçiş farklı şemadan olabilir, ayrıntı için her gecisler[] öğesinin kendi alanına bakın
eur_kur / lek_kur
TL karşılığında kullanılan güncel kurlar
Ücretli geçiş bulunmayan bir rotada ok:true, toll.eur/lek/tl:0, boş gecisler ve bilgilendirici bir mesaj döner — bu bir hata değildir.
MKKuzey Makedonya — Gişe (tek nokta, tam eşleşme)
Kategori Türkiye sinif'inden türetilir (resmi aks + ön aks
yüksekliği kuralı bugünkü araç profilinde henüz kullanılmıyor). Elle
seçim isterseniz mk_kategori gönderebilirsiniz — geçerli
bir mk_kategori gönderirseniz sinif hiç
gerekmez.
Alan
Zorunlu
Değerler
sinif
Şartlı*
1 = Otomobil · 2 = Ara sınıf/minibüs · 3 = 2-3 aks · 4 = 4-5 aks · 5 = 6+ aks · 6 = Motosiklet
mk_kategori
Opsiyonel
1-5 (sayı) veya resmi Kuzey Makedonya plaka şeması "1a"/"1b"/"2"/"3"/"4" (metin)
* Geçerli bir mk_kategori gönderilirse sinif zorunlu değildir; aksi halde zorunludur.
EUR varsa ondan, yoksa Denar'dan hesaplanan TL karşılığı
eur_kur / denar_kur
TL karşılığında kullanılan güncel kurlar
Ücretli geçiş bulunmayan bir rotada ok:true, toll.eur/denar/tl:0, boş gecisler ve bilgilendirici bir mesaj döner — bu bir hata değildir.
RSSırbistan — Gişe (giriş-çıkış, mesafe bazlı)
Kategori Türkiye sinif'inden türetilir (resmi aks + ön aks
yüksekliği + römork kuralı bugünkü araç profilinde henüz kullanılmıyor).
Elle seçim isterseniz rs_kategori gönderebilirsiniz —
geçerli bir rs_kategori gönderirseniz sinif
hiç gerekmez.
Alan
Zorunlu
Değerler
sinif
Şartlı*
1 = Otomobil · 2 = Ara sınıf/minibüs · 3 = 2-3 aks · 4 = 4-5 aks · 5 = 6+ aks · 6 = Motosiklet
rs_kategori
Opsiyonel
1-5 (sayı) veya resmi Sırbistan plaka şeması "1a"/"1"/"2"/"3"/"4" (metin)
* Geçerli bir rs_kategori gönderilirse sinif zorunlu değildir; aksi halde zorunludur.
Toplam ücret; her ikisi de her zaman doludur. EUR, Sırbistan'ın resmi/güvenilir kaynağıdır
toll.tl
EUR varsa ondan, yoksa RSD'den hesaplanan TL karşılığı
eur_kur / rsd_kur
TL karşılığında kullanılan güncel kurlar
Ücretli geçiş bulunmayan bir rotada ok:true, toll.eur/rsd/tl:0, boş gecisler ve bilgilendirici bir mesaj döner — bu bir hata değildir.
MEKaradağ — Gişe (tek nokta ve giriş-çıkış, karışık model)
Kategori Türkiye sinif'inden türetilir (resmi aks + ön aks
yüksekliği + römork kuralı bugünkü araç profilinde henüz kullanılmıyor).
Elle seçim isterseniz me_kategori gönderebilirsiniz —
geçerli bir me_kategori gönderirseniz sinif
hiç gerekmez. Karadağ
zaten EUR bazlı bir ülke olduğu için yanıtta ayrı bir yerel para birimi
alanı yoktur (bkz. yukarıdaki "Para Birimi ve Döviz Kuru" bölümü).
Alan
Zorunlu
Değerler
sinif
Şartlı*
1 = Otomobil · 2 = Ara sınıf/minibüs · 3 = 2-3 aks · 4 = 4-5 aks · 5 = 6+ aks · 6 = Motosiklet
me_kategori
Opsiyonel
1-5 (sayı veya metin) — Karadağ'ın resmi 5 kademeli tarife kategorisi
* Geçerli bir me_kategori gönderilirse sinif zorunlu değildir; aksi halde zorunludur.
Toplam ücret (EUR) — Karadağ'ın tek ve resmi para birimi
toll.tl
eur_kur ile hesaplanan TL karşılığı
eur_kur
TL karşılığında kullanılan güncel kur
gecisler[].tip
single: tek noktalı geçiş (ör. Sozina Tüneli) · cift: giriş-çıkış zonlu mesafe modeli (ör. Bar-Boljare otoyolunun zonları)
Ücretli geçiş bulunmayan bir rotada ok:true, toll.eur/tl:0, boş gecisler ve bilgilendirici bir mesaj döner — bu bir hata değildir.
AL/MK/RS/ME'de sinif, kategoriyi birebir kopyalamaz —
gönderirseniz motor onu kendi resmi kategori şemasına (Arnavutluk
4'lü/5'li, Kuzey Makedonya 1a/1b/2/3/4, Sırbistan 1a/1/2/3/4, Karadağ 1-5)
otomatik çevirir. Bu bir tahmindir ve gerçek yerel plaka sınıfıyla her
zaman birebir örtüşmeyebilir. Aracınızın gerçek yerel kategorisini
biliyorsanız (ör. filo yönetiminizde zaten kayıtlıysa), doğruluğu artıran
al_kategori / al_kategori5 / mk_kategori /
rs_kategori / me_kategori override alanlarını
kullanın — geçerli bir override gönderdiğinizde sinif hiç
gerekmez (Arnavutluk'ta yalnızca al_kategori VE
al_kategori5 ikisi birden gönderilirse; diğer üç ülkede tek
alan yeterlidir). Böylece o ülkenin kendi kategori sistemini bilen bir
entegrasyon, Türkiye sınıflandırmasını hiç öğrenmek zorunda kalmaz.
POST /classify — evrensel araç sınıflandırma
Yukarıdaki "Fiziksel Araç Profili"ni gönderin, her ülkenin kendi sınıf
karşılığını tek istekte alın. Bu, tek bir araç profilini ülke sınıflarına
dönüştürmek isteyen entegrasyonlar için kullanışlıdır.
classes yalnızca API anahtarınızın izinli olduğu ülkeleri
içerir. durum: "tamam" o ülke için sınıf üretilebildiğini,
eksik listesi doluysa hangi alan(lar) olmadan sonucun
güvenilir olmadığını gösterir.
Ülke
Sınıflandırma özeti
Türkiye
Motosiklet→CLASS_6 · 2 aks + ön aks ≥130cm→CLASS_2 · 2 aks + bilinmiyor/<130cm→CLASS_1 · 3 aks→CLASS_3 · 4-5 aks→CLASS_4 · 6+ aks→CLASS_5
Bulgaristan
Motosiklet→muaf · ≤3.5 ton→binek vinyet · >3.5 ton otobüs→BG_BUS_<EURO> · diğer ağır araç 2/3/4+ aks→BG_TRUCK_<N>AXLE_<EURO>. Emisyon bilinmiyorsa resolver EURO_6 varsayar — ücret hesabı için gerçek emisyonu mutlaka /toll'a açıkça gönderin.
Yunanistan
Motosiklet→GR_CAT_1 · ≤3.5 ton ve yükseklik ≤220cm→GR_CAT_2 · 2-3 aks→GR_CAT_3 · 4+ aks→GR_CAT_4
Arnavutluk
Motosiklet→AL_CAT_1 · otomobil ≤3.5 ton→AL_CAT_2 · van/otobüs ≤23 koltuk→AL_CAT_3 · >23 koltuk veya 2 aks kamyon→AL_CAT_4 · diğer 3+ aks ağır araç→AL_CAT_5
Kuzey Makedonya
Ağırlık ve yükseklik eşiklerinden resmi 1a/1b/2/3/4 plaka şemasına yaklaşık eşleme (net ayrım bilgisi eksikse ağırlık eşikleri kullanılır)
Sırbistan
Ağırlık, yükseklik ve römork bilgisinden resmi 1a/1/2/3/4 plaka şemasına yaklaşık eşleme
Karadağ
Motosiklet→ME_CAT_1 · >3.5 ton, 2-3 aks→ME_CAT_4 · >3.5 ton, 4+ aks→ME_CAT_5 · ≤3.5 ton (veya bilinmiyor), 3+ aks→ME_CAT_3 · ≤3.5 ton, 2 aks, ön aks <130cm (veya toplam yükseklik ≤190cm)→ME_CAT_2 · ≤3.5 ton, 2 aks, ön aks ≥130cm (veya toplam yükseklik >190cm)→ME_CAT_3. Resmi Autoput Bar-Boljare 5 kademeli tarifesiyle birebir aynı kural (bkz. monteput.me/cjenovnik/).
Eksik alanları açıkça göndermek en doğru yöntemdir; geriye dönük uyumluluk için vehicle_type yoksa car, axle_count yoksa/2'den küçükse 2 kabul edilir — bu nedenle eksik profille alınan sonuç fiziksel gerçeği garanti etmez, üretim entegrasyonunuzda sınıflandırma öncesi tam profil zorunlu tutun.
POST /route — iki nokta arasında rota ve Türkiye ücreti
Hazır geometri göndermeden, yalnızca başlangıç/varış vererek rota
oluşturmak ve Türkiye gişe ücretini hesaplamak için kullanılır. Kendi
rotanız yoksa (yalnızca iki koordinatınız varsa) bu endpoint tam size
göre — rotayı Valhalla/OSRM ile biz çizeriz.
vehicle_class 1–6 aralığına sıkıştırılır; gönderilmezse 1 kullanılır. Rota otomobil (auto) profiliyle çizilir — ağır araç güzergah kısıtlarını yansıtmaz, bu yüzden ağır araçlar için gerçek rotanızı kendiniz oluşturup /toll'a göndermeniz daha doğru sonuç verir. Anahtarınızın GR izni varsa ve rota Yunanistan'a giriyorsa yanıta ek olarak bir greece bölümü eklenebilir; anahtarınızın TR izni yoksa tr_izin_yok:true dönebilir. /route yalnızca Türkiye (+opsiyonel Yunanistan) ücretlendirir — Bulgaristan/Arnavutluk/Kuzey Makedonya/Sırbistan için rotanızı kendiniz oluşturup ülke başına ayrı /toll isteği atmanız gerekir.
using System.Net.Http;
using System.Text;
using System.Text.Json;
var client = new HttpClient();
client.DefaultRequestHeaders.Add("X-API-Key", "SIZIN_ANAHTARINIZ");
var govde = new {
ulke = "BG",
koordinatlar = new[] { new[] {27.9624, 43.2180}, new[] {28.5582, 43.7360} },
arac = new { kategori = "kamyon_12_4aks", emisyon = "euro_vi_eev", co2_sinifi = 1 }
};
var icerik = new StringContent(JsonSerializer.Serialize(govde), Encoding.UTF8, "application/json");
var yanit = await client.PostAsync("https://api.ucretliyol.com/toll", icerik);
Console.WriteLine(await yanit.Content.ReadAsStringAsync());
Java: kendi rotanızı gönderen yeniden kullanılabilir bir istemci
Aşağıdaki sınıf, kendi navigasyon/harita altyapınızın (Google Directions,
Mapbox, HERE, kendi OSRM/Valhalla sunucunuz, filo GPS logunuz vb.) ürettiği
rotayı alıp yedi ülkenin herhangi biri için ücretlendiren, yeniden
kullanılabilir bir örnektir. arac haritasını ülkeye göre siz
doldurursunuz (yukarıdaki "Ülkeye Göre Zorunlu Bilgiler" tablolarına bakın).
import org.json.JSONArray;
import org.json.JSONObject;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.List;
import java.util.Map;
public class UcretliYolClient {
private final String baseUrl;
private final String apiKey;
private final HttpClient http = HttpClient.newHttpClient();
public UcretliYolClient(String baseUrl, String apiKey) {
this.baseUrl = baseUrl;
this.apiKey = apiKey;
}
/**
* Kendi uygulamanızda ürettiğiniz rotayı ücretlendirir. "rota" parametresi
* rota üzerindeki NOKTALARIN SIRALI listesidir — yalnızca başlangıç/bitiş
* DEĞİL, rotanın gerçekten izlediği yoldur. Her nokta {boylam, enlem}
* sırasıyla (dikkat: çoğu harita SDK'sı enlem/boylam sırasıyla verir, bu
* API'nin beklediği sıra TERSİDİR).
*
* @param ulke "TR" | "GR" | "BG" | "AL" | "MK" | "RS"
* @param rota Rota noktaları, her eleman {boylam, enlem}
* @param arac Ülkeye göre değişen araç bilgisi (örn. TR icin "sinif",
* BG icin "kategori"+"emisyon")
*/
public JSONObject hesaplaToll(String ulke, List rota, Map arac) throws Exception {
JSONArray koordinatlar = new JSONArray();
for (double[] nokta : rota) {
koordinatlar.put(new JSONArray(new double[]{nokta[0], nokta[1]}));
}
JSONObject govde = new JSONObject()
.put("ulke", ulke)
.put("koordinatlar", koordinatlar)
.put("arac", new JSONObject(arac));
HttpRequest istek = HttpRequest.newBuilder()
.uri(URI.create(baseUrl + "/toll"))
.header("Content-Type", "application/json")
.header("X-API-Key", apiKey)
.POST(HttpRequest.BodyPublishers.ofString(govde.toString()))
.build();
HttpResponse yanit = http.send(istek, HttpResponse.BodyHandlers.ofString());
JSONObject sonuc = new JSONObject(yanit.body());
if (yanit.statusCode() != 200 || !sonuc.optBoolean("ok", false)) {
// 400/401/403/422 gibi durumlarda sonuc icinde "hata"/"eksik"/"gecerli_degerler"
// alanlarini kontrol edin — korlemesine tekrar denemeyin (bkz. HTTP Durum Kodlari).
throw new RuntimeException("ÜcretliYol hatası (" + yanit.statusCode() + "): " + sonuc);
}
return sonuc;
}
}
// ---- Kullanım ----
UcretliYolClient istemci = new UcretliYolClient("https://api.ucretliyol.com", "SIZIN_ANAHTARINIZ");
// Kendi navigasyon SDK'nizden gelen, rotanın izlediği yolu temsil eden noktalar
List rota = List.of(
new double[]{27.9624, 43.2180},
new double[]{28.1000, 43.4000},
new double[]{28.5582, 43.7360}
);
// TR — sinif zorunlu
JSONObject tr = istemci.hesaplaToll("TR", rota, Map.of("sinif", 1));
// GR — kategori zorunlu (1-4)
JSONObject gr = istemci.hesaplaToll("GR", rota, Map.of("kategori", 3));
// BG — kategori + emisyon zorunlu
JSONObject bg = istemci.hesaplaToll("BG", rota, Map.of(
"kategori", "kamyon_12_4aks", "emisyon", "euro_vi_eev", "co2_sinifi", 1));
// AL — sinif zorunlu; yanıt EUR VE Lek'i her zaman birlikte döner
JSONObject al = istemci.hesaplaToll("AL", rota, Map.of("sinif", 3));
System.out.println("Arnavutluk: " + al.getJSONObject("toll").getDouble("eur") + " EUR / "
+ al.getJSONObject("toll").getDouble("lek") + " Lek");
// MK — sinif zorunlu; yanıt EUR VE Denar'ı her zaman birlikte döner
JSONObject mk = istemci.hesaplaToll("MK", rota, Map.of("sinif", 3));
// RS — sinif zorunlu; yanıt EUR VE RSD'yi her zaman birlikte döner
JSONObject rs = istemci.hesaplaToll("RS", rota, Map.of("sinif", 1));
HTTP Durum Kodları ve Hata Yönetimi
HTTP
Anlamı
Ne yapmalı
200
Başarılı hesap (ücretli geçiş bulunamamış da olabilir)
Yanıt alanlarını kontrol edin
204
CORS OPTIONS ön isteği
Gövde beklemeyin
400
İstek/alan/kategori/geometri hatası
hata, eksik, gecerli_degerler alanlarını gösterin
401
API anahtarı eksik/geçersiz
Anahtarı kontrol edin
403
Ülke yetkisi yok
Anahtara ülke izni tanımlatın
404
Endpoint veya rota bulunamadı
URL'yi ve rota durumunu kontrol edin
422
Ücret motoru hesaplayamadı
Logladıktan sonra isteği körlemesine tekrarlamayın
429
Saatlik/günlük/aylık limit doldu
İlgili pencere sıfırlanana kadar bekleyin
502
Rota servisi yanıt vermiyor
Kontrollü backoff ile tekrar deneyin
503
Veritabanı/servis geçici kullanım dışı
Kontrollü backoff ile tekrar deneyin
⚠️ Yanıt gövdesinin şekli endpoint'e göre değişir — hepsi aynı
sözleşmeyi paylaşmaz: /toll ve /classify hata
durumunda {"ok":false,"hata":"..."} döner;
/route yalnızca {"error":"..."} döner
(ok veya hata alanı YOKTUR);
/site-hesapla ve /site-maliyet yalnızca
{"hata":"..."} döner (ok veya error
alanı YOKTUR). Hata ayrıştırma kodunuzu buna göre yazın — tek bir
endpoint'e göre yazıp diğerlerine kopyalamayın.
Kod
hata
Anlamı
400
eksik_parametre
Ülke için zorunlu alan eksik (yanıtta eksik alanlar + geçerli değerler)
400
kopru_bilgi_eksik
Çok kategorili köprü için araç detayı eksik (koltuk/aks/yükseklik)
Hata ile "sıfır ücret" arasındaki fark: HTTP 400/401/403/422/5xx
işlemin başarısız olduğu anlamına gelir. HTTP 200 + ok:true
+ eur:0 ise rota üzerinde ücret bulunamamış olabilir (hata değildir).
Türkiye'de giseler[].tip = "TARIFE_YOK" geçişi tespit edilmiş
ama tarifesi tanımlanmamış demektir — ücretsiz geçiş olarak
yorumlamayın. Yunanistan'da gecis_sayisi:0 ücretli geçiş
bulunmadığını belirten başarılı bir sonuçtur.
Tekrar deneme stratejisi:400/401/403/422 için tekrar denemeyin (istek gövdesini düzeltmeden tekrar etmek aynı hatayı verir); 429 için ilgili pencere sıfırlanmadan tekrar denemeyin; yalnızca 502/503 ve ağ zaman aşımlarında kontrollü (exponential) backoff ile tekrar deneyin.
Limitler
Her API anahtarı üç bağımsız pencereyle sınırlıdır: saatlik,
günlük ve aylık (son 30 gün, takvim ayı değil). Herhangi biri
dolarsa 429 alırsınız; yanıttaki hata alanı hangi
pencerenin dolduğunu belirtir.
Paket
Saatlik
Günlük
Aylık
Erişilebilir ülkeler
API Deneme (ücretsiz)
100
100
3.000
Yalnız TR
API Basic
1.500
— (tavan yok)
50.000
TR, GR (varsayılan — talep formunda farklı ülkeler de seçilebilir, onay admine bağlıdır)
API Pro
6.000
— (tavan yok)
500.000
Tüm ülkeler (TR, GR, BG, AL, MK, RS, ME)
API Enterprise
25.000
— (tavan yok)
5.000.000
Tüm ülkeler (TR, GR, BG, AL, MK, RS, ME)
Ücretli paketlerde aylık limit dolunca, paketinizde seçtiğiniz tercihe göre
(talep formunda belirlersiniz) hizmet ya durur ya da aşım ücretiyle devam
eder. Ücretsiz deneme paketinde limit dolunca hizmet her zaman durur.
Anahtarınızı yenilerseniz bu sayaçlar sıfırlanmaz — limitler hesap
bazında sayılır (yukarıdaki Kimlik Doğrulama bölümüne bakın). Limit
yükseltme için iletişime geçin.
Önerilen Entegrasyon Akışı
Araç kaydı: Her aracınızı yukarıdaki "Fiziksel Araç Profili" alanlarıyla kendi sisteminizde saklayın; ülke sınıflarını sabit bir tahminle değil, gerçek fiziksel veriden üretin.
/classify ile kontrol: Tam profili gönderip her ülke için durum: "tamam" döndüğünü doğrulayın; eksik listesi doluysa o ülke için sınıf güvenilir değildir.
Ülke bazlı /toll isteği: Aynı rotayı her ülke için ayrı istek gövdesiyle, o ülkenin kendi alanlarıyla gönderin (TR/AL/MK/RS→sinif, GR→kategori+gerekiyorsa köprü alanları, BG→kategori+emisyon+co2_sinifi). Bir ülkeden gelen kategori değerini diğerine kopyalamayın; AL/MK/RS'de gerçek yerel kategoriyi biliyorsanız al_kategori/mk_kategori/rs_kategori override alanlarıyla belirtin.
Yanıtı yorumlama: Önce HTTP durumunu, sonra hata/eksik alanlarını kontrol edin; toplamı ülkenin kendi para birimi alanından okuyun (bkz. "Para Birimi ve Döviz Kuru"), TL karşılıklarını yalnızca bilgi amaçlı kullanın; TARIFE_YOK kayıtlarını ücretsiz saymayın; kopru_bilgi_eksik durumunda kullanıcıdan tahmin değil gerçek fiziksel veri isteyin.
Tekrar deneme: Yalnızca 502, 503 ve ağ zaman aşımlarında, exponential backoff ile tekrar deneyin; 400/401/403/422 ve limit dolu 429 için tekrar denemeyin. API'de yazılı kullanım kaydı tutulduğundan, bir isteğin zaman aşımı sonrası işlenip işlenmediğinden emin değilseniz körlemesine tekrar göndermeyin.
Sık Sorulan Sorular
Bulgaristan'da geçiş ücreti, aracın kat ettiği gerçek yol mesafesine göre hesaplanır (Türkiye ve Yunanistan'daki sabit gişe/köprü sisteminden farklı olarak). ÜcretliYol bu mesafeyi kendi bağımsız motoruyla ve gerçek GPS koordinatları üzerinden ölçer — yolu düz çizgilerle tahmin etmez, gerçek geometrisini takip eder. Bulgaristan'ın resmi sistemi bazı durumlarda güzergâhı farklı bölümleyebilir, bazı bağlantı/kavşak noktalarını farklı değerlendirebilir ya da mesafeyi basitleştirebilir. Bu nedenle iki sonuç santim santim aynı olmayabilir; ancak fark son derece küçüktür ve bizim değerimiz gerçek yol uzunluğuna dayanır. Türkiye ve Yunanistan gişeli/sabit ücretli sistem kullandığından orada böyle bir fark oluşmaz — sonuçlar birebir eşleşir.
Rota kaynağınız ne olursa olsun (Google Directions, Mapbox, HERE, kendi OSRM/Valhalla sunucunuz, GPS logu), rotanın izlediği yolu temsil eden sıralı nokta listesini çıkarıp koordinatlar alanına [boylam, enlem] sırasıyla gönderin — sadece başlangıç/bitiş değil, aradaki tüm güzergâhı. Detaylı adımlar ve bir Java örneği için yukarıdaki "Kendi uygulamanızda oluşturduğunuz rotayı nasıl iletmelisiniz?" ve "Kod Örnekleri" bölümlerine bakın. Kendi rotanız yoksa /route kullanın, rotayı biz çizeriz.
[boylam, enlem] — yani önce boylam (longitude), sonra enlem (latitude). Örnek: [27.9624, 43.2180]. Bu sıra GeoJSON standardıyla aynıdır ama çoğu harita SDK'sının (Google, Mapbox vb.) size verdiği [enlem, boylam] sırasının TERSİDİR — dönüştürmeyi unutmayın. Ters gönderirseniz rota yanlış yerde eşleşir, hata dönmez.
POST /toll bugün 7 ülkenin tamamını destekler: Türkiye (TR), Yunanistan (GR), Bulgaristan (BG), Arnavutluk (AL), Kuzey Makedonya (MK), Sırbistan (RS), Karadağ (ME). POST /classify de aynı 7 ülkenin tamamı için sınıf üretir. Bunu sabit varsaymak yerine GET /countries'in capabilities.toll/capabilities.classify alanlarını kontrol etmenizi öneririz — yeni bir ülke veya yetenek eklendiğinde otomatik fark edersiniz (Romanya, Hırvatistan, Macaristan ve Bosna-Hersek üzerinde çalışıyoruz). Her istekte ulke alanıyla belirtirsiniz; anahtarınız yalnızca izinli olduğu ülkeler için çalışır.
Ekstra bir parametre göndermenize gerek yok. Arnavutluk, Kuzey Makedonya ve Sırbistan için yanıt her zaman hem EUR hem de o ülkenin yerel para birimini birlikte içerir — toll.lek (Arnavutluk), toll.denar (Kuzey Makedonya) veya toll.rsd (Sırbistan) alanını doğrudan okuyun, EUR'u görmezden gelebilirsiniz. Yunanistan ve Bulgaristan'da yerel para birimi zaten EUR'dur. Türkiye'de sonuç zaten TL'dir. Ayrıntılar için yukarıdaki "Para Birimi ve Döviz Kuru" bölümüne bakın.
Hayır — toll.eur/toll.lek alanları API sözleşmesinde HER ZAMAN mevcuttur, motor asla bu alanları eksik bırakmaz. Eğer belirli bir gişe/tesis için EUR değeri 0 ama Lek değeri 0'dan büyükse, bu o spesifik tarife satırının EUR karşılığının veri tabanımıza henüz girilmediği anlamına gelir — motor veya API'nin bir kusuru değil, o tek kayda özel bir veri eksikliğidir. Böyle bir durumda Lek değeri geçerli/yetkili tutardır, onu kullanın; bize bildirirseniz ilgili tarifeyi tamamlarız.
/toll — rotayı SİZ oluşturursunuz, biz yalnızca ücretlendiririz (API rota çizmez), 7 ülkenin tamamı. /route — yalnızca başlangıç/varış koordinatı verirsiniz, rotayı biz çizeriz; yalnızca Türkiye (+opsiyonel Yunanistan) ücretini döner. /classify — rota gerektirmez; 7 ülkenin tamamı için kendi sınıf karşılığını tek bir fiziksel araç profilinden üretir, genellikle /toll'a göndereceğiniz alanı belirlemek için ön adım olarak kullanılır.
Hayır. Panelden yeni anahtar oluşturduğunuzda önceki anahtarınız hemen geçersiz olur (bir hesapta yalnızca tek aktif anahtar bulunabilir), ama kalan saatlik/günlük/aylık kullanım hakkınız kaybolmaz — limitler anahtar değil hesap bazında sayılır. Yani yenileme, sınırınızı sıfırlamanın bir yolu değildir.
Anahtarınız, başvuru sırasında seçtiğiniz (veya admin onayıyla eklenen) ülkelerle sınırlıdır. Kapsam dışı bir ülke için istek atarsanız ulke_izni_yok hatası alırsınız. API Pro ve Enterprise paketleri varsayılan olarak tüm ülkelere erişebilir. Ek ülke için bizimle iletişime geçebilirsiniz.
Deneme süresi bitince anahtarınız pasif olur. Kesintisiz kullanım, daha yüksek istek limiti ve ek ülkeler için ücretli pakete geçmeniz gerekir — iletişime geçin, anahtarınızı yeniden aktifleştirelim.
Üç bağımsız limit vardır: saatlik, günlük ve aylık (bkz. yukarıdaki Limitler tablosu). Hangisi dolarsa 429 yanıtı ve ilgili hata kodunu (gunluk_limit_asildi / aylik_limit_asildi) alırsınız; ilgili pencere sıfırlanınca istekleriniz tekrar kabul edilir. Ücretsiz deneme paketinde asıl sınırlayıcı günlük 100 istek hakkıdır. Yoğun kullanım için limit yükseltme talep edebilirsiniz.
Rio–Antirrio gibi bazı Yunan köprülerinde ücret, araç tipine, koltuk sayısına, aks sayısına, yüksekliğe ve römork durumuna göre 9'a varan kategoriye ayrılır. Doğru köprü ücretini hesaplayabilmemiz için bu bilgileri göndermeniz gerekir; eksikse kopru_bilgi_eksik yanıtıyla hangi alanın gerektiğini, gerçek aktif kategorileri (secenekler) ile birlikte bildiririz. İstisna: binek (otomobil) araçlar için hiçbir zaman ek soru sorulmaz — engelli alanını göndermezseniz otomatik olarak standart kategori uygulanır, yalnızca gerçekten engelli/mavi kartlı bir araç için engelli:true göndermeniz yeterlidir.
Ne kadar sık nokta gönderirseniz mesafe ölçümü o kadar doğru olur. İdeal olarak yaklaşık her 50–100 metrede bir nokta önerilir. Çok seyrek noktalar (örn. sadece dönüşler) mesafeyi olduğundan kısa gösterebilir.
Ülkeye göre değişir: Türkiye→TL, Yunanistan/Bulgaristan→EUR (+ TL karşılığı), Arnavutluk/Kuzey Makedonya/Sırbistan→hem EUR hem yerel para birimi (Lek/Denar/RSD, ikisi de her zaman birlikte, + TL karşılığı). Bir "para birimi seçme" parametresi yoktur — her zaman ilgili ülkenin ürettiği tüm alanları alırsınız. Güncel döviz kuru her yanıtta eur_kur (ve varsa lek_kur/denar_kur/rsd_kur) alanında verilir. Tam tablo için yukarıdaki "Para Birimi ve Döviz Kuru" bölümüne bakın.
Ücretli pakete geçiş: Deneme sürenizde tüm özellikleri test edebilirsiniz.
Kalıcı erişim, yüksek limit ve ek ülkeler için
bizimle iletişime geçin.
ÜcretliYol API Documentation
The ÜcretliYol API supports two working models: (1) pricing a route you
already have — you build the route, send us its geometry and vehicle
details, and we return the highway/bridge/toll cost
(POST /toll); (2) pricing by building a route — you
send only a start and end point and we build the route server-side
(POST /route). Whichever endpoint you use, the cost is always
computed over the real itinerary — either the one you sent or the
one we built. POST /toll today covers all 7 countries:
Turkey, Greece, Bulgaria, Albania, North Macedonia, Serbia and Montenegro —
all in the same simple request/response model.
POST /classify also produces a class for all 7 of them.
How to get an API key? Sign in and open the
API Service tab to request access.
Once enabled, you can generate your own key. After the 14-day trial,
contact us to move to a paid plan.
📌 Release note (2026-09-08):POST /toll now produces
real pricing for Albania (AL), North Macedonia (MK) and Serbia (RS) as
well — previously only BG/GR/TR were supported. API Pro and Enterprise
plans now default to access for all countries (see the Limits
section). If your integration hard-codes a country list, check
GET /countries to pick up new countries automatically.
📌 Release note (2026-09-10): Montenegro (ME) added. POST /toll,
/site-hesapla and /site-maliyet now produce real
pricing for Montenegro — see the "MONTENEGRO" section below. It shows as
status: "development" in GET /countries (a
temporary label pending a live-route verification — the endpoint is usable
today since capabilities.toll: true).
📌 Release note (2026-09-11):POST /classify now produces
a class for Montenegro (ME) too — it computes Montenegro's official 5-tier
tariff category (ME_CAT_1..ME_CAT_5) from your
physical vehicle profile (see the "Classification summary" table below).
GET /countries now shows capabilities.classify: true
for ME as well.
📌 Release note (2026-09-11): On POST /toll, sinif
is now conditionally required for Albania (AL), North Macedonia (MK), Serbia
(RS) and Montenegro (ME) — send a valid country-native category
(me_kategori / rs_kategori / mk_kategori,
or for Albania BOTH al_kategori AND al_kategori5)
and you no longer need to send sinif at all. Existing
integrations that only ever send sinif are completely
unaffected.
Authentication
Send your API key in the X-API-Key header on every request:
Your key is valid only for the countries you selected (or that were later
approved by an admin). Requesting an unauthorized country returns
ulke_izni_yok. Your key must also be active, not expired, and
within its hourly/daily/monthly usage limits.
An account can have only one active key at a time. Clicking
"Generate New Key" in the panel immediately invalidates your previous key
and issues a new one — this is a key rotation, not a reset.
Your remaining hourly/daily/monthly usage allowance is not lost:
limits are counted per account, not per key, so rotating a key never
resets your usage counters. Cancelling a key deactivates it instantly on
both the panel and the server.
⚠️ Designed for server-to-server use only. Keep your API key on your
own server, never exposed in a browser or mobile client, and issue the
request from there. Also, CORS (browser Origin) is only open
for ucretliyol.com — calling
directly from your own domain's browser JavaScript will hit a CORS error;
issue the request from your own backend instead.
Endpoints
Base URL for all endpoints below:
Base URL
https://api.ucretliyol.com
Format
JSON (request and response, application/json; charset=utf-8)
Endpoints that don't require a key
Endpoint
Method
Description
/
GET
API name, version and endpoint list
/status
GET
Health of the database and routing service
/countries
GET
Country list and which endpoints each one supports today (details below)
Endpoints that require an API key
Endpoint
Method
Use
/toll
POST
Prices a route you send — the focus of this document. TR, GR, BG, AL, MK, RS, ME
/route
POST
Builds a route between two points and returns the Turkish (and, if the route enters Greece, Greek) toll cost
/classify
POST
Produces each country's own vehicle class from a single physical profile
/site-hesapla and /site-maliyet also technically
work with the same key (for fuel/logistics cost and a rich screen that
returns every country in one call), but they are not designed for general
third-party integration and can change without notice. Since
/toll alone now covers all 7 countries, we recommend
/toll, /route and /classify for your
own integration.
GET /countries — countries and capabilities
No key required. Returns each country's overall maturity (status)
and which endpoints actually work for it today (capabilities).
status: "development" does not mean a country doesn't
work at all — some capabilities can be true while status is
still "development"; always rely on capabilities
to decide whether an endpoint is usable.
true means POST /classify can produce a class for this country
capabilities.site_hesapla / site_maliyet
Whether this country produces real pricing in ÜcretliYol's own internal formats (not a reference for third-party integration)
planned
An optional field for capabilities not active today but planned for the future (all 7 countries currently have every capability active, so this field doesn't appear for any of them today — it may surface for a new country before it goes fully live)
We recommend polling /countries periodically instead of
hard-coding this list — you'll pick up a newly supported country without
changing your integration. Romania, Croatia, Hungary and
Bosnia-Herzegovina are in progress / on our roadmap — when one goes live
it will appear here first, with capabilities.toll:true.
GET /status — health check
No key required. A simple health endpoint you can use in your monitoring/
uptime tooling; confirms the database and routing service (Valhalla) are
reachable.
GET /status
{ "status": "ok", "database": true, "valhalla": true }
database is always
true in a response (if the database were unreachable the API
would already answer every request with 503 — so if you got
this far, the database is up). valhalla:false means the
routing service is temporarily unresponsive; this can affect endpoints
where the server DRAWS the route (/route,
/site-hesapla, /site-maliyet) — /toll
isn't affected since it never draws a route (you supply it).
Route Submission
Send your route in one of two forms:
koordinatlar(recommended)
Your route points as [longitude, latitude]. Longitude first!
An encoded polyline produced with the same algorithm Google popularized,
at precision 6 (1e6).
"geometri": "quwdI..._encoded_polyline_p6_..."
⚠️ Don't paste the overview_polyline or
steps[].polyline value you get from Google's Directions/Maps
API directly into geometri. Google's own API encodes its
polyline at precision 5 (1e5) by default; this API expects
precision 6 (1e6). A precision mismatch silently decodes to wrong
(completely unrelated) coordinates — it doesn't error, the route just maps
to the wrong place. If you're working with a route from Google, either
re-encode it at precision 6 on your side, or (much simpler and safer)
decode the route points and send them as a koordinatlar array
instead — this sidesteps the precision issue entirely.
Coordinates are WGS84 decimal degrees and a route must contain at least two
points. Denser route points give more accurate results — ideal: a point
every 50–100 m. The system looks for toll/segment intersections with exact
geometry, WITHOUT a proximity tolerance; a very sparse route can miss a
toll booth or bridge line.
⚠️ If koordinatlar and geometri are sent together
in the same request, which one is used can vary by country (the encoded
geometry is used for Bulgaria, koordinatlar for the others).
If the two fields don't represent the same route, different countries can
produce different results — it's safest to send only one of them
per request.
Use a decimal point per the JSON standard for numbers
("co2_sinifi": 1, 380.5); comma-decimal strings
("380,5") are not a reliable contract.
How should you submit a route your own app already built?
If your own navigation/mapping stack already produces a route (Google
Directions, Mapbox Directions, HERE, your own OSRM/Valhalla server, GPS
logs from a fleet-tracking system, etc.), POST /toll is built
exactly for this — the API does NOT draw its own route; it prices the
itinerary you send. What to do:
From your route source (map SDK, directions API, GPS log), extract the
ordered list of points that represent the actual road the route
follows — not just the start and end point, but the whole
itinerary in between. For example, decode Google Directions'
overview_polyline into its [lat, lon] points.
Convert every point to [longitude, latitude] order (most map SDKs
give you [lat, lon] — ÜcretliYol expects the
opposite order, matching GeoJSON).
Send that array as koordinatlar, together with the vehicle
fields for whichever country you want priced, to POST /toll.
If the route crosses more than one country (e.g. it leaves Turkey and
enters Greece), send the same coordinate array in a separate
request per country, with that country's own ulke and
arac fields — /toll only prices one country
per request.
If you don't have your own route (only a start/end point), we can build it
for you server-side — see POST /route below. Use
/toll when you already HAVE a route, /route when
you DON'T; don't mix the two up.
Physical Vehicle Profile
For consistent integration across countries, we recommend storing a
vehicle's physical data in your system as a single profile — this is
what the /classify endpoint consumes:
Field
Type
Description
vehicle_type
string
motorcycle, car, van, bus, truck
axle_count
integer
Total axle count
front_axle_height_cm
number
Front axle height (cm) — do not confuse with total vehicle height
total_height_cm
number
Total vehicle height (cm)
max_weight_tons
number
Maximum weight (tons)
seat_count
integer
Seat count, used to distinguish bus/minibus
emission_class
string
e.g. euro_3, euro_4, euro_5, euro_6
Important rule: If you don't know a physical field that affects the
toll class, don't send a guessed value — leave it out and, if
needed, ask the user. Don't copy one country's class number (e.g. Turkey's
sinif) directly into another country's field; each country has
its own classification logic (see below).
Currency and Exchange Rates
Which currency you get back varies by country. The table below shows which
currency field(s) appear in each country's response — there is no
"choose a currency" parameter, every country always returns ALL of the
fields its engine produces; you decide which one to use.
Country
Native currency
Response field(s)
TL equivalent
Turkey (TR)
TRY
toll.try
already TL, no separate field
Greece (GR)
EUR
toll.eur
toll.tl
Bulgaria (BG)
EUR
toll.eur
toll.tl
Albania (AL)
ALL (Lek)
toll.eurandtoll.lek (both, always)
toll.tl
North Macedonia (MK)
MKD (Denar)
toll.eurandtoll.denar (both, always)
toll.tl
Serbia (RS)
RSD (Dinar)
toll.eurandtoll.rsd (both, always)
toll.tl
Montenegro (ME)
EUR
toll.eur
toll.tl
Don't want EUR — just the local currency? For Albania, North
Macedonia and Serbia, the response always contains both EUR
(toll.eur) and that country's own local currency
(toll.lek / toll.denar / toll.rsd) —
no extra parameter is needed, just read the field you want. Greece,
Bulgaria and Montenegro (Montenegro is officially euroized) don't have a
separate "local currency" field because their local currency already IS
EUR (toll.eur is already local for all three). Turkey's
result is already in TL.
⚠️ If a crossing/facility's eur field is 0 but its local
currency field is greater than 0 — this is not a bug: that specific
toll/facility's EUR tariff simply hasn't been entered into our database
yet. For that record, the local-currency field
(lek/denar/rsd) is the authoritative
value — use it instead of EUR. If you notice this,
let us know and we'll complete that tariff row.
The exchange-rate fields (eur_kur, lek_kur,
denar_kur, rsd_kur) tell you the exact rate used
to compute the TL equivalent in that specific response. The rate is
refreshed periodically from the Central Bank of Turkey's (TCMB) daily
bulletin (for EUR and other major currencies) and a supplementary source
(for ALL/MKD/RSD, which aren't in the TCMB bulletin, as a cross-rate); if a
fresh rate can't be fetched, the system falls back to the last known good
rate — so tl is almost never left empty. Don't cache a rate
on your side — read each response's own *_kur field, since
it can vary slightly between requests.
Required Fields and Response Fields by Country
For POST /toll, each country prices tolls differently.
Send the vehicle fields required by the country you're requesting. If
something is missing, the API does not calculate — it returns which field is
missing and the valid values, so no wrong toll is produced.
1–4 (only matters for euro_vi_eev); zev automatically uses 5; a missing/out-of-range value falls back to 1
Tonnage/axle info is embedded in the category — don't send it separately. Category names must be sent verbatim as text; don't send Turkey's sinif number to Bulgaria.
Computed toll (EUR and its TL equivalent at the current rate)
toll.toll_km / toll.ucretsiz_km
Tolled classified km and toll-free km
toll.dagilim_km
Km breakdown across motorway (AM/FRC0), FRC1, FRC2
toll.birim_eur_km
Unit price array, fixed order [AM/FRC0, FRC1, FRC2]
eur_kur
Current EUR rate used for the TL equivalent
kaynak
Usually wayid; can be segment or eski_motor if that service is unreachable
For cars and motorcycles, BG's /toll accepts the heavy-vehicle categories above; the passenger-car vignette model is not part of this general API contract.
GRGreece — Highway + Bridge
Highway passes need kategori. If the route crosses a multi-category special facility (e.g. the Rio–Antirrio bridge), extra vehicle details may be required to resolve the right category.
binek, motosiklet, kamyon, otobus — the facility database's canonical type
koltuk_sayisi
If bus/minibus
Real seat count — the bridge fare can depend on it
aks_sayisi
If truck
2, 3, 4, 5…
yukseklik_cm
May be needed
Total vehicle height, in centimetres (the API converts to metres internally — sending 3.80 is read as "3.80 cm")
romork_var
May be needed
0 / 1
engelli
Optional
true / false — for cars, requests the disabled/blue-card category
If arac_tipi is omitted, the system makes a rough guess from the Turkish class or axle count; sending it explicitly is recommended for the correct facility category.
⚠️ Important — Greek bridges: Some bridges such as Rio–Antirrio split
fares into up to 9 categories (by vehicle type, seat count, axles,
height, trailer) — this list depends on the facility database's content and
can change, don't assume it's fixed. If information is missing you get a
response like this:
secenekler is generated from the facility database's real active categories. Flow: (1) ask the user for the field in eksik, (2) put the real physical value into arac, (3) resend the same request with the full profile. Don't add secenekler[].ucret to your total yourself — the authoritative result is the toll.koprular field of a successful request.
For cars, the system never asks a follow-up question: if you omit
engelli (or it's unknown), the standard category is applied
automatically; send engelli:true only when the vehicle
genuinely has a disabled/blue-card permit to get the correct (discounted)
category.
The category is derived from Turkey's sinif — the engine
detects, for each toll/facility it crosses, whether that specific
facility's tariff row uses a 4-class or 5-class scheme (e.g.
stand-alone facilities like the Llogara Tunnel) and picks the right
category itself. For manual control, you can send
al_kategori (1-4) and/or al_kategori5 (1-5) —
send both, valid, and sinif isn't needed at all
(whichever scheme a facility on the route turns out to use, the category
is determined directly from these two; sending only one still requires
sinif, since the route could cross a facility on the other
scheme).
Field
Required
Values
sinif
Conditional*
1 = Car · 2 = Minibus · 3 = 2-3 axles · 4 = 4-5 axles · 5 = 6+ axles · 6 = Motorcycle (same classification as Turkey — the engine derives Albania's own category from this)
al_kategori
Optional
1-4 — manual category selection for a facility with a 4-class tariff
al_kategori5
Optional
1-5 — manual category selection for a facility with a 5-class tariff (e.g. the Llogara Tunnel)
* sinif is not required only if BOTH al_kategori AND al_kategori5 are sent and valid; otherwise it's required.
Total toll; both are always populated (see "Currency and Exchange Rates" above)
toll.tl
TL equivalent, computed from EUR if present, otherwise from Lek
arac.kullanilan_kategori / kategori_sema
The category and scheme the engine auto-picked (or your manual override) — a route can cross facilities on different schemes, check each gecisler[] item's own fields for details
eur_kur / lek_kur
Current rates used for the TL equivalent
On a route with no tolled crossing, you get ok:true, toll.eur/lek/tl:0, an empty gecisler array and an informational mesaj — this is not an error.
MKNorth Macedonia — Toll booth (single point, exact match)
The category is derived from Turkey's sinif (the official
axle + front-axle-height rule isn't captured in today's vehicle profile
yet). For manual control, send mk_kategori — send a
valid one and sinif isn't needed at all.
The category is derived from Turkey's sinif (the official
axle + front-axle-height + trailer rule isn't captured in today's
vehicle profile yet). For manual control, send rs_kategori —
send a valid one and sinif isn't needed at all.
Total toll; both are always populated. EUR is Serbia's official/trusted source
toll.tl
TL equivalent, computed from EUR if present, otherwise from RSD
eur_kur / rsd_kur
Current rates used for the TL equivalent
On a route with no tolled crossing, you get ok:true, toll.eur/rsd/tl:0, an empty gecisler array and an informational mesaj — this is not an error.
MEMontenegro — Toll booth (single point and entry-exit, mixed model)
The category is derived from Turkey's sinif (the official
axle + front-axle-height + trailer rule isn't captured in today's
vehicle profile yet). For manual control, send me_kategori —
send a valid one and sinif isn't needed at all.
Montenegro is already EUR-based, so the response has no separate local
currency field (see "Currency and Exchange Rates" above).
Total toll (EUR) — Montenegro's single, official currency
toll.tl
TL equivalent, computed with eur_kur
eur_kur
Current rate used for the TL equivalent
gecisler[].tip
single: a single-point crossing (e.g. the Sozina Tunnel) · cift: an entry-exit zoned distance model (e.g. the zones on the Bar-Boljare highway)
On a route with no tolled crossing, you get ok:true, toll.eur/tl:0, an empty gecisler array and an informational mesaj — this is not an error.
For AL/MK/RS/ME, sinif isn't a literal copy of the category —
if you send it, the engine automatically translates it into that
country's own official category scheme (Albania's 4-class/5-class, North
Macedonia's 1a/1b/2/3/4, Serbia's 1a/1/2/3/4, Montenegro's 1-5). This is a
best-effort estimate and won't always match the real local plate class
exactly. If you know the vehicle's real local category (e.g. it's already
recorded in your fleet system), use the al_kategori /
al_kategori5 / mk_kategori / rs_kategori /
me_kategori override fields for better accuracy —
send a valid override and sinif isn't needed at all
(for Albania, only when BOTH al_kategori AND
al_kategori5 are sent; the other three countries need just one
field). This way an integration that already knows that country's own
category system never has to learn Turkey's classification at all.
POST /classify — universal vehicle classification
Send the "Physical Vehicle Profile" above and get every country's own class
in a single request. Useful for integrations that want to convert one
vehicle profile into country-specific classes.
classes contains only the countries your API key is authorized
for. durum: "tamam" means the class could be resolved for that
country; a non-empty eksik list means the result is not
reliable without those fields.
Motorcycle→exempt · ≤3.5t→passenger vignette · >3.5t bus→BG_BUS_<EURO> · other heavy vehicle 2/3/4+ axles→BG_TRUCK_<N>AXLE_<EURO>. If emission is unknown the resolver assumes EURO_6 — always send the real emission explicitly to /toll for pricing.
Motorcycle→AL_CAT_1 · car ≤3.5t→AL_CAT_2 · van/bus ≤23 seats→AL_CAT_3 · >23 seats or 2-axle truck→AL_CAT_4 · other 3+ axle heavy vehicle→AL_CAT_5
North Macedonia
Approximate mapping to the official 1a/1b/2/3/4 plate scheme from weight and height thresholds (falls back to weight thresholds if disambiguating info is missing)
Serbia
Approximate mapping to the official 1a/1/2/3/4 plate scheme from weight, height and trailer info
Montenegro
Motorcycle→ME_CAT_1 · >3.5t, 2-3 axles→ME_CAT_4 · >3.5t, 4+ axles→ME_CAT_5 · ≤3.5t (or unknown), 3+ axles→ME_CAT_3 · ≤3.5t, 2 axles, front axle <130cm (or total height ≤190cm)→ME_CAT_2 · ≤3.5t, 2 axles, front axle ≥130cm (or total height >190cm)→ME_CAT_3. Matches the official Autoput Bar-Boljare 5-tier tariff exactly (see monteput.me/cjenovnik/).
Sending fields explicitly is the most accurate approach; for backward compatibility, a missing vehicle_type defaults to car and a missing/<2 axle_count defaults to 2 — so a result from an incomplete profile does not guarantee physical accuracy. Require a complete profile before classifying in production.
POST /route — route and Turkish toll between two points
Use this to build a route and compute the Turkish toll cost by giving only a
start/end point, without sending ready-made geometry. If you don't have your
own route (only two coordinates), this is exactly what you need — we build
it with Valhalla/OSRM.
vehicle_class is clamped to 1–6; if omitted, 1 is used. The route is built with an automobile (auto) profile — it does not reflect heavy-vehicle routing restrictions, so for heavy vehicles it's more accurate to build your own real route and send it to /toll. If your key has GR access and the route enters Greece, an extra greece section may be added to the response; if your key lacks TR access, it may return tr_izin_yok:true. /route only prices Turkey (+ optional Greece) — for Bulgaria/Albania/North Macedonia/Serbia, build your own route and issue a separate /toll request per country.
using System.Net.Http;
using System.Text;
using System.Text.Json;
var client = new HttpClient();
client.DefaultRequestHeaders.Add("X-API-Key", "YOUR_API_KEY");
var body = new {
ulke = "BG",
koordinatlar = new[] { new[] {27.9624, 43.2180}, new[] {28.5582, 43.7360} },
arac = new { kategori = "kamyon_12_4aks", emisyon = "euro_vi_eev", co2_sinifi = 1 }
};
var content = new StringContent(JsonSerializer.Serialize(body), Encoding.UTF8, "application/json");
var resp = await client.PostAsync("https://api.ucretliyol.com/toll", content);
Console.WriteLine(await resp.Content.ReadAsStringAsync());
Java: a reusable client that submits your own route
The class below takes a route produced by your own navigation/mapping stack
(Google Directions, Mapbox, HERE, your own OSRM/Valhalla server, fleet GPS
logs, etc.) and prices it for any of the seven countries. You fill in the
arac map per country (see the "Required Fields by Country"
tables above).
import org.json.JSONArray;
import org.json.JSONObject;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.List;
import java.util.Map;
public class UcretliYolClient {
private final String baseUrl;
private final String apiKey;
private final HttpClient http = HttpClient.newHttpClient();
public UcretliYolClient(String baseUrl, String apiKey) {
this.baseUrl = baseUrl;
this.apiKey = apiKey;
}
/**
* Prices a route built by your own application. "route" is the ORDERED list
* of points the route follows — not just the start/end, but the actual
* itinerary. Each point is {longitude, latitude} (note: most map SDKs give
* you latitude/longitude order — this API expects the OPPOSITE order).
*
* @param ulke "TR" | "GR" | "BG" | "AL" | "MK" | "RS"
* @param route Route points, each {longitude, latitude}
* @param arac Vehicle info, which fields depend on the country (e.g. "sinif"
* for TR, "kategori"+"emisyon" for BG)
*/
public JSONObject priceToll(String ulke, List route, Map arac) throws Exception {
JSONArray koordinatlar = new JSONArray();
for (double[] point : route) {
koordinatlar.put(new JSONArray(new double[]{point[0], point[1]}));
}
JSONObject body = new JSONObject()
.put("ulke", ulke)
.put("koordinatlar", koordinatlar)
.put("arac", new JSONObject(arac));
HttpRequest req = HttpRequest.newBuilder()
.uri(URI.create(baseUrl + "/toll"))
.header("Content-Type", "application/json")
.header("X-API-Key", apiKey)
.POST(HttpRequest.BodyPublishers.ofString(body.toString()))
.build();
HttpResponse res = http.send(req, HttpResponse.BodyHandlers.ofString());
JSONObject data = new JSONObject(res.body());
if (res.statusCode() != 200 || !data.optBoolean("ok", false)) {
// On 400/401/403/422 check data's "hata"/"eksik"/"gecerli_degerler"
// fields — don't blindly retry (see HTTP Status Codes above).
throw new RuntimeException("ÜcretliYol error (" + res.statusCode() + "): " + data);
}
return data;
}
}
// ---- Usage ----
UcretliYolClient client = new UcretliYolClient("https://api.ucretliyol.com", "YOUR_API_KEY");
// Points from your own navigation SDK, representing the road the route follows
List route = List.of(
new double[]{27.9624, 43.2180},
new double[]{28.1000, 43.4000},
new double[]{28.5582, 43.7360}
);
// TR — sinif required
JSONObject tr = client.priceToll("TR", route, Map.of("sinif", 1));
// GR — kategori required (1-4)
JSONObject gr = client.priceToll("GR", route, Map.of("kategori", 3));
// BG — kategori + emisyon required
JSONObject bg = client.priceToll("BG", route, Map.of(
"kategori", "kamyon_12_4aks", "emisyon", "euro_vi_eev", "co2_sinifi", 1));
// AL — sinif required; response always includes EUR AND Lek together
JSONObject al = client.priceToll("AL", route, Map.of("sinif", 3));
System.out.println("Albania: " + al.getJSONObject("toll").getDouble("eur") + " EUR / "
+ al.getJSONObject("toll").getDouble("lek") + " Lek");
// MK — sinif required; response always includes EUR AND Denar together
JSONObject mk = client.priceToll("MK", route, Map.of("sinif", 3));
// RS — sinif required; response always includes EUR AND RSD together
JSONObject rs = client.priceToll("RS", route, Map.of("sinif", 1));
HTTP Status Codes and Error Handling
HTTP
Meaning
What to do
200
Successful calculation (may still find no tolled crossing)
Check the response fields
204
CORS OPTIONS preflight
Don't expect a body
400
Request/field/category/geometry error
Show hata, eksik, gecerli_degerler
401
Missing or invalid API key
Check your key
403
Key not authorized for this country
Have the country added to your key
404
Endpoint or route not found
Check the URL and route status
422
The pricing engine could not compute a result
Log it; don't blindly retry the same request
429
Hourly/daily/monthly limit reached
Wait for that window to reset
502
Routing service not responding
Retry with controlled backoff
503
Database/service temporarily unavailable
Retry with controlled backoff
⚠️ The response body shape differs by endpoint — they do not all
share one contract: /toll and /classify return
{"ok":false,"hata":"..."} on error; /route returns
only {"error":"..."} (no ok or hata
field); /site-hesapla and /site-maliyet return
only {"hata":"..."} (no ok or error
field). Write your error parsing accordingly — don't write it for one
endpoint and copy it to the others.
Code
error
Meaning
400
eksik_parametre
A required field is missing (response lists missing fields + valid values)
400
kopru_bilgi_eksik
Multi-category bridge needs more vehicle detail (seats/axles/height)
Error vs. "zero cost": HTTP 400/401/403/422/5xx means
the operation failed. HTTP 200 + ok:true +
eur:0 can mean no toll was found on the route (not an error).
In Turkey, giseler[].tip = "TARIFE_YOK" means a crossing was
detected but no fare is defined — do not treat it as free. In
Greece, gecis_sayisi:0 is a successful result meaning no
tolled crossing was found.
Retry strategy: don't retry 400/401/403/422 (repeating without fixing the request body returns the same error); don't retry 429 before the relevant window resets; only retry 502/503 and network timeouts, using controlled (exponential) backoff.
Limits
Each API key is limited by three independent windows: hourly,
daily and monthly (a rolling 30 days, not a calendar month).
If any window is exceeded you get 429; the response's
hata field tells you which window was hit.
Plan
Hourly
Daily
Monthly
Countries available
API Trial (free)
100
100
3,000
TR only
API Basic
1,500
— (no cap)
50,000
TR, GR (default — other countries can be requested on the signup form, subject to admin approval)
API Pro
6,000
— (no cap)
500,000
All countries (TR, GR, BG, AL, MK, RS, ME)
API Enterprise
25,000
— (no cap)
5,000,000
All countries (TR, GR, BG, AL, MK, RS, ME)
On paid plans, when the monthly limit is reached the service either stops
or continues with an overage fee, depending on the preference you chose on
your request form. On the free trial plan, the service always stops when
the limit is reached. Rotating your key does not reset these
counters — limits are counted per account (see Authentication above).
Contact us to raise your limits.
Recommended Integration Flow
Register the vehicle: Store each vehicle's physical fields (see "Physical Vehicle Profile" above) in your own system; derive country classes from real physical data, never from a fixed guess.
Check with /classify: Send the full profile and confirm durum: "tamam" for each country; a non-empty eksik list means that country's class is not reliable yet.
Per-country /toll request: Send the same route once per country, with that country's own fields (TR/AL/MK/RS→sinif, GR→kategori plus bridge fields if needed, BG→kategori+emisyon+co2_sinifi). Never copy a category value from one country into another; for AL/MK/RS, if you know the vehicle's real local category, specify it with the al_kategori/mk_kategori/rs_kategori override fields.
Interpret the response: Check the HTTP status first, then the hata/eksik fields; read the total from the country's own currency field (see "Currency and Exchange Rates" above) and use the TL equivalent only for display; don't treat TARIFE_YOK entries as free; on kopru_bilgi_eksik, ask the user for real physical data, never a guess.
Retries: Only retry 502, 503 and network timeouts, with exponential backoff; never retry 400/401/403/422 or a 429 whose window hasn't reset. The API keeps a written usage record, so don't blindly resend a request if you're unsure whether it was already processed after a timeout.
Frequently Asked Questions
In Bulgaria the toll is based on the actual road distance travelled (unlike the fixed booth/bridge system in Turkey and Greece). ÜcretliYol measures this distance with its own independent engine using real GPS coordinates — it follows the true road geometry instead of approximating with straight lines. Bulgaria's official system may segment the route differently, treat certain junctions/links differently, or simplify the distance. So the two results may not match to the centimetre; the difference is very small and our value is based on the real road length. Turkey and Greece use fixed booth/bridge pricing, so no such difference occurs there — results match exactly.
Whatever your route source is (Google Directions, Mapbox, HERE, your own OSRM/Valhalla server, GPS logs), extract the ordered list of points representing the actual road it follows and send it as koordinatlar in [longitude, latitude] order — not just the start/end, the whole itinerary in between. See "How should you submit a route your own app already built?" and "Code Examples" above for step-by-step guidance and a full Java example. If you don't have your own route, use /route instead and we'll build it.
[longitude, latitude] — longitude first, then latitude. Example: [27.9624, 43.2180]. This matches the GeoJSON standard, but is the OPPOSITE of the [lat, lon] order most map SDKs (Google, Mapbox, etc.) give you — don't forget to convert. Reversing it will map your route to the wrong place, with no error returned.
POST /toll today supports all 7 countries: Turkey (TR), Greece (GR), Bulgaria (BG), Albania (AL), North Macedonia (MK), Serbia (RS), Montenegro (ME). POST /classify also produces a class for all 7 of them. Rather than hard-coding this, we recommend checking GET /countries's capabilities.toll / capabilities.classify fields — you'll pick up a new country or capability automatically (Romania, Croatia, Hungary and Bosnia-Herzegovina are in progress). Specify it via the ulke field on each request; your key works only for the countries it's authorized for.
No extra parameter needed. For Albania, North Macedonia and Serbia, the response always includes both EUR and the local currency together — just read toll.lek (Albania), toll.denar (North Macedonia) or toll.rsd (Serbia) directly and ignore EUR. Greece and Bulgaria's local currency already is EUR. Turkey's result is already TL. See "Currency and Exchange Rates" above for the full breakdown.
No — the toll.eur/toll.lek fields are ALWAYS present in the contract, the engine never omits them. If a specific toll/facility shows 0 EUR but a positive Lek value, that just means that one tariff row's EUR figure hasn't been entered into our database yet — not an engine or API bug, a data gap on that single record. In that case, the Lek value is the authoritative figure — use it; let us know and we'll complete that tariff.
/toll — YOU build the route, we only price it (the API does not draw routes), all 7 countries. /route — you give only a start/end coordinate, we build the route; returns only the Turkish (+ optional Greek) toll cost. /classify — no route needed; it turns a single physical vehicle profile into every country's own class, all 7 of them, usually as a preparation step before deciding what to send to /toll.
No. Generating a new key in the panel immediately invalidates your previous one (an account can only have one active key), but your remaining hourly/daily/monthly usage allowance is not lost — limits are counted per account, not per key. So rotating your key is not a way to reset your limit.
Your key is limited to the countries chosen at signup (or later approved by an admin). Requesting a country outside that scope returns ulke_izni_yok. API Pro and Enterprise plans default to access for all countries. Contact us to add more countries.
When the trial ends your key becomes inactive. For uninterrupted use, higher limits and more countries, move to a paid plan — contact us to reactivate your key.
There are three independent limits: hourly, daily and monthly (see the Limits table above). Whichever one is hit returns 429 with the matching error code (gunluk_limit_asildi / aylik_limit_asildi); once that window rolls over, requests are accepted again. On the free trial plan, the real constraint is the daily cap of 100 requests. You can request higher limits for heavy usage.
Some Greek bridges such as Rio–Antirrio split the fare into up to 9 categories by vehicle type, seat count, axles, height and trailer. We need these to compute the correct bridge fare; if missing, a kopru_bilgi_eksik response tells you which field is required, along with the facility's real active categories (secenekler). Exception: for cars, we never ask a follow-up question — omit engelli to get the standard category automatically, or send engelli:true only when the vehicle genuinely holds a disabled/blue-card permit.
The denser the points, the more accurate the distance. Ideally a point every 50–100 m. Very sparse points (e.g. only turns) can make the distance appear shorter than it is.
It depends on the country: Turkey→TL, Greece/Bulgaria→EUR (+ TL equivalent), Albania/North Macedonia/Serbia→both EUR and the local currency (Lek/Denar/RSD, both always together, + TL equivalent). There is no "choose a currency" parameter — you always get every field that country's engine produces. The current exchange rate is provided in each response's eur_kur field (and lek_kur/denar_kur/rsd_kur where applicable). See "Currency and Exchange Rates" above for the full table.
Upgrading: Test all features during the trial. For permanent access,
higher limits and more countries, contact us.
🍪 Bu site, oturumunuzu açık tutmak, formları güvenli hale getirmek ve
ücretsiz sorgu hakkının kötüye kullanımını önlemek için zorunlu çerez
ve benzeri teknik teknolojiler kullanır. Detaylar için
Çerez Politikası'nı inceleyebilirsiniz.