e-bon
e-bon.ro
Referință API

Limite de rată

Cum limitează API-ul e-bon traficul de cereri, ce înseamnă antetele RateLimit, cum arată un răspuns 429 și cum să reîncerci corect din integrarea ta POS.

Limite de rată

API-ul e-bon limitează traficul primit ca să rămână rapid pentru toată lumea. Această pagină îți arată ce limite se aplică cererilor tale, cum le citești din antetele răspunsului și cum reîncerci în siguranță atunci când atingi una.

Pe scurt — trei contoare independente (Global, Autentificare, Comenzi), toate cu antete IETF draft-7RateLimit-*, toate returnând același corp plat {code, message, status} la 429, plus un antet Retry-After în secunde întregi. Citește antetele din timp, respectă Retry-After, adaugă jitter.

Înțelege cele trei contoare

Fiecare cerere este numărată în unul sau mai multe contoare independente. Primul plafon pe care îl atingi este cel mai mic dintre cele aplicabile.

ContorFereastrăPlafonIndexat dupăSe aplică la
Global10 min150 cerericheia API (sau IP client)Orice cerere către https://api.e-bon.ro
Autentificare10 min30 cereriIP clientPOST /api/v1/auth/* (login, înregistrare, refresh)
Comenzi10 min50 cerericheia API (sau IP client)POST /api/v1/commands și endpoint-urile de comenzi pe dispozitiv

Câteva puncte practice:

  • O singură cheie API împarte un contor Global și un contor Comenzi. Două sute de dispozitive care folosesc aceeași cheie împart aceleași 50 de comenzi la 10 minute.
  • Contorul Autentificare este indexat doar după IP. Mai mulți utilizatori în spatele aceluiași NAT sau Wi-Fi de birou împart același contor — vezi Gestionează contorul Autentificare pe IP-uri partajate mai jos.
  • Contoarele nu se înlănțuie. Un POST /api/v1/commands se numără simultan în Global și în Comenzi.

Citește antetele de limită

Fiecare răspuns — nu doar cele 429 — poartă antetele IETF draft-7 RateLimit:

AntetÎnțeles
RateLimit-LimitNumărul maxim de cereri permise în fereastra curentă pentru contorul din acest răspuns.
RateLimit-RemainingCâte cereri mai sunt permise în fereastră. Când se apropie de 0, încetinește.
RateLimit-ResetCâte secunde mai sunt până se resetează fereastra curentă.

O trimitere de comandă reușită cu contorul Comenzi aproape epuizat arată așa:

HTTP/1.1 201 Created
RateLimit-Limit: 50
RateLimit-Remaining: 3
RateLimit-Reset: 412
Content-Type: application/json

{ "id": "cmd_abc123", "status": "pending" }

Antetele reflectă întotdeauna contorul care a produs răspunsul. Citește-le, nu ghici care contor este aproape de plafon.

Tratează un răspuns 429

Când un contor se epuizează, cererea nu mai ajunge la handler. Primești înapoi:

HTTP/1.1 429 Too Many Requests
Retry-After: 47
RateLimit-Limit: 150
RateLimit-Remaining: 0
RateLimit-Reset: 47
Content-Type: application/json

{
  "code": "RATE_LIMIT_EXCEEDED",
  "message": "Too many requests, please try again later.",
  "status": 429
}

Corpul este identic pentru toate cele trei contoare. Verifică pe câmpul code === "RATE_LIMIT_EXCEEDED" de la nivelul de sus pentru a detecta răspunsurile de limită.

Retry-After este mereu un număr întreg de secunde, cu minim 1. Așteaptă cel puțin atât înainte de următoarea încercare — nu reîncerca niciodată un 429 imediat.

Reîncearcă în siguranță

Un client civilizat nu se bazează pe primirea unui 429 ca să descopere că merge prea repede. Integrează antetele direct în stratul de transport.

Citește antetele la fiecare răspuns

Extrage RateLimit-Limit, RateLimit-Remaining și RateLimit-Reset din fiecare răspuns, nu doar din erori. Când Remaining scade sub ~10% din Limit, încetinește voluntar — adormi, grupează în loturi sau pune în coadă.

Respectă Retry-After la 429

Parsează Retry-After ca secunde întregi și așteaptă cel puțin atât înainte de a reîncerca.

Adaugă jitter la fiecare reîncercare

Înmulțește timpul de așteptare cu 1 + (Math.random() - 0.5) * 0.4 (±20% jitter). Fără el, mai multe dispozitive POS care revin online în același minut se vor năpusti la API și vor declanșa din nou același contor.

Folosește întârziere exponențială la 429-uri repetate

Dacă primești 429 de două ori la rând pentru aceeași operațiune, dublează așteptarea de fiecare dată (plafonează în jur de 5 minute). 429-uri repetate înseamnă de obicei o eroare în clientul tău, nu o problemă tranzitorie — loghează rafala.

Folosește o cheie API per integrare, nu per dispozitiv

Contoarele Global și Comenzi sunt indexate după cheia API. Dacă 200 de dispozitive împart o cheie, împart un contor Comenzi de 50 la 10 minute. Pentru majoritatea partenerilor POS, o singură cheie pentru serviciul backend care proxiază toate dispozitivele este modelul corect. Nu roti chei doar ca să eviți plafoanele.

Exemple de cod

Acești clienți de referință scurți arată forma antetelor / Retry-After / jitter-ului. Nu sunt pregătiți pentru producție (fără logare, fără metrici, fără circuit breaker) — adaptează-i la stack-ul tău.

async function callWithBackoff(url, options, attempt = 0) {
  const res = await fetch(url, options);
  if (res.status !== 429) return res;
  const retryAfter = parseInt(res.headers.get('retry-after') ?? '1', 10);
  const base = Math.max(retryAfter, 2 ** attempt);
  const jitter = base * (0.8 + Math.random() * 0.4); // ±20%
  if (attempt >= 5) throw new Error('limită de rată: am renunțat după 5 încercări');
  await new Promise((r) => setTimeout(r, jitter * 1000));
  return callWithBackoff(url, options, attempt + 1);
}

Gestionează contorul Autentificare pe IP-uri partajate

Contorul Autentificare este indexat după IP-ul clientului, nu după cheia API. Intenția este anti credential-stuffing: un singur IP nu poate forța mai mult de 30 de încercări de login, înregistrare sau refresh în 10 minute, indiferent câte nume de utilizator încearcă.

Compromisul este că un NAT corporativ, un Wi-Fi de birou partajat sau un reverse proxy configurat greșit poate satura contorul Autentificare pentru toți cei din spatele lui.

Dacă operezi un produs POS multi-tenant unde tenanții împart un IP de ieșire:

  • Eșalonează rotirea token-urilor de refresh ca mai mulți tenanți să nu reîmprospăteze în același minut.
  • Memorează token-urile de acces în cache până la ~60 de secunde înainte de expirare. Nu reîmprospăta la fiecare cerere.
  • Dacă ai nevoie de un debit Autentificare mai mare, rulează din IP-uri de ieșire distincte.
Împerechează reîncercările cu Idempotency-Key ca o reîncercare după un 429 să nu cauzeze niciodată tipărirea unui bon fiscal duplicat.

Continuă cu

  • Prezentare API — forma completă a cererilor și răspunsurilor și modelul de autentificare.
  • Erori API — toate codurile de eroare pe care le poți primi, inclusiv RATE_LIMIT_EXCEEDED.
  • Autentificare — fluxurile de login, înregistrare și refresh protejate de contorul Autentificare.
  • Comenzi — familia de endpoint-uri protejată de contorul Comenzi.
  • Idempotență — reîncearcă în siguranță cu Idempotency-Key.