Account Status
Checks whether an API key can make billable API calls right now. If it can't, the response says why and when the block may clear.
Call this endpoint after an unexpected 402, after a 429 whose msg starts
with Rate Limited. Monthly quota used., or before a large batch job. It gives a
single machine-readable verdict instead of leaving you to work out the account's
state from individual error messages. It also works when the key is blocked,
because a blocked key is the one that needs this check most.
The verdict uses the same payment and quota checks as the billable endpoints, so for account-wide blocks it matches what a real request would get: an expired key or subscription, depleted credits, a billing limit, the trial daily allowance or the monthly quota.
It does not report the short-term, per-endpoint rate limit. If one endpoint
answers 429 with Rate limited. Capacity used., this endpoint can still
return api_access: true, because each endpoint has its own allowance. For
that case, use the 429's retry_after and the X-RateLimit-* headers
described in Rate Limits & Errors.
Request Parameters
https://api.sapling.ai/api/v1/account/status
HTTP method: GET or POST
key: String
32-character API key. With GET, pass it as a ?key= query parameter. With
POST, pass it in the JSON body. With either method you can instead send it as
a bearer token in the Authorization header. If both are provided, the key
parameter takes precedence.
The endpoint takes no other parameters.
- The public client-side key used by Sapling's website demos gets a
403. - OAuth access tokens issued for the MCP server also get a
403. Send an API key instead.
This call is not billed. Each call counts as 2,000 characters against a separate rate-limit bucket, so polling it doesn't use up your main allowance. Check it when something goes wrong rather than before every request.
Sample Code
- cURL
- Python
- JavaScript
curl "https://api.sapling.ai/api/v1/account/status?key=<api-key>"
import requests
from pprint import pprint
try:
response = requests.get(
"https://api.sapling.ai/api/v1/account/status",
params={"key": "<api-key>"},
)
if 200 <= response.status_code < 300:
pprint(response.json())
else:
print('Error: ', str(response.status_code), response.text)
except Exception as e:
print('Error: ', e)
import { Client } from '@saplingai/sapling-js/client';
const client = new Client('<api-key>');
const { data: status } = await client.accountStatus();
if (!status.api_access) {
console.log(`Blocked (${status.blocked_reason}): ${status.message}`);
console.log(`Check again in ${status.retry_after} seconds`);
}
Response Parameters
A JSON object. api_access, blocked_reason, message and usage are always
present.
api_access: Boolean
true when no account-wide block applies to this key. An individual endpoint
can still answer a short-term 429 if its own allowance is used up.
blocked_reason: String or null
null when api_access is true. Otherwise it is one of the values below.
These values are stable, so you can safely branch on them in code.
blocked_reason | Meaning | What a real request returns | Clears when |
|---|---|---|---|
key_expired | The API key has expired. | 402 | You renew the subscription or buy credits. |
subscription_expired | The API subscription is no longer active. | 402 | You resubscribe or buy credits. |
credits_depleted | The prepaid credit balance is used up. | 402 | You buy more credits. |
monthly_billing_limit | The account reached the monthly billing limit set on its subscription. | 402 | You raise the limit in API settings, or the next billing month starts. |
trial_daily_limit | A free-trial key used up its daily character allowance. | 402 | The next UTC midnight. |
monthly_quota_exhausted | The key used up its monthly character quota. | 429 | The next UTC month starts. |
message: String or null
Human-readable explanation of the block, the same text a real request's msg
would contain. null when not blocked.
retry_after: Integer
Only present when blocked. The number of seconds until the block may clear,
matching the Retry-After header a real request would carry:
trial_daily_limit: seconds until the next UTC midnight.monthly_quota_exhausted: seconds until the next UTC month starts.- Every other reason:
3600. That is only a backoff hint. These blocks don't clear on their own, so resolve them in your API settings.
usage: Object
- monthly_quota_chars (Integer or null): this key's monthly character quota, or
nullwhen it has none. - month_start (String (ISO date)): first day of the current UTC month.
- chars_this_month (Integer): characters used by the account since
month_start. This figure is updated periodically, so it can trail your most recent requests slightly.
subscription: Object
- active (Boolean): whether the account has an API subscription.
- trial_only (Boolean): whether the account has only free-trial API access.
- key_expired (Boolean): whether this key has passed its expiry date.
credits: Object
The prepaid credit summary shown in the API dashboard. When
credits.enabled is false, the account has no prepaid credits and only
currency and alert_threshold_percent are included. Otherwise it also
includes:
balance_credits,purchased_credits,consumed_creditsandconsumed_percentlow_balanceanddepletedbilling_modeandstops_when_depletedauto_topup
One credit equals one US dollar.
trial: Object
Only present for free-trial accounts.
- daily_limit_chars (Integer or null): the daily character allowance.
- chars_used_today (Integer): characters used since the last UTC midnight.
- resets_at (String): always
utc_midnight.
A team-scoped key can be shared by many people, so its response leaves out the
personal subscription, credits and trial sections, and
usage.month_start and usage.chars_this_month. You still get the verdict
fields (api_access, blocked_reason, message, retry_after) and
usage.monthly_quota_chars.
Sample Response
A free-trial key that has used up today's allowance:
{
"api_access": false,
"blocked_reason": "trial_daily_limit",
"message": "Free trial daily API character limit reached. Purchase API credits at https://sapling.ai/api_settings#billing to continue, or wait for the limit to reset at UTC midnight.",
"retry_after": 22140,
"usage": {
"monthly_quota_chars": 250000,
"month_start": "2026-09-01",
"chars_this_month": 131872
},
"subscription": {
"active": false,
"trial_only": true,
"key_expired": false
},
"credits": {
"enabled": false,
"currency": "USD",
"alert_threshold_percent": 80.0
},
"trial": {
"daily_limit_chars": 100000,
"chars_used_today": 100412,
"resets_at": "utc_midnight"
}
}
A key that is ready to use:
{
"api_access": true,
"blocked_reason": null,
"message": null,
"usage": {
"monthly_quota_chars": 25000000,
"month_start": "2026-09-01",
"chars_this_month": 1840533
},
"subscription": {
"active": true,
"trial_only": false,
"key_expired": false
},
"credits": {
"enabled": false,
"currency": "USD",
"alert_threshold_percent": 80.0
}
}
Errors
| Status | When |
|---|---|
401 | The key is missing or invalid: {"msg": "Missing API key."} or {"msg": "Invalid API key."}. |
403 | The public demo key or an MCP OAuth access token was sent. |
429 | The status bucket is exhausted. Wait retry_after seconds. |
See Rate Limits & Errors for the shared error format.