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 de mai multe ori pe zi. 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 cu cel puțin un preț (367 acum); 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 cele cu preț (367 acum), /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 și cu CORS deschis; /openapi.json vine cu Cache-Control: public, max-age=300 (5 minute), iar aliasul /api/v1/openapi.json cu no-store, ca restul API-ului.
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, cu Access-Control-Allow-Origin: * și Cache-Control: no-store: niciun cache intermediar (CDN, proxy, browser) nu le păstrează, deci fiecare cerere ajunge la server. Dacă ai nevoie de cache, ține-l la tine — prețurile se schimbă de câteva ori pe zi, iar câmpul actualizat_la îți spune cât de noi sunt. O cale greșită sub /api/ răspunde 404 cu un corp JSON, nu cu o pagină HTML. 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 cele cu cel puțin un preț (367 acum) |
Fără rate limit |
GET /api/v1/preturi/minime |
Prețurile minime, medii și maxime la nivel național, per tip de carburant | Fără rate limit |
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 5 min doar pe /openapi.json) |
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); ca tot API-ul, răspunsul vine cu Cache-Control: no-store.
| 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 (367 orase cu preturi) 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) | Ziua serverului în momentul răspunsului — nu data prețurilor: după miezul nopții sau cu o sursă căzută arată tot ziua curentă |
actualizat_la | string (ISO 8601 cu offset) sau null | Momentul ultimei actualizări reușite a datelor de preț — același calcul și aceeași valoare ca în /health. Câmp adăugat pe 25.09.2026 (lipsește din exemplul capturat mai sus) |
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 cu cel puțin un preț (367 acum; orașele în care avem doar stații fără preț nu apar), 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 și fără cotă săptămânală.
| 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, actualizat_la, 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 |
data, actualizat_la | — | Ca la /preturi: ziua serverului, respectiv momentul datelor |
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, plus data și actualizat_la cu același înțeles. 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, adblue (AdBlue, doar la unele stații) |
lat | number | nu | — | Împreună cu lon activează filtrul geografic: doar stațiile din rază, sortate după distanță. Un număr nefinit (nan, inf) dă 400 |
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 |
|---|---|---|
actualizat_la | string sau null | Momentul ultimei actualizări reușite a prețurilor, ISO 8601 cu offset — același ca în /api/v1/health |
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. Pinul fără preț nu se repetă lângă înregistrarea cu preț a aceleiași perechi id + tip |
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 (prezent pe fiecare; adblue la unele stații) |
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[].nesigur | boolean | Doar când e cazul: true dacă prețul e servit, dar site-ul nu se bazează pe el (blocat, orfan sau cu observație veche) — îl scoate din minime și topuri, iar la blocat și observatie_veche scrie și „Preț nesigur” pe pagina stației. Un minim calculat de voi peste răspuns trebuie să excludă aceste înregistrări; minimul național al site-ului ocolește în plus prețurile izolate sau aberante |
statii[].motiv_nesigur | string | Doar cu nesigur: blocat (preț neschimbat de cel puțin 14 zile, cât restul rețelei și-a schimbat prețul), observatie_veche (sursa nu l-a mai raportat de cel puțin 7 zile) sau orfan_anpc (Monitorul Prețurilor nu mai trimite rândul) |
statii[].nesigur_din | string sau null | Doar cu nesigur: ziua de la care prețul e neschimbat (blocat) sau ziua ultimei observații (observatie_veche); null la orfan_anpc. ANPC datează prețul cu ziua raportării, deci observat_la poate fi recent și la un preț blocat |
statii[].pret_masurat_la | string | Când sursa o dă: data măsurătorii exact cum vine de la sursă (ziua ANPC, momentul paginii SOCAR). Pentru vârsta prețului servit folosiți observat_la |
statii[].pret_incoerent | boolean | Doar când e cazul: prețul premium a fost retras fiindcă era sub standard la aceeași pompă (pret e null) |
statii[].nume / franciza / nota_pret / program / servicii / telefon | string / boolean | Când sursa le dă: numele stațiilor adăugate manual; stațiile SOCAR în franciză, pentru care rețeaua nu publică prețul, cu o notă; programul, serviciile și telefonul stației |
statii[].coord_suspecta / approximate_coords | boolean | Doar când e cazul: coordonatele rețelei cad în alt județ decât cel declarat, respectiv sunt aproximative |
statii[].observat_la | string sau null | Când a fost observat prețul, ISO 8601: pentru Monitorul Prețurilor (ANPC) publicăm doar ziua raportării, fără oră ("2026-09-22"); pentru pagina SOCAR și raportările manuale, momentul cu fus orar; null = sursa nu spune când sau nu există preț. Nu e niciodată ora la care am descărcat sursa |
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. Din 28.09.2026 răspunsul nu mai conține pinii rămași fără preț lângă o înregistrare cu preț a aceleiași stații și aceluiași carburant, nici înregistrări fără tip.
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, număr JSON pozitiv; plafonată la 15. Text, null sau un număr nefinit dau 400 |
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ă, rută imposibilă sau corp invalid (nu e obiect JSON, start/end/tip nu sunt text, raza nu e un număr pozitiv) → HTTP 400, tot în forma {ok, msg} (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, corp care nu e obiect JSON sau câmpuri text de alt tip → 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. Pe server, răspunsurile se păstrează 10 minute pe coordonate rotunjite la 0,01 grade și rază rotunjită la km — lat/lng pot fi cele ale cererii care a umplut cache-ul |
Coordonate lipsă, nefinite (nan, inf) 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, cu antetul Retry-After.
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 pagina — calea paginii pe care s-a abonat vizitatorul, din care serverul deduce orașul și carburantul abonamentului; fără ea, sau pe o pagină fără oraș, abonamentul e național. Câmpurile vechi tip_carburant, prag_pret și oras sunt acceptate, dar ignorate din 23.09.2026. Răspunsul întoarce oras și tip_carburant deduse, null când nu există). 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 real (HTTP 200, 25.09.2026):
{
"actualizat_la": "2026-09-24T21:15:07.097831+00:00",
"inregistrari_total": 6656,
"observatie_cea_mai_veche": "2026-09-21",
"preturi_disponibile": 5481,
"preturi_fara_data_observatie": 0,
"statii_cu_pret": 5481,
"statii_cu_pret_unice": 1348,
"statii_total": 1705,
"status": "ok",
"vechime_maxima_descarcare_secunde": 19324,
"vechime_maxima_secunde": 19324,
"vechime_secunde": 2816
}
status—"ok"sau"degraded".actualizat_la— cea mai recentă scriere reușită a unei surse de prețuri, ISO 8601 cu offset;nullcând nu poate fi determinată. Aceeași valoare caactualizat_lade pe/preturi,/preturi/minime,/reteleși/judete.vechime_secunde— secunde de la acea scriere. Informativ: nu el decide starea.vechime_maxima_secunde— secunde de la cea mai veche descărcare reușită dintre sursele de prețuri aflate în serviciu; el decide starea.vechime_maxima_descarcare_secunde— același număr, cu numele corect: măsoară descărcarea, nu vârsta prețurilor.observatie_cea_mai_veche— cel mai vechi moment de observație dintre prețurile servite, așa cum îl publică sursa (dată, sau dată și oră);nullcând niciun preț servit nu are unul.preturi_fara_data_observatie— câte prețuri servite nu au nicio dată de observație.statii_cu_pret— câmp istoric: înregistrări stație×carburant cu preț nenul, nu stații fizice.inregistrari_total— toate înregistrările stație×carburant, inclusiv cele cu preț lipsă sau expirat.preturi_disponibile— înregistrări cu preț valid după expirare;0înseamnă că nu servim prețuri.statii_total— stații fizice distincte de pe hartă, inclusiv cele fără preț.statii_cu_pret_unice— stații fizice distincte cu cel puțin un preț valid.
Codul HTTP spune tot: 200 = fiecare sursă de prețuri aflată în serviciu a scris în ultimele 24 de ore și servim prețuri; 503 (cu status: "degraded") = cel puțin o sursă de prețuri în serviciu e mai veche de 24 de ore (o sursă vie nu ascunde una căzută), nu mai e nicio sursă în serviciu sau nu servim niciun preț. Sursele scoase din serviciu printr-o decizie nu sunt judecate. Un monitor care verifică doar codul de stare este suficient; pe 503 corpul are aceleași 12 câmpuri.
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ă, un număr nefinit (nan, inf) într-un parametru, un corp JSON care nu e obiect sau un câmp text de alt tip | 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, stație fără istoric sau o cale inexistentă sub /api/ (corp JSON {status, message, message_en, docs}, nu pagină HTML) | Verifică inputul: codul de județ, respectiv câmpul id luat din /api/v1/statii, respectiv calea endpoint-ului |
| 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 (sau antetul Retry-After); pentru volum, ia o cheie API — linkul e în upgrade_url și în antetul Link cu rel="payment" |
| 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 |
| 503 (cu cheie) | Ai trimis X-Api-Key, dar verificarea cheilor e temporar indisponibilă la noi — cheia nu e declarată invalidă | Reîncearcă după Retry-After (egal cu retry_after_seconds din corp) |
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; vine cu antetele Retry-After și Link cu rel="payment" spre pagina de planuri):
{
"message": "Rate limit: 1 cerere pe saptamana. Urmatoarea cerere permisa peste 6z 23h.",
"message_en": "Rate limit: 1 request per week per IP for this endpoint without an API key. Next request allowed in 6d 23h.",
"next_allowed": "2026-09-09T23:30:59.057642+03:00",
"retry_after_seconds": 604793,
"status": "error",
"upgrade_mesaj": "O cheie API inlocuieste limita saptamanala cu cota lunara a planului (de la 10.000 cereri/luna).",
"upgrade_message": "An API key replaces the weekly limit with a monthly plan quota (from 10,000 requests/month).",
"upgrade_url": "https://pretcarburant.ro/api-preturi-carburanti",
"upgrade_url_en": "https://pretcarburant.ro/en/fuel-price-api"
}
503 — health check pe date învechite: corpul are aceeași formă ca pe 200 (vezi health), cu status: "degraded".
503 cu cheie — verificarea cheilor temporar indisponibilă (corpul exact; vine cu antetul Retry-After: 30):
{
"message": "Verificarea cheii API e temporar indisponibila. Reincearca peste 30 de secunde.",
"message_en": "API key verification is temporarily unavailable. Retry in 30 seconds.",
"retry_after_seconds": 30,
"status": "error"
}
404 — cale inexistentă sub /api/ (corpul exact; 410 are aceeași formă):
{
"docs": "https://pretcarburant.ro/api",
"message": "Endpoint inexistent.",
"message_en": "Not found.",
"status": "error"
}
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) în momentul răspunsului — nu data prețurilor; cât de noi sunt prețurile spuneactualizat_la. Timpii compleți (actualizat_la,creata,generat_la,next_allowed) 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, 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. Limitele pe minut (/de-statii, boții AI) sunt comune tuturor proceselor serverului și răspund 429 cu antetul Retry-After.
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)".
Licența se aplică tuturor răspunsurilor /api/v1, cu sau fără cheie API: abonamentul plătește accesul (setul complet și cota lunară), nu o altă licență. Fiecare răspuns o declară și în antetul Link cu rel="license".
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. - v1.6 (2026-09-23) — răspunsul 429 al cotei publice săptămânale spune unde se ia cheia: câmpurile aditive
upgrade_url,upgrade_url_en,message_en,upgrade_mesaj/upgrade_messageși anteteleRetry-AfterșiLinkcurel="payment". Câmpurile existente rămân neschimbate. - v1.7 (2026-09-25) — câmpul aditiv
actualizat_lape/preturi,/preturi/minime,/reteleși/judete(momentul datelor;datarămâne ziua serverului);next_allowedpoartă offset explicit; baza de chei indisponibilă dă 503 cuRetry-After, nu 401; o cale inexistentă sub/api/răspunde 404 JSON; parametrii nefiniți (nan,inf) și corpurile JSON de alt tip dau 400, nu 500; antetulRetry-Afterpe 429-urile pe minut. Documentația spune acum cache-ul real:no-storepe tot/api/(mențiunile „cache 5 min" de pe/preturierau greșite). - v1.8 (2026-09-28) —
/statii: câmpurile aditivenesigur,motiv_nesigurșinesigur_dinpe prețurile blocate, orfane sau cu observație veche, pe care site-ul nu le folosește în minime;actualizat_lași aici; valoareaadbluelatip, documentată; fără pinii dublați fără preț și fără înregistrări fărătip; câmpurile interne care nu erau în specificație nu mai pleacă (ale propunerilor din aplicație,geo_dedus,geo_incercat,coord_corectata,added_at); stațiile aprobate care n-au încă niciun preț nu mai apar ca rând fărătip. Minimul la premium ocolește premiumul raportat egal cu standardul aceleiași pompe./traseu/customnu mai pune pe listă prețurile blocate sau orfane (cele doar vechi rămân)./preturi(cu cheie) și/judetenumără doar orașele cu cel puțin un preț, iar numărul lor e scris pe pagină din date, nu de mână.
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.