Rate limiting
Limiti di chiamata per fascia di endpoint, header di quota, gestione del 429 e strategie di retry con backoff.
Ogni richiesta alle API di Blu Pantheon consuma quota. I limiti proteggono la piattaforma da picchi anomali e garantiscono tempi di risposta stabili per tutti i tenant. Sono espressi come numero massimo di richieste in una finestra temporale scorrevole.
Limiti per fascia
| Categoria | Endpoint tipici | Limite | Finestra |
|---|---|---|---|
| Autenticazione | POST /identity/v1/auth/login, /auth/mfa/verify | 10 richieste | 1 minuto |
| Recupero credenziali | /auth/password/forgot, /auth/password/reset | 5 richieste | 15 minuti |
| Scritture identità | POST /identity/v1/users, PATCH /identity/v1/users/{userId} | 60 richieste | 1 minuto |
| Letture identità | GET /identity/v1/me, /identity/v1/users | 600 richieste | 1 minuto |
| Letture Console | GET /console/v1/sessions, /console/v1/clients | 1.200 richieste | 1 minuto |
| Scritture Console | POST /console/v1/clients, /console/v1/sessions/{sessionId}/close | 300 richieste | 1 minuto |
| Upload documenti | POST /console/v1/data-stores/{storeId}/documents | 30 richieste | 1 minuto |
| Statistiche e report | GET /console/v1/statistics | 60 richieste | 1 minuto |
| Letture Billing | GET /billing/v1/subscription, /billing/v1/invoices | 120 richieste | 1 minuto |
| Disponibilità Booking | GET /booking/v1/availability | 600 richieste | 1 minuto |
| Scritture Booking | POST /booking/v1/bookings | 120 richieste | 1 minuto |
| Servizio-a-servizio (HMAC) | Tutti gli endpoint interni | 3.000 richieste | 1 minuto |
Perché l'autenticazione è più restrittiva
Gli endpoint di login e recupero credenziali hanno limiti molto bassi perché sono i bersagli tipici di
attacchi di forza bruta e di credential stuffing. Un'integrazione corretta autentica una volta e
riutilizza il token fino alla scadenza: non chiamare /auth/login a ogni richiesta.
Header di risposta
Ogni risposta, sia in caso di successo sia in caso di errore, include gli header di quota.
Header di quota
| Parametro | Tipo | Obblig. | Default | Descrizione |
|---|---|---|---|---|
X-RateLimit-Limit | number | Sì | — | Numero massimo di richieste consentite nella finestra corrente per la fascia richiesta. |
X-RateLimit-Remaining | number | Sì | — | Richieste ancora disponibili nella finestra corrente. Quando arriva a zero le chiamate successive ricevono 429. |
X-RateLimit-Reset | number | Sì | — | Timestamp Unix in secondi in cui la finestra si azzera e il contatore torna al valore di X-RateLimit-Limit. |
Retry-After | number | No | — | Presente solo nelle risposte 429. Secondi da attendere prima di ritentare la richiesta. |
Esempio di risposta positiva:
HTTP/1.1 200 OK
Content-Type: application/json
X-RateLimit-Limit: 1200
X-RateLimit-Remaining: 1186
X-RateLimit-Reset: 1788515595Esempio di superamento del limite:
HTTP/1.1 429 Too Many Requests
Content-Type: application/json
X-RateLimit-Limit: 10
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1788515595
Retry-After: 42
{
"code": "RATE_LIMITED",
"title": "Troppe richieste",
"message": "Hai superato il limite di 10 richieste al minuto per questo endpoint. Riprova tra 42 secondi.",
"traceId": "01J8ZQ3K5V6X7Y8Z9A0B1C2D3E",
"details": [
{ "field": "scope", "code": "PER_KEY", "message": "Limite applicato alla chiave API bp_live_xxxx…" }
]
}Come leggere Retry-After
Retry-After contiene un numero intero di secondi. Non ritentare prima che sia trascorso: le
richieste anticipate consumano quota senza essere servite e prolungano il blocco.
function retryDelayMs(response: Response, fallbackMs: number): number {
const retryAfter = response.headers.get('Retry-After');
if (retryAfter) {
const seconds = Number.parseInt(retryAfter, 10);
if (Number.isFinite(seconds)) return seconds * 1000;
}
const reset = response.headers.get('X-RateLimit-Reset');
if (reset) {
const deltaMs = Number.parseInt(reset, 10) * 1000 - Date.now();
if (deltaMs > 0) return deltaMs;
}
return fallbackMs;
}Un'integrazione ben scritta non aspetta il 429: quando X-RateLimit-Remaining scende sotto il dieci
per cento del limite, rallenta autonomamente il ritmo delle chiamate.
Strategia di retry consigliata
Applica un backoff esponenziale con jitter per evitare che tutti i client ritentino nello stesso istante
(effetto "thundering herd"). Ritenta solo su 429, 502, 503 e 504, e solo per richieste
idempotenti o accompagnate da un header Idempotency-Key.
const RETRYABLE_STATUS = new Set([429, 502, 503, 504]);
interface RetryOptions {
maxAttempts?: number;
baseDelayMs?: number;
maxDelayMs?: number;
}
const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
export async function fetchWithRetry(
url: string,
init: RequestInit = {},
options: RetryOptions = {},
): Promise<Response> {
const maxAttempts = options.maxAttempts ?? 5;
const baseDelayMs = options.baseDelayMs ?? 500;
const maxDelayMs = options.maxDelayMs ?? 30_000;
let lastError: unknown;
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
try {
const response = await fetch(url, init);
if (!RETRYABLE_STATUS.has(response.status)) return response;
if (attempt === maxAttempts) return response;
// Rispetta Retry-After se presente, altrimenti backoff esponenziale con full jitter.
const header = response.headers.get('Retry-After');
const serverDelay = header ? Number.parseInt(header, 10) * 1000 : 0;
const backoff = Math.min(baseDelayMs * 2 ** (attempt - 1), maxDelayMs);
const jittered = Math.random() * backoff;
await sleep(Math.max(serverDelay, jittered));
} catch (error) {
lastError = error;
if (attempt === maxAttempts) break;
const backoff = Math.min(baseDelayMs * 2 ** (attempt - 1), maxDelayMs);
await sleep(Math.random() * backoff);
}
}
throw lastError ?? new Error('Numero massimo di tentativi superato');
}Non ritentare le scritture senza chiave di idempotenza
Un POST che va in timeout potrebbe essere stato eseguito comunque dal server. Ritentarlo senza
Idempotency-Key può creare risorse duplicate. Genera la chiave una volta prima del primo tentativo e
riusala identica in tutti i ritentativi.
Limiti per chiave, per tenant e per IP
I tre livelli sono applicati contemporaneamente: viene rifiutata la richiesta che supera il primo limite raggiunto.
| Livello | Chiave di conteggio | Scopo | Come identificarlo |
|---|---|---|---|
| Per credenziale | Chiave API o jti del token | Isola le integrazioni tra loro: un job impazzito non blocca la Console | details[0].code vale PER_KEY |
| Per tenant | Identificativo del tenant (tid) | Somma di tutte le credenziali del tenant, allineata al piano sottoscritto | details[0].code vale PER_TENANT |
| Per IP | Indirizzo sorgente | Protegge gli endpoint non autenticati come /auth/login | details[0].code vale PER_IP |
Il limite per tenant dipende dal piano: il piano Business ha un tetto complessivo pari a cinque volte il limite per singola credenziale, il piano Enterprise è configurabile contrattualmente.
Traffico attraverso proxy
Se le tue chiamate passano da un gateway NAT condiviso, tutte le richieste risultano provenire dallo stesso IP e possono saturare il limite per IP sugli endpoint pubblici. In questo caso richiedi l'inserimento dei tuoi IP di uscita nella allowlist: vedi Ambienti.
Buone pratiche
Un access token vale 15 minuti. Ottienilo una volta, tienilo in memoria e rinnovalo solo quando manca meno di un minuto alla scadenza. Riautenticarsi a ogni chiamata esaurisce la fascia più restrittiva.
Interrogare GET /console/v1/sessions ogni pochi secondi per scoprire nuove conversazioni è il modo più
rapido per saturare la quota. Sottoscrivi gli eventi e reagisci alle notifiche:
vedi Automazioni.
Leggere 1.000 elementi con limit=50 richiede 20 chiamate; con limit=200 ne bastano 5.
Vedi Paginazione.
Le importazioni di anagrafiche vanno eseguite con un pool di concorrenza limitato (da 4 a 8 richieste parallele) e non con centinaia di chiamate simultanee.
Registra X-RateLimit-Remaining nelle metriche applicative e imposta un allarme quando scende sotto
il 20 per cento del limite: intervieni prima che l'integrazione inizi a fallire.
Richiedere un aumento dei limiti
Gli aumenti sono valutati caso per caso per i piani Business ed Enterprise.
Raccogli i dati
Prepara: endpoint interessati, volume di picco misurato (richieste al minuto), volume medio giornaliero, finestra oraria in cui si concentra il traffico e motivazione funzionale.
Verifica le alternative
Il supporto chiederà se hai già adottato webhook al posto del polling, limit più alti e caching lato
tuo. Un aumento non viene concesso per compensare un'integrazione inefficiente.
Apri la richiesta
Invia la richiesta dalla Console.
La valutazione richiede alcuni giorni lavorativi. Per eventi pianificati (migrazioni, campagne) segnala la finestra con almeno due settimane di anticipo.