Limite de rată
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.
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.
| Contor | Fereastră | Plafon | Indexat după | Se aplică la |
|---|---|---|---|---|
| Global | 10 min | 150 cereri | cheia API (sau IP client) | Orice cerere către https://api.e-bon.ro |
| Autentificare | 10 min | 30 cereri | IP client | POST /api/v1/auth/* (login, înregistrare, refresh) |
| Comenzi | 10 min | 50 cereri | cheia 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/commandsse 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-Limit | Numărul maxim de cereri permise în fereastra curentă pentru contorul din acest răspuns. |
RateLimit-Remaining | Câte cereri mai sunt permise în fereastră. Când se apropie de 0, încetinește. |
RateLimit-Reset | Câ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);
}
import random, time, requests
def call_with_backoff(method, url, attempt=0, **kwargs):
r = requests.request(method, url, **kwargs)
if r.status_code != 429:
return r
retry_after = int(r.headers.get('Retry-After', '1'))
base = max(retry_after, 2 ** attempt)
jitter = base * (0.8 + random.random() * 0.4)
if attempt >= 5:
r.raise_for_status()
time.sleep(jitter)
return call_with_backoff(method, url, attempt + 1, **kwargs)
attempt=0
while [ $attempt -lt 5 ]; do
resp=$(curl -sS -D /tmp/h -o /tmp/b -w '%{http_code}' \
-H "Authorization: Bearer $EBON_KEY" \
-H 'Content-Type: application/json' \
-X POST -d @body.json https://api.e-bon.ro/api/v1/commands)
[ "$resp" != "429" ] && cat /tmp/b && break
wait=$(awk 'tolower($1)=="retry-after:"{print $2+0}' /tmp/h)
sleep "$((wait > 0 ? wait : 1))"
attempt=$((attempt + 1))
done
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.
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.
Autentificare
Cum te autentifici la API-ul e-bon — formatul cheii API, cele nouă permisiuni, JWT pentru sesiunile din Portal, erori uzuale de autentificare și exemple curl gata de folosit.
Trasare și jurnalizare a cererilor
Cum să urmărești un singur apel HTTP de la clientul tău până la e-bon — turul antetului X-Request-Id, forma plicului de eroare, rețete client gata făcute și o listă de raport de bug care transformă investigațiile de minute în secunde.