curl --request GET \
--url https://api.samsa.ai/public/v1/credits \
--header 'Authorization: Bearer <token>'import requests
url = "https://api.samsa.ai/public/v1/credits"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://api.samsa.ai/public/v1/credits', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.samsa.ai/public/v1/credits",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.samsa.ai/public/v1/credits"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://api.samsa.ai/public/v1/credits")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.samsa.ai/public/v1/credits")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body{
"available": 1450,
"spendable_plan_credits": 1200,
"plan_credits": 1200,
"topup_credits": 250,
"period": {
"start": "2026-07-01T00:00:00+00:00",
"end": "2026-08-01T00:00:00+00:00"
},
"scope": {
"type": "unallocated",
"team": {
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"name": "Marketing"
},
"budget": 2000,
"used": 500,
"remaining": 1500
}
}{
"error": {
"type": "authentication_error",
"code": "invalid_api_key",
"message": "The provided API key is invalid, expired, or revoked.",
"request_id": "8f14e45fceea167a5a36dedd4bea2543",
"param": "aspect_ratio"
}
}{
"error": {
"type": "authentication_error",
"code": "invalid_api_key",
"message": "The provided API key is invalid, expired, or revoked.",
"request_id": "8f14e45fceea167a5a36dedd4bea2543",
"param": "aspect_ratio"
}
}{
"error": {
"type": "authentication_error",
"code": "invalid_api_key",
"message": "The provided API key is invalid, expired, or revoked.",
"request_id": "8f14e45fceea167a5a36dedd4bea2543",
"param": "aspect_ratio"
}
}{
"error": {
"type": "authentication_error",
"code": "invalid_api_key",
"message": "The provided API key is invalid, expired, or revoked.",
"request_id": "8f14e45fceea167a5a36dedd4bea2543",
"param": "aspect_ratio"
}
}{
"error": {
"type": "authentication_error",
"code": "invalid_api_key",
"message": "The provided API key is invalid, expired, or revoked.",
"request_id": "8f14e45fceea167a5a36dedd4bea2543",
"param": "aspect_ratio"
}
}{
"error": {
"type": "authentication_error",
"code": "invalid_api_key",
"message": "The provided API key is invalid, expired, or revoked.",
"request_id": "8f14e45fceea167a5a36dedd4bea2543",
"param": "aspect_ratio"
}
}{
"error": {
"type": "authentication_error",
"code": "invalid_api_key",
"message": "The provided API key is invalid, expired, or revoked.",
"request_id": "8f14e45fceea167a5a36dedd4bea2543",
"param": "aspect_ratio"
}
}Ottieni i credits
Leggi il saldo di credits che questa chiave API può spendere.
curl --request GET \
--url https://api.samsa.ai/public/v1/credits \
--header 'Authorization: Bearer <token>'import requests
url = "https://api.samsa.ai/public/v1/credits"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://api.samsa.ai/public/v1/credits', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.samsa.ai/public/v1/credits",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.samsa.ai/public/v1/credits"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://api.samsa.ai/public/v1/credits")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.samsa.ai/public/v1/credits")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body{
"available": 1450,
"spendable_plan_credits": 1200,
"plan_credits": 1200,
"topup_credits": 250,
"period": {
"start": "2026-07-01T00:00:00+00:00",
"end": "2026-08-01T00:00:00+00:00"
},
"scope": {
"type": "unallocated",
"team": {
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"name": "Marketing"
},
"budget": 2000,
"used": 500,
"remaining": 1500
}
}{
"error": {
"type": "authentication_error",
"code": "invalid_api_key",
"message": "The provided API key is invalid, expired, or revoked.",
"request_id": "8f14e45fceea167a5a36dedd4bea2543",
"param": "aspect_ratio"
}
}{
"error": {
"type": "authentication_error",
"code": "invalid_api_key",
"message": "The provided API key is invalid, expired, or revoked.",
"request_id": "8f14e45fceea167a5a36dedd4bea2543",
"param": "aspect_ratio"
}
}{
"error": {
"type": "authentication_error",
"code": "invalid_api_key",
"message": "The provided API key is invalid, expired, or revoked.",
"request_id": "8f14e45fceea167a5a36dedd4bea2543",
"param": "aspect_ratio"
}
}{
"error": {
"type": "authentication_error",
"code": "invalid_api_key",
"message": "The provided API key is invalid, expired, or revoked.",
"request_id": "8f14e45fceea167a5a36dedd4bea2543",
"param": "aspect_ratio"
}
}{
"error": {
"type": "authentication_error",
"code": "invalid_api_key",
"message": "The provided API key is invalid, expired, or revoked.",
"request_id": "8f14e45fceea167a5a36dedd4bea2543",
"param": "aspect_ratio"
}
}{
"error": {
"type": "authentication_error",
"code": "invalid_api_key",
"message": "The provided API key is invalid, expired, or revoked.",
"request_id": "8f14e45fceea167a5a36dedd4bea2543",
"param": "aspect_ratio"
}
}{
"error": {
"type": "authentication_error",
"code": "invalid_api_key",
"message": "The provided API key is invalid, expired, or revoked.",
"request_id": "8f14e45fceea167a5a36dedd4bea2543",
"param": "aspect_ratio"
}
}usage.read. Per i dettagli sui prezzi, vedi Prezzi.
Spendibile dalla credenziale vs. totale dell’organizzazione
La risposta contiene due tipi di numeri.available e spendable_plan_credits rispondono
a «quanto può spendere questa credenziale»; plan_credits e topup_credits sono saldi
a livello di organizzazione.
| Campo | Che cos’è |
|---|---|
available | I credits che questa credenziale può spendere adesso — spendable_plan_credits + topup_credits. È il numero con cui confrontare gli estimated_credits di un job. |
spendable_plan_credits | La parte del pool del piano dentro available: plan_credits limitato da scope.remaining. Uguale a plan_credits quando nulla limita questa credenziale. |
plan_credits | Il pool mensile del piano residuo dell’organizzazione — non necessariamente spendibile per intero da questa credenziale. 0 quando il pool è esaurito, o quando l’abbonamento è annullato e oltre la fine del suo periodo (i top-up restano spendibili). |
topup_credits | I credits residui da acquisti di top-up validi e non scaduti. I top-up sono esenti dai budget dei team, quindi sono sempre spendibili per intero. |
period | Il periodo di fatturazione corrente dell’organizzazione. Il consumo dei budget dei team si azzera con esso. |
scope | Il regime di budget sotto cui questa credenziale spende — vedi sotto. |
available == spendable_plan_credits + topup_creditsspendable_plan_credits <= plan_credits- per un’organizzazione senza team attivi,
spendable_plan_credits == plan_credits
scope.type è org,
spendable_plan_credits è uguale a plan_credits e available è ancora il pool del
piano più i top-up.Il regime di budget (scope)
Un’organizzazione può riservare parti del suo pool mensile del piano come budget per
team, e ogni chiave API può essere assegnata a un team. Una credenziale spende allora da
esattamente un bucket, e le è disponibile solo il margine di quel bucket — ecco perché
available può essere inferiore ai plan_credits dell’organizzazione.
scope.type | Significato | Codice di errore in caso di scoperto |
|---|---|---|
org | L’organizzazione non ha team attivi (il caso comune), oppure è un’organizzazione di sistema esente dal budgeting per team. Nulla limita questa credenziale, quindi budget, used e remaining sono tutti null. | insufficient_credits |
team | La credenziale è assegnata a un team attivo che ha un proprio budget mensile. | insufficient_team_credits |
unallocated | L’organizzazione ha almeno un team attivo e questa credenziale non è assegnata a un team con budget — non ha team, oppure il suo team non ha un budget proprio. budget è l’allocazione mensile del piano meno tutti i budget dei team attivi, con minimo 0. | insufficient_unallocated_credits |
scope.team indica il team a cui la credenziale è assegnata, oppure è null quando non è
assegnata a nessuno. scope.remaining è budget - used, con minimo 0.
Una chiave legata a un team il cui budget è il vincolo attivo:
{
"available": 1450,
"spendable_plan_credits": 1200,
"plan_credits": 8031,
"topup_credits": 250,
"period": {
"start": "2026-07-01T00:00:00+00:00",
"end": "2026-08-01T00:00:00+00:00"
},
"scope": {
"type": "team",
"team": {
"id": "7f3c1e28-9b4d-4a61-8e52-0c7d6b5a4f39",
"name": "Marketing"
},
"budget": 10000,
"used": 8800,
"remaining": 1200
}
}
8031 credits di piano, ma al budget Marketing ne restano
1200: questa chiave può quindi spenderne solo 1200, più i 250 credits di top-up
esenti da budget, per un totale di 1450.
Prevedere e diagnosticare un 402
available è il predittore. Un job i cui estimated_credits sono al massimo pari ad
available non viene rifiutato con un 402 di credits insufficienti e, viceversa, un tale
402 significa che la stima ha superato available — in entrambi i casi il numero
della credenziale, non quello dell’organizzazione. Un’eccezione: un guasto operativo
nella detrazione stessa (per esempio una lettura del saldo degradata) viene segnalato
come il generico insufficient_credits
qualunque sia il regime, senza provare nulla su available — un semplice
insufficient_credits dove ti aspettavi un codice team-aware può quindi essere
transitorio; riprova prima di trattarlo come un saldo esaurito.
available — e persino cambiare
scope.type, e quindi il codice di errore che ricevi — senza alcuna spesa da parte
tua. subscription_inactive è un 402
distinto, non legato a questo confronto.scope lo spiega:
scope.typeindica il regime di budget, ed è questo a selezionare il codice di errore (salvo il fallback operativo di cui sopra) — vedi la tabella sopra. Non dice quale saldo si è esaurito: una chiave nel regimeteamriceveinsufficient_team_creditsanche quando è il pool del piano dell’organizzazione, e non il budget del team, a essere esaurito.- Per trovare il vincolo reale sotto un regime limitato (
teamounallocated), confrontaplan_creditsconscope.remaining— il minore dei due limitaspendable_plan_credits. Sottoorg,scope.remainingènulleplan_creditsè l’unico vincolo possibile. remaining: 0significa che quel bucket è completamente speso per laperiodcorrente. Non è una precondizione dell’errore: un bucket conremaining: 100e senza top-up rifiuta comunque un job che costa200.
available: 0 con scope.type: "unallocated" e remaining: 0 —
ogni invio che costa credits verrà rifiutato con insufficient_unallocated_credits finché un budget non
viene abbassato, la chiave non viene assegnata a un team con margine, o non vengono
acquistati top-up. Vedi Errori per come
risolvere ciascun codice.Autorizzazioni
Organization API key as a bearer token: Authorization: Bearer samsa_sk_....
Risposta
Successful Response
GET /credits body — what the CALLING credential can spend (ADR §10, SAM-1029).
Two kinds of number:
available/spendable_plan_creditsanswer "what can THIS API key or OAuth connection spend right now" — capped by its team budget bucket (scope), so a job estimated at or belowavailableis not rejected withinsufficient_team_credits/insufficient_unallocated_credits.plan_credits/topup_creditsare the ORGANIZATION-wide balances.
All amounts are balances (not allocations), and
available == spendable_plan_credits + topup_credits always holds, as does
spendable_plan_credits <= plan_credits (they are equal whenever no team
budget applies).
Total credits the calling credential can spend right now — the sum of spendable_plan_credits and topup_credits. Capped by the credential's team budget bucket (see scope), so it can be lower than the organization-wide plan_credits.
1450
Plan-pool credits the calling credential can spend right now: plan_credits capped by scope.remaining. Equal to plan_credits when no team budget applies.
1200
The ORGANIZATION's remaining monthly subscription-pool credits — not necessarily all spendable by this credential (see spendable_plan_credits). 0 when the plan pool is exhausted or the subscription is canceled and past its period end (top-up credits stay spendable).
1200
Remaining credits from valid (non-expired) top-up purchases. Top-ups are exempt from team budgets, so they are always fully spendable by any credential of the organization.
250
The org subscription's current billing-period bounds (ISO-8601).
Show child attributes
Show child attributes
The credit bucket the calling credential spends from (SAM-1029).
Organizations can reserve parts of their monthly plan pool for teams. A
credential (API key or OAuth connection) spends from exactly ONE bucket, and
only that bucket's headroom is available to it — which is why available
can be lower than the organization-wide plan_credits. The shortfall rule
is cost > available (spendable_plan_credits + topup_credits): a submit
is refused with an insufficient-credit 402 when the deduction,
re-evaluating the same numbers at execution time, finds the estimated cost
above available — insufficient_credits under the uncapped org regime,
insufficient_team_credits under team, and
insufficient_unallocated_credits under unallocated (operational
failures inside the deduction fall back to the generic
insufficient_credits, whatever the regime). This response is a
point-in-time snapshot, not a reservation: concurrent spends, top-up lots
expiring, a canceled subscription's period ending, or a degraded balance
lookup can make the deduction-time numbers differ from the ones reported
here, and other checks can reject a request regardless of credits.
type names the budget regime and selects which of those codes a balance
shortfall raises — it does not say which balance ran out: under a capped
regime the smaller of plan_credits and remaining limits
spendable_plan_credits.
remaining: 0 means this bucket has no plan-pool headroom left for the
current period (spent down, or never allocated any); it is NOT a
precondition of the error — a bucket with remaining: 100 and no top-ups
still refuses a job costing 200.
Show child attributes
Show child attributes

