API public PretCarburant.ro — REST v1
Acces programatic la datele despre prețurile carburanților din România. Aceleași date pe care le folosim și noi pe site, actualizate la fiecare 2 ore. Toate endpoint-urile răspund JSON și funcționează și fără autentificare, cu date sau limite reduse; o cheie API (antetul X-Api-Key) deblochează setul complet și o cotă lunară pe plan. Licență Creative Commons BY 4.0 — folosește liber, cu atribuire.
Quickstart: de la zero la primul apel
-
Ia-ți o cheie API (opțional, dar recomandat). Abonează-te pe pagina de planuri — cheia, în formatul
pcro_live_..., sosește pe email în câteva minute și vine cu 3 zile de probă gratuită. Poți sări peste acest pas: fără cheie, API-ul răspunde cu un set redus de date (detalii la Autentificare). -
Fă primul apel. Copiază comanda de mai jos în terminal (înlocuiește cheia; sau șterge linia cu
-Hca să încerci varianta publică):curl -s -H "X-Api-Key: pcro_live_CHEIA_TA" \ "https://pretcarburant.ro/api/v1/preturi" | jq '.rezultate[0]' -
Citește ce ai primit. Primul element din
rezultatearată așa (răspuns real):{ "benzina": 8.92, "benzina_premium": 9.4, "gpl": 4.23, "judet": "B", "lat": 44.42527, "lng": 26.01369, "motorina": 9.67, "motorina_premium": 10.49, "oras": "Bucuresti", "slug": "bucuresti" }Prețurile sunt în RON/litru, cele mai mici din orașul respectiv la momentul ultimei actualizări. Un preț pe care nu îl avem vine
null, nu0(vezi Convenții de date). Cu cheie primești toate orașele (~595); fără cheie, top-20 plus un câmpnotacare îți spune exact asta.
Autentificare: antetul X-Api-Key
Cheia API se trimite pe fiecare cerere, în antetul HTTP X-Api-Key. Nu există parametru de query pentru cheie și nu folosim Authorization: Bearer — doar antetul dedicat:
curl -s -H "X-Api-Key: pcro_live_CHEIA_TA" \
"https://pretcarburant.ro/api/v1/statii?brand=omv"
Fără cheie nu primești eroare, ci un set redus — poți prototipa tot API-ul înainte să plătești un leu: /api/v1/preturi întoarce top-20 orașe în loc de toate (~595), /api/v1/statii un eșantion de 150 de înregistrări în loc de setul complet (~6.000), iar endpoint-urile marcate au o cotă publică de 1 cerere pe săptămână per IP și endpoint. Cu cheie validă dispare cota săptămânală, primești setul complet și antetele X-RateLimit-* pe fiecare răspuns.
O cheie invalidă sau revocată primește însă HTTP 401 — semnal clar, nu degradare tăcută la setul public (răspuns real):
{
"message": "Invalid or revoked API key.",
"status": "error"
}
Ține cheia pe server, nu în JavaScript-ul public al paginii tale: oricine o vede o poate folosi și îți consumă cota. Statusul și consumul propriei chei se verifică oricând, gratuit, la /api/v1/cheie.
Specificație OpenAPI 3.1
Întregul API v1 este descris formal într-o specificație OpenAPI 3.1, disponibilă la https://pretcarburant.ro/openapi.json (alias: /api/v1/openapi.json). Conține toate endpoint-urile, parametrii, formele de răspuns și codurile de eroare — o poți importa direct în Postman sau Insomnia, poți genera un client în limbajul tău cu openapi-generator sau o poți da unui asistent de cod AI ca să scrie integrarea pentru tine. Se servește fără rate limit, cu cache de o oră și CORS deschis.
curl -s https://pretcarburant.ro/openapi.json | jq '.info.version'
Endpoint-uri disponibile
Baza URL: https://pretcarburant.ro/api/v1/. Toate răspunsurile sunt JSON; endpoint-urile publice de agregate au Cache-Control: public, max-age=300 și Access-Control-Allow-Origin: *. Fiecare rând din tabel duce la secțiunea detaliată a endpoint-ului.
| Endpoint | Descriere | Rate limit fără cheie |
|---|---|---|
GET /api/v1/preturi |
Prețuri agregate pe orașe (filtru opțional ?judet=CLUJ). Public: top 20 orașe; cu cheie API: toate (~595) |
Fără rate limit (cache 5 min) |
GET /api/v1/preturi/minime |
Prețurile minime, medii și maxime la nivel național, per tip de carburant | Fără rate limit (cache 5 min) |
GET /api/v1/judete/<judet> |
Preț mediu agregat la nivel de județ (ex: /api/v1/judete/CLUJ) |
1 cerere/săptămână per IP |
GET /api/v1/retele |
Listă rețele monitorizate cu prețuri agregate per rețea | 1 cerere/săptămână per IP |
GET /api/v1/statii |
Listă stații cu coordonate, brand, prețuri și id stabil per stație (filtre: ?brand=, ?tip=, ?lat=&lon=&raza=). Public: eșantion 150 înregistrări; cu cheie API: setul complet |
1 cerere/săptămână per IP |
GET /api/v1/statie/<station_id>/istoric |
Istoricul prețurilor pentru o stație (station_id = câmpul id din /api/v1/statii; id necunoscut → 404) |
1 cerere/săptămână per IP |
GET /api/v1/geocode |
Autocomplete pentru numele orașelor (?q=cluj, minim 2 caractere) |
1 cerere/săptămână per IP |
POST /api/v1/traseu/custom |
Stații de-a lungul unei rute A→B (body JSON: start, end, tip) |
1 cerere/săptămână per IP |
GET /api/v1/cheie |
Statusul și consumul propriei chei API (necesită X-Api-Key; nu consumă din cotă) |
— |
GET /api/v1/reviews/<slug> |
Recenziile publice ale unei stații (rating mediu + max 20 recenzii aprobate) | Fără rate limit |
POST /api/v1/review |
Adaugă un rating/recenzie la o stație (text liber cere reCAPTCHA v3) | Limite per IP (vezi secțiunea) |
GET /api/v1/de-statii |
Stații și prețuri live din Germania (diaspora), în EUR | 30 cereri/minut per IP |
GET /api/v1/push/vapid-keyPOST /api/v1/push/subscribe |
Notificări web push — folosite de site-ul propriu, nu pentru integrări comerciale | Fără rate limit |
GET /api/v1/health |
Starea datelor: prospețime și număr de înregistrări cu preț | Fără rate limit (fără cache) |
GET /openapi.json |
Specificația OpenAPI 3.1 (alias: /api/v1/openapi.json) |
Fără rate limit (cache 1h) |
Referință detaliată per endpoint
Toate exemplele de răspuns de mai jos sunt capturate din API-ul real, nu scrise de mână — valorile de preț sunt cele din ziua capturii, la tine vor fi cele curente. Listele lungi sunt trunchiate la primul element, cu un marcaj „… încă N" pentru rest.
GET /api/v1/preturi — prețuri pe orașe + medii pe rețele
Endpoint-ul principal: prețurile minime curente din fiecare oraș, plus media națională a fiecărei rețele — tot ce îți trebuie ca să afișezi „cât costă benzina în orașul X" sau să compari rețelele între ele. Fără cotă publică săptămânală (e sursa widgetului nostru embed); răspuns cacheabil 5 minute.
| Parametru | Tip | Obligatoriu | Implicit | Constrângeri / descriere |
|---|---|---|---|---|
judet | string | nu | — | Filtrează orașele pe un județ: nume complet (cu/fără diacritice) sau cod auto (ex. CJ, cluj) |
zeros | string | nu | — | 1/true/yes/da readuce sentinela veche 0 pentru prețurile lipsă (vezi convenția) |
curl -s https://pretcarburant.ro/api/v1/preturi | jq '.rezultate[0]'
Răspuns real (fără cheie, trunchiat):
{
"data": "2026-09-02",
"nota": "Acces public: top 20 orase. Setul complet (~595 orase) necesita o cheie API: https://pretcarburant.ro/api-preturi-carburanti",
"preturi_lipsa": "null",
"retele": [
{
"benzina": 8.92,
"benzina_premium": 9.4,
"culoare": "#E31937",
"gpl": 3.72,
"logo": "/static/img/brands/petrom.svg",
"motorina": 9.73,
"motorina_premium": 10.5,
"nume": "Petrom",
"slug": "petrom"
},
"… încă 7 rețele"
],
"rezultate": [
{
"benzina": 8.92,
"benzina_premium": 9.4,
"gpl": 4.23,
"judet": "B",
"lat": 44.42527,
"lng": 26.01369,
"motorina": 9.67,
"motorina_premium": 10.49,
"oras": "Bucuresti",
"slug": "bucuresti"
},
"… încă 19 orașe"
],
"status": "ok",
"total": 20
}
| Câmp | Tip | Semnificație |
|---|---|---|
status | string | Mereu "ok" pe 200 |
data | string (dată ISO 8601) | Data serverului |
total | integer | Numărul de orașe din rezultate |
rezultate[] | array de obiecte | Un obiect per oraș: oras, slug, judet (cod auto), lat/lng (number) și cele 5 prețuri benzina, benzina_premium, motorina, motorina_premium, gpl (number sau null, RON/litru) |
retele[] | array de obiecte | Un obiect per rețea: nume, slug, culoare (hex, pentru UI), logo (cale relativă) și aceleași 5 câmpuri de preț — media națională a rețelei |
preturi_lipsa | string | "null" sau "zero" — convenția activă pentru prețurile lipsă |
nota | string | Doar fără cheie: explică limitarea la top-20 orașe |
Cu cheie: rezultate conține toate orașele (~595 — la captură, 586), câmpul nota dispare, iar răspunsul poartă antetele X-RateLimit-*.
GET /api/v1/preturi/minime — minim / medie / maxim național
Agregatele naționale pe cele 5 tipuri de carburant — pentru un banner „azi benzina e între X și Y lei" sau pentru monitorizarea trendului fără să procesezi orașele. Complet public: fără autentificare, fără cotă săptămânală, cache 5 minute.
| Parametru | Tip | Obligatoriu | Implicit | Constrângeri / descriere |
|---|---|---|---|---|
zeros | string | nu | — | 1/true/yes/da — sentinela veche 0 pentru prețurile lipsă |
curl -s https://pretcarburant.ro/api/v1/preturi/minime | jq '.preturi'
Răspuns real (complet):
{
"data": "2026-09-02",
"preturi": {
"benzina_premium": {"max": 9.9, "mediu": 9.6, "min": 8.88},
"benzina_standard": {"max": 9.42, "mediu": 8.98, "min": 8.69},
"gpl": {"max": 5.0, "mediu": 4.34, "min": 3.61},
"motorina_premium": {"max": 10.89, "mediu": 10.53, "min": 10.2},
"motorina_standard": {"max": 10.49, "mediu": 9.74, "min": 9.27}
},
"preturi_lipsa": "null",
"status": "ok"
}
| Câmp | Tip | Semnificație |
|---|---|---|
preturi | obiect | Cheie = tipul de carburant (benzina_standard, benzina_premium, motorina_standard, motorina_premium, gpl) |
preturi.*.min / mediu / max | number sau null | Minimul, media și maximul național, RON/litru |
data, status, preturi_lipsa | — | Ca la /preturi |
GET /api/v1/judete/<judet> — media pe județ
Media peste toate orașele unui județ — pentru integrări ERP/TMS care bugetează pe județe, nu pe orașe. Acceptă numele complet (cu sau fără diacritice) sau codul auto. Sub cota publică săptămânală fără cheie.
| Parametru | Tip | Obligatoriu | Implicit | Constrângeri / descriere |
|---|---|---|---|---|
judet (în cale) | string | da | — | Nume județ sau cod auto: CLUJ, cluj, CJ, Timiș, timis |
zeros | string | nu | — | Sentinela veche 0 pentru prețurile lipsă |
curl -s https://pretcarburant.ro/api/v1/judete/CLUJ | jq
Răspuns real (complet):
{
"benzina_premium": 9.55,
"benzina_standard": 8.92,
"data": "2026-09-02",
"gpl": 4.35,
"judet": "CJ",
"motorina_premium": 10.52,
"motorina_standard": 9.65,
"nr_orase": 15,
"preturi_lipsa": "null",
"status": "ok"
}
| Câmp | Tip | Semnificație |
|---|---|---|
judet | string | Codul auto normalizat (indiferent cum ai scris în URL) |
nr_orase | integer | Peste câte orașe s-a făcut media |
benzina_standard … gpl | number sau null | Media județului per tip de carburant, RON/litru; null când niciun oraș din județ nu are prețul respectiv |
Județ necunoscut → HTTP 404 (răspuns real):
{
"message": "Judet necunoscut: XX",
"status": "error"
}
GET /api/v1/retele — mediile naționale pe rețele
Doar rețelele, fără orașe — pentru un comparator de branduri. Aceleași obiecte ca în câmpul retele din /preturi. Sub cota publică săptămânală fără cheie.
| Parametru | Tip | Obligatoriu | Implicit | Constrângeri / descriere |
|---|---|---|---|---|
zeros | string | nu | — | Sentinela veche 0 pentru prețurile lipsă |
curl -s https://pretcarburant.ro/api/v1/retele | jq '.retele[0]'
Răspuns real (trunchiat):
{
"data": "2026-09-02",
"preturi_lipsa": "null",
"retele": [
{
"benzina": 8.92,
"benzina_premium": 9.4,
"culoare": "#E31937",
"gpl": 3.72,
"logo": "/static/img/brands/petrom.svg",
"motorina": 9.73,
"motorina_premium": 10.5,
"nume": "Petrom",
"slug": "petrom"
},
"… încă 7 rețele"
],
"status": "ok"
}
GET /api/v1/statii — date la nivel de stație
Granularitatea maximă: fiecare înregistrare este o pereche stație × carburant — o stație fizică apare o dată pentru fiecare tip de carburant vândut. De aici iei coordonate pentru hartă, prețul exact de la o pompă anume și id-ul stabil cu care ceri istoricul. Sub cota publică săptămânală fără cheie.
| Parametru | Tip | Obligatoriu | Implicit | Constrângeri / descriere |
|---|---|---|---|---|
brand | string | nu | — | Filtru pe brand, case-insensitive (ex. omv, Petrom) |
tip | string | nu | — | Unul din: benzina_standard, benzina_premium, motorina_standard, motorina_premium, gpl |
lat | number | nu | — | Împreună cu lon activează filtrul geografic: doar stațiile din rază, sortate după distanță |
lon | number | nu | — | Longitudinea filtrului geo; lng e acceptat ca alias |
raza | number | nu | 10 | Raza filtrului geo, în km; limitată la intervalul 0.1–50 |
curl -s "https://pretcarburant.ro/api/v1/statii?brand=petrom&tip=motorina_standard" | jq '.statii[0]'
Răspuns real (fără cheie, trunchiat):
{
"data": "2026-09-02",
"nota": "Acces public: esantion de 150 inregistrari. Setul complet necesita o cheie API: https://pretcarburant.ro/api-preturi-carburanti",
"statii": [
{
"adresa": "Str. Libertatii 19A, 515500",
"brand": "Petrom",
"id": "2f4185850d36",
"judet": "Alba",
"lat": 46.3618,
"lng": 23.04885,
"oras": "Campeni",
"pret": 9.73,
"tip": "motorina_standard"
},
"… încă 149 înregistrări"
],
"status": "ok",
"total": 150,
"total_disponibil": 397
}
| Câmp | Tip | Semnificație |
|---|---|---|
total | integer | Câte înregistrări sunt în acest răspuns (după eventuala trunchiere publică) |
statii[].id | string | Identificator stabil al stației — cheia pentru /statie/<id>/istoric |
statii[].brand / oras / judet / adresa | string | Identificarea stației; judet vine cu numele județului |
statii[].lat / lng | number | Coordonatele stației |
statii[].tip | string | Tipul de carburant al acestei înregistrări |
statii[].pret | number sau null | RON/litru; null = preț indisponibil |
statii[].pret_expirat | boolean | Doar când e cazul: true dacă prețul a fost retras fiindcă sursa lui nu a mai publicat de peste 24h |
statii[].distanta_km | number | Doar cu filtrul geo: distanța față de punctul cerut, km |
total_disponibil | integer | Doar fără cheie, când răspunsul e trunchiat: câte înregistrări există în total pentru filtrele date |
nota | string | Doar fără cheie: explică eșantionul |
Cu cheie: setul complet (~6.000 de înregistrări — la captură, 6.189 fără filtre), fără total_disponibil și nota.
GET /api/v1/statie/<station_id>/istoric — istoricul unei stații
Seriile zilnice de preț ale unei stații — pentru grafice și pentru „s-a scumpit sau s-a ieftinit aici?". station_id este câmpul id din /statii. Sub cota publică săptămânală fără cheie.
| Parametru | Tip | Obligatoriu | Implicit | Constrângeri / descriere |
|---|---|---|---|---|
station_id (în cale) | string | da | — | Câmpul id al unei înregistrări din /api/v1/statii |
days | integer | nu | 30 | Câte zile de istoric; plafonat la 90 |
curl -s "https://pretcarburant.ro/api/v1/statie/2f4185850d36/istoric?days=7" | jq
Răspuns real (complet):
{
"labels": ["27.08", "28.08", "29.08", "30.08", "31.08", "01.09", "02.09"],
"series": {
"benzina_premium": [9.99, 9.99, 9.99, 9.99, 9.99, 9.99, 10.03],
"benzina_standard": [9.51, 9.51, 9.51, 9.51, 9.51, 9.51, 9.55],
"motorina_premium": [10.91, 10.91, 10.91, 10.91, 10.91, 10.74, 10.74],
"motorina_standard": [10.14, 10.14, 10.14, 10.14, 10.14, 9.97, 9.97]
},
"station_id": "2f4185850d36",
"status": "ok",
"variatie": {
"benzina_premium": 0.04,
"benzina_standard": 0.04,
"motorina_premium": 0.0,
"motorina_standard": 0.0
}
}
| Câmp | Tip | Semnificație |
|---|---|---|
labels[] | array de string | Eticheta de dată a fiecărui punct, în formatul de afișare zi.lună (ex. "27.08") — nu ISO 8601; ordinea e cronologică |
series | obiect | Cheie = tipul de carburant; valoare = lista de prețuri aliniată index-cu-index cu labels. Apar doar carburanții vânduți de stație |
variatie | obiect | Diferența de preț pe fereastra cerută (ultimul punct minus primul), per tip de carburant, RON/litru |
Id necunoscut sau stație fără istoric → HTTP 404 (răspuns real):
{
"message": "Statie necunoscuta sau fara istoric. Foloseste campul `id` din /api/v1/statii.",
"status": "error"
}
GET /api/v1/geocode — autocomplete orașe
Potrivește un fragment de text pe lista orașelor cunoscute de API — pentru un câmp de căutare cu sugestii. Sub cota publică săptămânală fără cheie.
| Parametru | Tip | Obligatoriu | Implicit | Constrângeri / descriere |
|---|---|---|---|---|
q | string | da | — | Fragment din numele orașului; sub 2 caractere se întoarce lista goală |
curl -s "https://pretcarburant.ro/api/v1/geocode?q=cluj" | jq
Răspuns real (complet) — cel mult 10 potriviri; răspunsul nu are câmpul status:
{
"results": ["Cluj-Napoca"]
}
POST /api/v1/traseu/custom — stații de-a lungul unei rute
Geocodează capetele, calculează ruta rutieră și întoarce stațiile aflate în raza dată de-a lungul ei — pentru „unde alimentez pe drumul București–Brașov?". Singurul endpoint POST de date; corpul cererii e JSON. Sub cota publică săptămânală fără cheie.
| Câmp | Tip | Obligatoriu | Implicit | Constrângeri / descriere |
|---|---|---|---|---|
start | string | da | — | Localitatea de plecare (ex. "Bucuresti") |
end | string | da | — | Localitatea de sosire (ex. "Brasov") |
tip | string | nu | benzina_standard | Tipul de carburant căutat (aceleași valori ca la /statii) |
raza | number | nu | 5 | Raza în km față de traseu; plafonată la 15 |
curl -s -X POST https://pretcarburant.ro/api/v1/traseu/custom \
-H "Content-Type: application/json" \
-d '{"start": "Bucuresti", "end": "Brasov", "tip": "motorina_standard", "raza": 5}'
Răspuns real (trunchiat). Atenție: acest endpoint folosește forma {ok, msg} pentru erori și ok: true pe succes, nu {status, message} ca restul API-ului:
{
"duration_min": 165,
"end": {"lat": 45.655, "lng": 25.611, "name": "Brasov"},
"km": 182.0,
"ok": true,
"start": {"lat": 44.4268, "lng": 26.1025, "name": "Bucuresti"},
"statii": [
{
"adresa": "Str. Fagarasului 2",
"brand": "Rompetrol",
"dist_ruta": 4.0,
"lat": 45.662296,
"lng": 25.574169,
"oras": "Brasov",
"pret": 9.67,
"tip": "motorina_standard"
},
"… restul stațiilor (max 50 în răspuns)"
],
"tip": "motorina_standard",
"total_statii": 153,
"waypoints": [
[44.425874, 26.102435],
[44.428773, 26.103985],
"… restul punctelor de pe rută (max ~200)"
]
}
| Câmp | Tip | Semnificație |
|---|---|---|
ok | boolean | true pe succes, false pe eroare |
start / end | obiect | name, lat, lng — capetele geocodate |
km, duration_min | number | Lungimea rutei și durata estimată de condus, în minute |
statii[] | array de obiecte | Stațiile din rază, cu dist_ruta (km față de traseu), pret, brand, adresa, oras, lat/lng, tip; cel mult 50 returnate |
total_statii | integer | Câte stații s-au găsit în total (poate depăși cele 50 returnate) |
waypoints[] | array de perechi [lat, lng] | Poligonul rutei, subeșantionat la ~200 de puncte — suficient pentru desenat pe hartă |
Localitate negăsită sau rută imposibilă → HTTP 400 (răspuns real):
{
"msg": "Nu am gasit locatia",
"ok": false
}
GET /api/v1/cheie — statusul propriei chei
Planul, limita și consumul cheii tale — pentru dashboard-ul tău intern sau pentru alerte de consum. Cere obligatoriu X-Api-Key, dar nu consumă din cota lunară și nu e supus cotei publice: verificarea propriului consum nu trebuie să coste.
curl -s -H "X-Api-Key: pcro_live_CHEIA_TA" https://pretcarburant.ro/api/v1/cheie | jq
Răspuns real (cheie de test pe planul Starter):
{
"abonament": false,
"consum_luna": 3,
"creata": "2026-09-02T20:32:19+00:00",
"limita_lunara": 10000,
"plan": "starter",
"plan_nume": "Starter",
"status": "ok",
"total_cereri": 3,
"ultima_folosire": "2026-09-02T20:32:19+00:00",
"zilnic": [{"count": 3, "zi": "2026-09-02"}]
}
| Câmp | Tip | Semnificație |
|---|---|---|
plan / plan_nume | string | Codul și numele planului (Starter/Pro/Business; Dedicat pentru chei cu contract separat) |
limita_lunara | integer | Cota lunară efectivă a cheii |
consum_luna | integer | Cereri consumate în luna calendaristică UTC curentă |
total_cereri | integer | Total istoric, peste toate lunile |
creata / ultima_folosire | string (ISO 8601) / poate fi null | Când a fost emisă cheia și când a fost folosită ultima dată |
abonament | boolean | true dacă cheia e legată de un abonament Stripe activ |
zilnic[] | array de obiecte | Consumul pe ultimele 30 de zile: {zi, count} |
Fără cheie sau cu cheie greșită → HTTP 401 (răspuns real):
{
"message": "Cheie invalida sau revocata.",
"status": "error"
}
GET /api/v1/reviews/<slug> — recenziile unei stații
Ratingul mediu și cel mult 20 de recenzii aprobate ale unei stații. slug-ul este cel din URL-urile paginilor noastre de stație (/statie/<slug>). Fără autentificare și fără cotă.
curl -s https://pretcarburant.ro/api/v1/reviews/omv-alba-iulia-1 | jq
Răspuns real (complet):
{
"avg_rating": 4.0,
"review_count": 1,
"reviews": [
{
"comment": "",
"created_at": "2026-05-05 13:08:32",
"id": 1,
"nickname": "Anonim",
"rating": 4
}
],
"status": "ok"
}
Un slug fără recenzii nu dă 404, ci avg_rating: 0, review_count: 0 și lista goală.
POST /api/v1/review — adaugă un rating
Trimite un rating (1–5 stele) sau o recenzie cu text la o stație. Ratingurile simple (doar stele) se acceptă direct; textul liber cere un token reCAPTCHA v3 și trece prin moderare înainte să apară public. Limite anti-abuz: o recenzie pe zi per stație per IP, maximum 3 recenzii pe zi per IP.
| Câmp | Tip | Obligatoriu | Implicit | Constrângeri / descriere |
|---|---|---|---|---|
statie_slug | string | da | — | Slug-ul stației |
rating | integer | da | — | Între 1 și 5 |
nickname | string | nu | Anonim | Maximum 50 de caractere |
comment | string | nu | — | Maximum 500 de caractere |
recaptcha_token | string | condițional | — | Obligatoriu când există text liber (comentariu sau nickname personalizat) |
curl -s -X POST https://pretcarburant.ro/api/v1/review \
-H "Content-Type: application/json" \
-d '{"statie_slug": "omv-alba-iulia-1", "rating": 5}'
Răspuns real la un rating simplu:
{
"avg_rating": 5.0,
"message": "Multumim pentru rating!",
"review_count": 1,
"status": "ok"
}
Rating lipsă sau în afara intervalului → HTTP 400 (răspuns real): {"message": "Rating invalid.", "status": "error"}. reCAPTCHA eșuat → 403; limitele per IP depășite → 429.
GET /api/v1/de-statii — stații din Germania (diaspora)
Prețuri live de la pompele din Germania, în EUR — pentru diaspora și pentru planificarea drumurilor prin Germania. Datele vin din Tankerkönig/MTS-K (CC BY 4.0) și sunt un canal complet separat de datele România: alte câmpuri, altă monedă. Limită proprie: 30 de cereri pe minut per IP (fără cotă săptămânală, fără cheie).
| Parametru | Tip | Obligatoriu | Implicit | Constrângeri / descriere |
|---|---|---|---|---|
lat | number | da | — | Latitudine, în interiorul Germaniei (47.0–55.2) |
lng | number | da | — | Longitudine, în interiorul Germaniei (5.5–15.5) |
rad | number | nu | 15 | Raza de căutare în km; limitată la 1–25 |
curl -s "https://pretcarburant.ro/api/v1/de-statii?lat=52.5200&lng=13.4050&rad=3" | jq '.statii[0]'
Răspuns real (Berlin, trunchiat):
{
"atribuire": "https://www.tankerkoenig.de",
"generat_la": "2026-09-02T23:31+03:00",
"lat": 52.52,
"lng": 13.405,
"moneda": "EUR",
"nr_statii": 10,
"rad_km": 3.0,
"statii": [
{
"adresa": "Holzmarktstraße 12/14",
"benzina": 2.259,
"brand": "ARAL",
"cod_postal": 10179,
"deschis": true,
"dist_km": 1.3,
"e10": 2.199,
"lat": 52.514153,
"lng": 13.421487,
"motorina": 2.279,
"nume": "Aral Tankstelle",
"oras": "Berlin"
},
"… încă 9 stații"
],
"status": "ok",
"sursa": "Tankerkönig / MTS-K (CC BY 4.0)",
"tara": "DE"
}
| Câmp | Tip | Semnificație |
|---|---|---|
tara / moneda | string | Mereu "DE" / "EUR" |
sursa / atribuire | string | Sursa datelor și linkul de atribuire cerut de licența ei |
statii[].benzina / e10 / motorina | number sau null | Prețuri în EUR/litru: Super E5, Super E10, motorină |
statii[].deschis | boolean | Dacă stația e deschisă acum |
statii[].dist_km | number | Distanța față de punctul cerut, km |
generat_la | string (ISO 8601) | Momentul generării răspunsului (cache 10 minute pe server) |
Coordonate lipsă sau în afara Germaniei → HTTP 400 (răspuns real): {"message": "Coordonatele trebuie să fie în Germania.", "status": "error"}. Sursa externă căzută → 502; serviciul neconfigurat → 503; peste 30 cereri/minut → 429.
Notificări web push — /api/v1/push/*
Două endpoint-uri folosite de service worker-ul site-ului nostru pentru alertele de preț din browser: GET /api/v1/push/vapid-key (cheia publică VAPID) și POST /api/v1/push/subscribe (înregistrează un abonament push: endpoint, p256dh, auth obligatorii, plus tip_carburant, prag_pret, oras opționale). Sunt documentate aici pentru completitudine — nu sunt destinate integrărilor comerciale și pot fi schimbate odată cu site-ul. Răspuns real de la cheia publică:
{
"publicKey": "BE7ueOZzVNlQLqeFLGAb8Oq6rLc2sIhf8_j9gcl5FTgYwo9eB2YX8u23O0HaIO9NXJGDLM-ClmPHqeyz3mIAHrQ",
"status": "ok"
}
GET /api/v1/health — starea datelor
Pentru monitorizare înainte și după punerea în producție: răspunde fără autentificare, fără rate limit și cu Cache-Control: no-store — un health check servit din cache ar ascunde exact incidentul pe care încerci să-l detectezi.
curl -s https://pretcarburant.ro/api/v1/health | jq
Răspuns pe date proaspete (HTTP 200):
{
"status": "ok",
"actualizat_la": "2026-09-02T06:30:00+00:00",
"vechime_secunde": 4210,
"statii_cu_pret": 5571
}
status—"ok"sau"degraded".actualizat_la— momentul ultimei actualizări reușite a datelor de preț, ISO 8601 (UTC);nullcând nu poate fi determinat.vechime_secunde— de câte secunde nu s-au mai actualizat datele.statii_cu_pret— câte înregistrări stație×carburant au preț valid în acest moment (nu stații fizice unice);0înseamnă că nu servim date.
Codul HTTP spune tot: 200 = date proaspete și nevide; 503 (cu status: "degraded") = ultima actualizare e mai veche de 24 de ore sau nu servim niciun preț. Un monitor care verifică doar codul de stare este suficient. Corpul rămâne același și pe 503 — răspuns real, capturat pe un mediu cu date vechi:
{
"actualizat_la": "2026-05-01T00:00:00+00:00",
"statii_cu_pret": 5571,
"status": "degraded",
"vechime_secunde": 10787465
}
Catalog de erori
Corpurile de eroare de mai jos sunt capturate din API-ul real. Regula generală: forma e {"status": "error", "message": "..."} plus câmpuri suplimentare unde e cazul; singura excepție e /traseu/custom, care folosește {"ok": false, "msg": "..."}. Nu parsa textul din message — nu face parte din contract; decide după codul HTTP și după câmpurile structurate.
| Cod | Când apare | Ce să faci |
|---|---|---|
| 400 | Cerere invalidă: rating în afara intervalului, coordonate în afara Germaniei, localitate negăsită pe rută | Corectează cererea; nu reîncerca aceeași cerere neschimbată |
| 401 | Ai trimis X-Api-Key, dar cheia e invalidă sau revocată | Verifică cheia (spații, prefix pcro_live_); nu reîncerca automat — 401 nu se repară singur. Fără antetul X-Api-Key nu primești niciodată 401 |
| 404 | Județ necunoscut sau stație fără istoric | Verifică inputul: codul de județ, respectiv câmpul id luat din /api/v1/statii |
| 429 (cu cheie) | Cota lunară a planului e depășită | Citește Retry-After și antetele X-RateLimit-*; oprește cererile până la reset sau treci pe un plan mai mare |
| 429 (fără cheie) | Cota publică de 1 cerere/săptămână/IP/endpoint e consumată | Folosește next_allowed / retry_after_seconds din corp; pentru volum, ia o cheie API |
| 502 | Doar /de-statii: sursa de date externă nu răspunde | Reîncearcă cu backoff — e o problemă tranzitorie a sursei |
| 503 | /health pe date vechi/lipsă; /de-statii neconfigurat | Tratează ca „datele nu sunt de încredere acum": alertează, nu consuma răspunsul |
401 — cheie invalidă (răspuns real):
{
"message": "Invalid or revoked API key.",
"status": "error"
}
429 cu cheie — cota lunară a planului depășită (răspuns real; vine împreună cu antetele X-RateLimit-* și Retry-After):
{
"limit": 10000,
"message": "Cvota lunara a planului (10000 cereri) a fost depasita.",
"status": "error",
"upgrade_url": "https://pretcarburant.ro/api-preturi-carburanti"
}
429 fără cheie — cota publică săptămânală consumată (răspuns real):
{
"message": "Rate limit: 1 cerere pe saptamana. Urmatoarea cerere permisa peste 6z 23h.",
"next_allowed": "2026-09-09T23:30:59.057642",
"retry_after_seconds": 604793,
"status": "error"
}
503 — health check pe date învechite (răspuns real): vezi exemplul din secțiunea health; corpul păstrează aceeași formă ca pe 200, cu status: "degraded".
Mostre de cod
Același apel — prețurile pe orașe, cu cheie API — în patru limbaje. Fiecare mostră tratează 401 și 429 și citește antetul X-RateLimit-Remaining, pentru că exact astea sunt lucrurile care se uită din happy path.
curl
curl -s -w "\nHTTP %{http_code}\n" \
-H "X-Api-Key: pcro_live_CHEIA_TA" \
"https://pretcarburant.ro/api/v1/preturi" | jq '.rezultate[0]'
Python (requests)
import requests
API_KEY = "pcro_live_CHEIA_TA"
r = requests.get(
"https://pretcarburant.ro/api/v1/preturi",
headers={"X-Api-Key": API_KEY},
timeout=10,
)
if r.status_code == 401:
raise SystemExit("Cheie API invalida sau revocata.")
if r.status_code == 429:
raise SystemExit(f"Cota depasita; reincearca peste {r.headers.get('Retry-After')} secunde.")
r.raise_for_status()
print("Cereri ramase luna aceasta:", r.headers.get("X-RateLimit-Remaining"))
for oras in r.json()["rezultate"]:
if oras["benzina"] is not None: # pretul lipsa vine null, nu 0
print(f'{oras["oras"]}: {oras["benzina"]} RON/l')
JavaScript (fetch, Node 18+)
const API_KEY = "pcro_live_CHEIA_TA";
const res = await fetch("https://pretcarburant.ro/api/v1/preturi", {
headers: { "X-Api-Key": API_KEY },
});
if (res.status === 401) throw new Error("Cheie API invalida sau revocata.");
if (res.status === 429) {
throw new Error(`Cota depasita; reincearca peste ${res.headers.get("Retry-After")} s`);
}
if (!res.ok) throw new Error(`HTTP ${res.status}`);
console.log("Cereri ramase luna aceasta:", res.headers.get("X-RateLimit-Remaining"));
const date = await res.json();
for (const oras of date.rezultate) {
if (oras.benzina !== null) { // pretul lipsa vine null, nu 0
console.log(`${oras.oras}: ${oras.benzina} RON/l`);
}
}
PHP (curl)
<?php
$apiKey = "pcro_live_CHEIA_TA";
$ramase = null;
$ch = curl_init("https://pretcarburant.ro/api/v1/preturi");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ["X-Api-Key: $apiKey"],
CURLOPT_TIMEOUT => 10,
CURLOPT_HEADERFUNCTION => function ($ch, $header) use (&$ramase) {
if (stripos($header, "X-RateLimit-Remaining:") === 0) {
$ramase = trim(substr($header, 22));
}
return strlen($header);
},
]);
$body = curl_exec($ch);
$cod = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);
if ($cod === 401) { exit("Cheie API invalida sau revocata.\n"); }
if ($cod === 429) { exit("Cota depasita — vezi antetul Retry-After.\n"); }
echo "Cereri ramase luna aceasta: $ramase\n";
$date = json_decode($body, true);
foreach ($date["rezultate"] as $oras) {
if ($oras["benzina"] !== null) { // pretul lipsa vine null, nu 0
echo $oras["oras"] . ": " . $oras["benzina"] . " RON/l\n";
}
}
Convenții de date
Reguli valabile în tot API-ul v1 — citește-le o dată și scutești o zi de debugging:
- Unitate: toate prețurile România sunt în RON/litru; doar
/de-statiie în EUR/litru. - Separator zecimal: în JSON, mereu punctul (
8.92) — standardul JSON. Formatarea cu virgulă pentru afișare (8,92 lei) e treaba clientului. - Date calendaristice: câmpul
datae dată ISO 8601 ("2026-09-02"), data zilei pe server (fus orar România). Timpii compleți (actualizat_la,creata,generat_la) sunt ISO 8601 cu offset explicit. Singura excepție:labelsdin istoric, etichete de afișare în formatzi.lună. - Cota lunară se contorizează pe luna calendaristică UTC;
X-RateLimit-Resete un timestamp Unix (secunde, UTC). - Ordinea cheilor JSON nu e garantată (răspunsurile vin cu cheile sortate alfabetic azi, dar nu te baza pe asta) și pot apărea oricând câmpuri noi — parserul tău trebuie să ignore ce nu cunoaște (vezi versionare).
Toate răspunsurile de succes ale endpoint-urilor de date au structura comună:
{
"status": "ok",
"data": "2026-09-02",
"preturi_lipsa": "null",
"rezultate": []
}
În caz de eroare:
{
"status": "error",
"message": "Rate limit: 1 cerere pe saptamana. Urmatoarea cerere permisa peste 6z 23h.",
"retry_after_seconds": 604793
}
Prețuri lipsă: null, nu 0
Când nu avem prețul unui carburant într-un oraș sau la o rețea, câmpul vine null. Niciun carburant nu costă zero lei — intervalele reale sunt 5,50–12,00 RON/L la benzină, 5,50–13,00 la motorină și 2,50–6,00 la GPL — deci un 0 nu ar fi o măsurătoare, ci o valoare inventată care trage în jos orice medie calculată peste răspunsul nostru. Regula se aplică pe /preturi (atât în rezultate[], cât și în retele[]), pe /preturi/minime (câmpurile min, mediu, max), pe /judete/<judet> și pe /retele. Dacă faci medii peste răspuns, exclude valorile null — un zero tratat ca preț îți falsifică media.
Se convertesc numai câmpurile de preț. Un 0 la total, nr_orase, lat sau lng rămâne 0, pentru că acolo zero e un răspuns real, nu o valoare lipsă. Câmpul pret din /statii venea null și înainte.
{
"oras": "Turda", "slug": "turda", "judet": "CJ",
"lat": 46.5667, "lng": 23.7833,
"benzina": 7.15, "benzina_premium": null,
"motorina": 7.40, "motorina_premium": null, "gpl": null
}
Pentru integrările care nu pot fi actualizate odată cu noi, ?zeros=1 (acceptă și ?zeros=true) readuce exact comportamentul vechi, cu 0 în loc de null:
curl -s "https://pretcarburant.ro/api/v1/preturi?zeros=1" | jq '.rezultate[0]'
Fiecare răspuns își declară convenția în câmpul preturi_lipsa: "null" (implicit) sau "zero" (cu ?zeros=1). Citește-l în cod în loc să presupui — e singurul mod sigur de a ști ce ai primit. Conversia merge într-un singur sens: un null nu devine niciodată 0, nici cu ?zeros=1, pentru că /judete/<judet> răspundea null și înainte când nu avea din ce să facă media.
Limite și cote
Fără cheie: endpoint-urile de agregate /preturi și /preturi/minime nu au rate limit (cache 5 minute), la fel /reviews, /health și /openapi.json. Endpoint-urile /judete, /retele, /statii, /statie/<id>/istoric, /geocode și /traseu/custom au cota publică de 1 cerere pe săptămână per IP și endpoint. /de-statii are limita proprie de 30 cereri/minut per IP.
Cu cheie API: dispare cota săptămânală și primești o cotă lunară pe plan (luna calendaristică UTC):
| Plan | Preț | Cotă lunară |
|---|---|---|
| Fără cheie (CC-BY 4.0) | gratuit | set redus + cota publică săptămânală |
| Starter | 19 €/lună | 10.000 cereri |
| Pro | 49 €/lună | 100.000 cereri |
| Business | 149 €/lună | 1.000.000 cereri (suport prioritar) |
La depășirea cotei lunare primești 429 cu corpul din catalogul de erori, antetele X-RateLimit-* și Retry-After calculat până la resetarea reală a lunii. Apelurile către /api/v1/cheie nu consumă din cotă.
Pentru asistenții AI (ChatGPT, Claude, Perplexity, Gemini, Bing Copilot, etc.) am implementat un whitelist explicit pe User-Agent. Aceștia bypass-ează rate limit-ul săptămânal și au în loc o limită de 60 cereri/minut/IP, exact ca să poată cita datele noastre direct în răspunsuri. User-Agent-uri recunoscute: GPTBot, ChatGPT-User, OAI-SearchBot, ClaudeBot, anthropic-ai, PerplexityBot, Google-Extended, Googlebot, Applebot-Extended, Bytespider, Meta-ExternalAgent, CCBot, MistralAI, cohere-ai, YouBot, DiffBot, Bravebot și altele.
Antetele de cotă X-RateLimit-*
Orice cerere cu o cheie API validă (header X-Api-Key) primește trei antete de răspuns care spun exact unde te afli cu consumul lunar — pe orice endpoint, inclusiv pe cele publice:
X-RateLimit-Limit— cota lunară a planului tău (ex.10000pe Starter).X-RateLimit-Remaining— câte cereri mai ai până la resetare (niciodată negativ).X-RateLimit-Reset— timestamp Unix (secunde, UTC) al momentului resetării: începutul următoarei luni calendaristice UTC, fereastra reală a contorizării.
Antetele vin și pe răspunsul 429 de cotă depășită — exact atunci când ai cea mai mare nevoie de ele — împreună cu Retry-After: numărul de secunde până la resetarea cotei, calculat real, nu o valoare fixă. Exemplu de citire:
curl -s -D - -o /dev/null -H "X-Api-Key: CHEIA_TA" \
https://pretcarburant.ro/api/v1/statii
HTTP/2 200
X-RateLimit-Limit: 10000
X-RateLimit-Remaining: 9977
X-RateLimit-Reset: 1790812800
1790812800 înseamnă 1 octombrie 2026, 00:00 UTC. Când X-RateLimit-Remaining se apropie de zero, e momentul să treci pe un plan mai mare — după resetare contorul pleacă din nou de la cota întreagă.
Licență și atribuire
Datele sunt publicate sub Creative Commons BY 4.0. Le poți folosi în produse comerciale și non-comerciale, cu atribuire vizibilă: "Sursa: PretCarburant.ro (https://pretcarburant.ro)".
Setul de date complet este publicat și pe Zenodo cu DOI: 10.5281/zenodo.19560194. Identitatea organizației pe Wikidata: Q139285387.
Acces comercial — planuri self-serve
Tier-ul gratuit CC-BY 4.0 rămâne neschimbat: 1 cerere/săptămână per IP pe endpoint-urile non-publice, cu atribuire vizibilă. Nu îl închidem și nu îl plafonăm — datele publice rămân publice.
Pentru aplicații, flote, redacții și orice integrare cu volum mare, există planuri comerciale cu activare imediată — cheia API sosește pe email în câteva minute după abonare:
- Starter — 19 €/lună, 10.000 cereri/lună
- Pro — 49 €/lună, 100.000 cereri/lună
- Business — 149 €/lună, 1.000.000 cereri/lună, suport prioritar
Toate includ 3 zile de probă gratuită, acces la toate endpoint-urile fără cvota săptămânală și headerele X-RateLimit-* pentru monitorizarea consumului. Vezi planurile și abonează-te →
Nevoi speciale (SLA contractual, export bulk, snapshot-uri istorice, drepturi extinse fără atribuire)? Scrie-ne pentru o ofertă dedicată.
Versionare și compatibilitate
Ce garantăm pentru /api/v1 — o politică simplă pe care chiar o putem ține:
- Nu ștergem și nu redenumim câmpuri existente și nu le schimbăm tipul în interiorul lui v1. O schimbare incompatibilă ar însemna un
/api/v2— care azi nu există și pe care nu îl plănuim. - Adăugăm câmpuri și endpoint-uri noi fără preaviz. Sunt schimbări aditive, sigure prin construcție — cu condiția ca parserul tău să ignore câmpurile necunoscute (fă asta; e singura cerință pe care o punem clienților).
- Valorile nu sunt contract: prețurile, numărul de orașe și de stații se schimbă la fiecare actualizare de date. Textele din
message,msgșinotapot fi reformulate oricând — nu le parsa. - Schimbările se anunță în istoricul de versiuni de pe această pagină, iar contractul formal e versionat semver în
/openapi.json(info.version): patch = corecturi de descriere, minor = adăugiri, major = niciodată, în interiorul lui v1. - Clienții plătitori primesc pe email un anunț înainte de orice schimbare care i-ar putea afecta.
Istoric versiuni
- v1.0 (2026-04-01) — primele 5 endpoint-uri publice; cache 5 min; CORS deschis.
- v1.1 (2026-04-14) — bot whitelist UA pentru AI assistants; per-IP flood cap 60 req/min.
- v1.2 (2026-05-07) — documentație publică formală la
/api(RO/EN/HU). - v1.3 (2026-07-26) — planuri comerciale self-serve cu chei API (header
X-Api-Key), cvote lunare per plan și headereleX-RateLimit-Limit/X-RateLimit-Remaining; endpoint-urile/geocodeși/traseu/customdocumentate. - v1.4 (2026-08-16) — prețurile lipsă vin
null, nu0, pe/preturi,/preturi/minime,/judeteși/retele; parametrul?zeros=1readuce formatul vechi, iar câmpulpreturi_lipsadeclară în fiecare răspuns ce convenție s-a folosit. - v1.5 (2026-09-02) — specificație OpenAPI 3.1 la
/openapi.json; endpoint de health checkGET /api/v1/health; antetulX-RateLimit-Reset, plus antetele de cotă pe toate răspunsurile cu cheie (inclusiv pe 429), cuRetry-Aftercalculat până la resetarea reală a lunii; manual complet pe această pagină: quickstart, referință per endpoint cu răspunsuri reale, catalog de erori, mostre de cod.
Contact și raportare bug-uri
Email: contact@pretcarburant.ro. Pentru bug-uri tehnice sau cereri de feature, scrie cu subiectul prefixat „[API]". Răspundem în maxim 48h în zilele lucrătoare.
Disclosure responsabilă pentru probleme de securitate: vezi /.well-known/security.txt.