Novità Registrati gratis, 10 chiamate le offriamo noi. Fino a $1, senza carta.

API di fatturazione

Porta l'uso e il costo del tuo workspace nei tuoi sistemi, senza aprire la console. Una chiave di fatturazione è una di sola lettura credenziale limitata a un solo workspace - può solo leggere i dati di fatturazione, nulla di più.

⚠

Una chiave di fatturazione può leggere ogni richiesta registrata nel suo workspace, incluse le chiavi eliminate o revocate nel frattempo, ma non può chiamare alcun modello, né creare, modificare o eliminare alcuna chiave API. Usarla altrove restituisce 401 wrong_key_kind.

Creare una chiave di fatturazione

  1. Apri Console → Chiavi API di fatturazione (solo proprietario del workspace). Assegnale un nome, limitala facoltativamente a una whitelist di IP o imposta una scadenza, poi copia la chiave - inizia con sk-syn-bill- e viene mostrata solo una volta.
  2. Chiama gli endpoint sottostanti usandola come token Bearer. Revocala in qualsiasi momento dalla stessa pagina - la revoca la invalida immediatamente e non influisce su nulla altro nel workspace.
  3. Una chiave è legata a chi l'ha creata: se questa persona non è più il proprietario del workspace, smette di funzionare e restituisce 403 key_owner_not_authorized.

Autenticazione

Ogni chiamata richiede Authorization: Bearer sk-syn-bill-... verso l'URL di base qui sotto. Una chiave di fatturazione funziona solo su questi endpoint, e questi endpoint accettano solo una chiave di fatturazione - qualsiasi altra combinazione restituisce 401 wrong_key_kind.

Authorization: Bearer sk-syn-bill-...

URL di base: https://synthorai.io/api/v1/billing

ℹ

L'ambito è l'intero workspace: ogni chiave di inferenza al suo interno, incluse quelle eliminate - le loro righe storiche restano interrogabili. api_key_id e model possono solo restringere il risultato, mai ampliarlo; ogni query resta ancorata al workspace proprio della chiave.

ℹ

Ogni query viene eseguita sulla replica di lettura, mai sulla primaria. Un processo senza replica configurata risponde 503 replica_unavailable invece di ricorrervi silenziosamente.

✓

Campi dei token: seguono la convenzione OpenAI - prompt_tokens è il totale dei token di input e include già ogni lettura e scrittura di cache elencata sotto, e completion_tokens include già reasoning_tokens. Nessuno dei due si somma ai totali - sono una scomposizione, non token aggiuntivi.

La parte di lettura cache di prompt_tokens è cached_tokens; la parte di scrittura cache è cache_write_tokens, ulteriormente divisa nelle righe di /records in cache_write_5m_tokens e cache_write_1h_tokens in base al TTL.

✓

Campi monetari: cost_usd è l'importo effettivamente addebitato. byok_list_price_usd è ciò che una richiesta BYOK avrebbe pagato al prezzo di listino - 0 per una richiesta non BYOK. Non esiste list_price_usd in nessun punto di questa API.

ℹ

Qui "errore" indica qualsiasi richiesta che ha raggiunto l'API ed è stata rifiutata - un 4xx come 402 (saldo insufficiente), 403 o 429, così come un errore upstream. Sono escluse solo le reiezioni della nostra infrastruttura interna, esattamente come in console. I rifiuti vengono registrati al massimo una volta per chiave, codice di stato e minuto su ogni server, quindi i conteggi di 402, 403 e 429 sono un minimo; le richieste riuscite e gli addebiti sono completi.

GET /usage - utilizzo aggregato

GET /usage

Righe pre-aggregate per ora o per giorno, pensate per fatturazione e dashboard. Provengono dall'aggregato orario più le richieste scritte dall'ultima ingestione, così i bucket recenti non restano indietro di un intero ciclo di ingestione.

ParametroTipoDescrizione
start*stringInizio dell'intervallo: un timestamp RFC 3339 o una data come 2026-09-01.
endstringFine dell'intervallo. Il valore predefinito è ora.
granularitystringDimensione del bucket: hour o day. Il valore predefinito è day.
group_bystringDimensioni per scomporre le righe: api_key, model, o entrambe (qualsiasi sottoinsieme). Il valore predefinito è entrambe.
api_key_idintegerFiltra per uno o più id di chiave API. Ripetibile; restringe solo, non amplia mai.
modelstringFiltra per uno o più id di modello. Ripetibile.
limitintegerRighe per pagina. Il valore predefinito è 1000, con un limite massimo di 5000.
cursorstringCursore di paginazione opaco, ottenuto dal meta.next_cursor della pagina precedente.

In ogni riga, api_key_id e api_key_name compaiono solo quando group_by include api_key, e model solo quando include model. avg_latency_ms può essere null quando un bucket non ha campioni di latenza.

Esempio di richiesta

curl "https://synthorai.io/api/v1/billing/usage?start=2026-09-01&end=2026-09-24&granularity=day" \
  -H "Authorization: Bearer sk-syn-bill-..."

Esempio di risposta

{
  "data": [
    {
      "bucket_start": "2026-09-23T00:00:00Z",
      "bucket_end": "2026-09-24T00:00:00Z",
      "api_key_id": 42,
      "api_key_name": "prod-backend",
      "model": "claude-sonnet-5",
      "requests": 1280,
      "success_requests": 1265,
      "error_requests": 15,
      "errors_4xx": 12,
      "errors_429": 2,
      "errors_5xx": 1,
      "prompt_tokens": 512000,
      "completion_tokens": 98000,
      "cached_tokens": 210000,
      "cache_write_tokens": 8000,
      "reasoning_tokens": 4000,
      "cost_usd": 4.1732,
      "byok_list_price_usd": 0,
      "byok_requests": 0,
      "avg_latency_ms": 812,
      "final": true
    }
  ],
  "meta": {
    "generated_at": "2026-09-24T08:00:00Z",
    "data_complete_until": "2026-09-24T06:00:00Z",
    "timezone": "UTC",
    "currency": "USD",
    "next_cursor": null,
    "has_more": false
  }
}

GET /records - righe per richiesta

GET /records

Una riga per richiesta, pensata per la sincronizzazione incrementale nel tuo database. Sincronizza per cursor, mai per tempo, e deduplica le righe ricevute in base a record_id - vedi "Evitare i vuoti temporali" più sotto per il motivo.

ParametroTipoDescrizione
cursorstringCursore di sincronizzazione opaco (stringa), ottenuto dal meta.next_cursor della chiamata precedente. Fornisci esattamente uno tra cursor o start; non ha alcun limite di intervallo.
startstringInizio dell'intervallo (timestamp RFC 3339 o data). Fornisci esattamente uno tra cursor o start; un intervallo start/end è limitato a 31 giorni, mentre cursor non ha alcun limite di intervallo.
endstringFine dell'intervallo. Il valore predefinito è ora.
api_key_idintegerFiltra per uno o più id di chiave API. Ripetibile; restringe solo, non amplia mai.
modelstringFiltra per uno o più id di modello. Ripetibile.
limitintegerRighe per pagina. Il valore predefinito è 500, con un limite massimo di 1000.

Esempio di richiesta

curl "https://synthorai.io/api/v1/billing/records?start=2026-09-24T00:00:00Z&limit=500" \
  -H "Authorization: Bearer sk-syn-bill-..."

Esempio di risposta

{
  "data": [
    {
      "record_id": "rec_8f2a91c4",
      "request_id": "req_9f3c2b1a",
      "recorded_at": "2026-09-24T05:58:31Z",
      "completed_at": "2026-09-24T05:58:29Z",
      "api_key_id": 42,
      "api_key_name": "prod-backend",
      "model": "claude-sonnet-5",
      "status": "success",
      "status_code": 200,
      "prompt_tokens": 812,
      "completion_tokens": 194,
      "cached_tokens": 512,
      "cache_write_tokens": 64,
      "cache_write_5m_tokens": 64,
      "cache_write_1h_tokens": 0,
      "cost_usd": 0.00412,
      "byok_list_price_usd": 0,
      "is_byok": false,
      "is_stream": true,
      "duration_ms": 1340,
      "ttft_ms": 210
    }
  ],
  "meta": {
    "next_cursor": "eyJvIjoiODgxNDA5MCJ9",
    "has_more": false,
    "window_final": true,
    "safe_until": "2026-09-24T05:58:31Z",
    "latest_recorded_at": "2026-09-24T05:58:31Z",
    "data_complete_until": "2026-09-24T05:58:00Z"
  }
}
ParametroDescrizione
next_cursorCursore per la chiamata successiva. Sempre presente.
has_moreIndica se richiamare subito con questo cursore restituirebbe altre righe.
window_finaltrue solo se hai fornito un end ed è precedente o uguale a safe_until - in tal caso l'intervallo richiesto è garantito completo. Assente o false altrimenti.
safe_untilIl recorded_at più recente che un chiamante può considerare definitivo; le righe più recenti di questo sono ancora trattenute.
latest_recorded_atIl recorded_at più recente tra tutte le righe, definitive o no.
data_complete_untilLo stesso valore restituito da /freshness - fino a dove il rollup orario è completo, per un confronto con /usage.
⚠

Base temporale: /usage raggruppa per completed_at (quando la richiesta è terminata). Il filtro start/end di /records si basa su recorded_at (quando la riga è stata scritta, che sotto carico può ritardare rispetto al completamento). Per riconciliare /records con /usage, raggruppa tu stesso le righe sincronizzate per completed_at, non per recorded_at.

GET /records/export - esportazione in streaming

GET /records/export

Gli stessi filtri di /records, trasmessi in streaming come JSON delimitato da a-capo (application/x-ndjson) - pensato per un grande backfill invece di un elenco paginato. Per ogni workspace può essere in streaming una sola esportazione alla volta; una seconda riceve 429 too_many_concurrent_requests.

Esempio di richiesta

curl -N "https://synthorai.io/api/v1/billing/records/export?cursor=eyJvIjoiODgxNDAzMiJ9" \
  -H "Authorization: Bearer sk-syn-bill-..."

Esempio di risposta

{"record_id":"rec_8f2a91c4","request_id":"req_9f3c2b1a","model":"claude-sonnet-5","status":"success","cost_usd":0.00412}
{"record_id":"rec_8f2a91c5","request_id":"req_9f3c2b1b","model":"claude-sonnet-5","status":"success","cost_usd":0.00398}
{"_meta":{"next_cursor":"eyJvIjoiODgxNDA5MSJ9","rows":2,"complete":true,"window_final":true,"generated_at":"2026-09-24T06:00:02Z"}}

L'ultima riga dello stream è un _meta oggetto:

ParametroDescrizione
next_cursorCursore per la successiva chiamata di esportazione.
rowsNumero di record scritti in questo stream.
completefalse significa che lo stream si è interrotto prima di esaurire l'intervallo - riprendi con cursor=next_cursor.
window_finalStesso significato di /records: true solo se l'end dell'esportazione è precedente o uguale a safe_until.
generated_atMomento in cui è stata scritta questa riga.
stopped_becausePresente quando complete è false: row_cap, scan_cap (lo stream ha percorso il suo intervallo massimo di id senza riempirsi - frequente con un filtro stretto), time_cap, query_error o window_not_final.

GET /summary - statistiche preliminari e anomalie

GET /summary

Totali, i modelli e le chiavi principali per costo, e un tasso di errore per l'intervallo, più una anomalies elenco (high_error_rate, rate_limited, data_not_final, rollup_delayed), ciascuna con una gravità info o warning e un messaggio leggibile. I numeri di un intervallo non ancora finalizzato possono ancora cambiare - vedi sotto.

ParametroTipoDescrizione
startstringInizio dell'intervallo. Il valore predefinito è 30 giorni prima di end.
endstringFine dell'intervallo. Il valore predefinito è ora.
api_key_idintegerFiltra per uno o più id di chiave API. Ripetibile; restringe solo, non amplia mai.
modelstringFiltra per uno o più id di modello. Ripetibile.

models_count e keys_count forniscono i totali dietro top_models e top_keys, che elencano al massimo i primi 20 per costo. L'intervallo predefinito è i 30 giorni prima di end quando start viene omesso.

Esempio di richiesta

curl "https://synthorai.io/api/v1/billing/summary?start=2026-09-17&end=2026-09-24" \
  -H "Authorization: Bearer sk-syn-bill-..."

Esempio di risposta

{
  "data": {
    "totals": { "requests": 48210, "cost_usd": 132.55, "error_rate": 0.021 },
    "models_count": 6,
    "keys_count": 3,
    "top_models": [ { "model": "claude-sonnet-5", "cost_usd": 88.10, "byok_list_price_usd": 0 } ],
    "top_keys": [ { "api_key_id": 42, "api_key_name": "prod-backend", "cost_usd": 61.30, "byok_list_price_usd": 0 } ],
    "anomalies": [
      { "type": "data_not_final", "severity": "info", "message": "The last 2 hours of this range are not yet final." }
    ]
  },
  "meta": {
    "generated_at": "2026-09-24T06:00:02Z",
    "data_complete_until": "2026-09-24T04:00:00Z"
  }
}

GET /freshness - quanto sono completi i dati

GET /freshness

Restituisce data_complete_until, latest_recorded_at, safe_until e pipeline_lag_seconds - interrogalo prima di considerare definitivo un intervallo appena recuperato. safe_until è il recorded_at più recente che un chiamante può considerare completo; righe più recenti potrebbero essere ancora in arrivo.

Esempio di richiesta

curl "https://synthorai.io/api/v1/billing/freshness" \
  -H "Authorization: Bearer sk-syn-bill-..."

Esempio di risposta

{
  "data": {
    "data_complete_until": "2026-09-24T04:00:00Z",
    "latest_recorded_at": "2026-09-24T05:58:31Z",
    "safe_until": "2026-09-24T05:58:31Z",
    "pipeline_lag_seconds": 47
  },
  "meta": { "generated_at": "2026-09-24T06:00:02Z" }
}

GET /balance - quanto il workspace può spendere ora

GET /balance

Restituisce il saldo disponibile attuale del workspace, lo stesso importo mostrato in console. Pensato per interrogazioni periodiche: ogni 30-60 secondi è più che sufficiente. Ha un proprio limite di frequenza, quindi interrogarlo non consuma il budget degli endpoint dei dati, e risponde anche quando il saldo è zero.

Esempio di richiesta

curl "https://synthorai.io/api/v1/billing/balance" \
  -H "Authorization: Bearer sk-syn-bill-..."

Esempio di risposta

{
  "data": {
    "available_usd": 1284.37,
    "debt_usd": 0,
    "source": "live",
    "voucher": null,
    "scheduled_credits": [
      { "amount_usd": 250, "release_at": "2026-10-01T00:00:00Z" }
    ],
    "scheduled_credit_total_usd": 250
  },
  "meta": { "generated_at": "2026-09-24T06:00:02Z", "currency": "USD" }
}
ParametroDescrizione
available_usdIl saldo generale su cui vengono addebitate le richieste in questo momento. Mai sotto 0. Può risalire brevemente quando le richieste in corso vengono regolate; per la spesa usa /usage o /records.
debt_usdIl saldo dovuto: il consumo oltre l'importo accreditato. 0 se non c'è nulla da pagare. Una ricarica lo salda per primo; solo il resto va in available_usd.
sourcelive: letto dal registro in tempo reale. delayed: il registro non era raggiungibile e il valore proviene da una copia nel database che può essere indietro di circa 30 secondi.
voucherCredito promozionale solo per la generazione di immagini (applies_to), sui modelli elencati e fino a expires_at; le altre richieste usano solo available_usd, e questo credito non ne fa parte. null se assente; active è false dopo la scadenza.
scheduled_creditsCrediti già concordati che arrivano a rate (amount_usd, release_at); ogni rata viene aggiunta al saldo entro circa 10 minuti da release_at, non prima. scheduled_credit_total_usd ne è la somma.

Errori

Ogni errore è {"error": {"type", "message", "hint"}}:

{
  "error": {
    "type": "range_too_large",
    "message": "the requested range exceeds this endpoint's cap",
    "hint": "narrow start/end to at most 31 days, or sync /records with a cursor instead"
  }
}
TipoStato HTTPSignificato
invalid_parameter400Un parametro di query è mancante, malformato o fuori intervallo.
range_too_large400L'intervallo di tempo o la finestra di id richiesti sono più grandi di quanto consentito dall'endpoint; il hint indica il limite in questione.
start_too_recent400Una finestra start/end inizia dopo safe_until, dove il suo limite iniziale non è ancora stabile. Riprova dopo il tempo indicato dall'header Retry-After, oppure sincronizza con un cursor.
wrong_key_kind401La credenziale non è una chiave di fatturazione, oppure una chiave di fatturazione è stata usata fuori da questi endpoint.
authentication_error401La chiave è assente, non valida, scaduta, oppure bloccata dalla sua whitelist di IP.
key_owner_not_authorized403Il creatore di questa chiave non è più il proprietario del workspace. Le chiavi di fatturazione sono esclusive del proprietario e smettono di funzionare nel momento in cui la proprietà cambia; il nuovo proprietario deve crearne una propria.
rate_limited429Più di 60 richieste al minuto su questo workspace. Riprova dopo il tempo indicato nell'header Retry-After.
too_many_concurrent_requests429Più di 3 richieste contemporanee su questo workspace, oppure un'altra esportazione già in corso.
replica_unavailable503Questo processo non ha alcuna replica di lettura configurata; l'API non ricorre mai alla primaria.
query_timeout503La query ha superato il timeout di 10 secondi o il termine di 15 secondi. Restringi l'intervallo e riprova.
internal_error500Un errore imprevisto del server. Riprova e segnalalo se persiste.

Limiti di frequenza e massimali

ProtezioneValore
Limite di frequenza60 richieste al minuto per workspace, una finestra scorrevole condivisa tra tutte le istanze.
Concorrenza3 richieste contemporanee per workspace su ogni server, e uno stream di esportazione per workspace su ogni server.
Interrogazione del saldo/balance: 60 richieste al minuto e 2 in parallelo per workspace, conteggiate separatamente dagli altri endpoint.
Massimali di intervallo/usage: 31 giorni con granularità hour, 366 giorni con day. /summary: fino a 366 giorni. /records e l'esportazione: fino a 31 giorni.
Massimali di pagina/usage restituisce fino a 5.000 righe per pagina, /records fino a 1.000. L'esportazione trasmette pagine di 2.000 e si arresta a 1.000.000 di righe - riprendibile con cursor. Si ferma anche dopo aver percorso 2.000.000 di id di record (stopped_because=scan_cap) e regola il ritmo sul database; riprendi con cursor.
CachingOgni risposta porta Cache-Control: no-store.

Non viene mai restituito: channel_id, upstream_request_id, usage_raw, other, content, ip_address, user_agent, cost_detail.

Evitare i vuoti temporali

Le richieste vengono scritte nel log in modo asincrono dopo il loro completamento (di norma pochi secondi, più a lungo in caso di accumulo), e il rollup orario letto da /usage e /summary viene caricato in batch. Le ore più recenti sono sempre un po' incomplete. /usage e /summary aggiungono anche le richieste scritte dall'ultima ingestione (meta.live_tail è true); se l'aggregato è troppo indietro, meta.live_tail è false e i bucket recenti sono parziali.

data_complete_until è il più antico tra questi due punti: il livello di riferimento del rollup orario meno il suo ritardo di pipeline attuale, oppure la richiesta più vecchia non ancora inclusa nel rollup - meno un margine di sicurezza di 30 minuti, arrotondato all'ora inferiore.

ℹ

Un bucket finale è stabile, con un'eccezione: il job di riconciliazione giornaliera può ancora correggere un'ora entro 48 ore se rileva una deriva. /records è sempre la fonte di verità per i numeri esatti di una richiesta.

Ogni riga di /usage porta un indicatore final : true , valido non appena il suo bucket termina prima o entro data_complete_until (valore di /freshness). Un bucket finale non cambia mai più.

  • Dati aggregati: esegui l'upsert di ogni riga nel tuo archivio in base a (bucket_start, api_key_id, model), e recupera di nuovo qualsiasi bucket mentre è ancora final: false. Non calcolare mai un delta sottraendo un totale vecchio da uno nuovo.
  • Righe grezze: sincronizza per cursor, mai per tempo. Conserva next_cursor e riprendi da lì alla chiamata successiva, e deduplica le righe ricevute in base a record_id (un id opaco stabile per riga) nel caso in cui una pagina ripetuta riconsegni la stessa riga. Le righe più recenti di circa 5 minuti vengono trattenute - safe_until è l'orario della riga registrata più di recente meno 5 minuti - così una riga ancora in fase di commit non può mai scivolare prima di una posizione di cursore già consegnata, e niente cade in un vuoto. Per contare le richieste, conta i request_id distinti.
  • Finestra limitata: se interroghi un intervallo start/end invece di sincronizzare per cursor, considéralo completo solo quando window_final è true (è true solo se hai fornito un end ed è precedente o uguale a safe_until). Se window_final è false, richiama più tardi con lo stesso intervallo, oppure passa alla sincronizzazione per cursor. Una finestra deve iniziare al più tardi a safe_until; un inizio successivo restituisce 400 start_too_recent con un header Retry-After.

Integrazione consigliata

  • Fatturazione notturna: chiama /usage una volta al giorno con granularity=day, e recupera di nuovo qualsiasi bucket che la tua ultima esecuzione vedeva ancora come final: false.
  • Dettaglio per richiesta: chiama /records ogni ora con cursor impostato sull'ultimo next_cursor salvato, continua a paginare finché has_more è true, e deduplica le righe ricevute in base a record_id.
  • Dashboard: chiama /summary per l'intervallo visibile invece di aggregare /records da solo - porta già l'elenco delle anomalie.

Devi generare o gestire chiavi API normali in modo programmatico invece di leggere dati di fatturazione? Vedi Chiavi di provisioning.