Skip to main content

Errors

Every error carries a stable code to branch on, since one HTTP status can mean several things

An error answer is JSON with a code and a message:

{ "code": "insufficient_scope", "message": "The token lacks the required scope." }

code is stable and safe to branch on. message is for people and can change. Some routes add fields: credits_required carries the balance, and plan_invalid, email_invalid and content_invalid list every broken rule in details. A few older routes also send runsRemaining; ignore it.

Over MCP, a failed tool call returns isError: true with the text code: message, using the same codes. A credits_required tool error also carries the fields below as structuredContent.

Branch on the code​

402 means more than one thing. credits_required means you need credits. no_runs_remaining, plan_limit and agent_limit are plan limits that credits do not lift. paid_required means the workspace needs paid access. Always read code:

status=$(curl -s -o answer.json -w '%{http_code}' https://app.makesmores.com/api/v1/viral-score \
-H "Authorization: Bearer $SMORE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"network": "x", "post": "Shipping our first public API today"}')
if [ "$status" != 200 ]; then
case "$(jq -r .code answer.json)" in
credits_required) echo "Top up: $(jq -r .topup_url answer.json)" ;;
rate_limited) echo "Slow down and retry later" ;;
*) jq . answer.json ;;
esac
fi
import os, time, requests

def call(path, body):
r = requests.post(
f"https://app.makesmores.com/api/v1/{path}",
headers={"Authorization": f"Bearer {os.environ['SMORE_API_KEY']}"},
json=body,
)
if r.ok:
return r.json()
err = r.json()
if err.get("code") == "credits_required":
raise RuntimeError(f"Out of credits ({err['reason']}): top up at {err['topup_url']}")
if err.get("code") == "rate_limited":
time.sleep(int(r.headers.get("Retry-After", "5")))
return call(path, body)
raise RuntimeError(f"{err.get('code')}: {err.get('message')}")

402 credits_required​

The call needs more credits than the workspace has, or than the key's monthly cap allows. Nothing ran and nothing was charged.

{
"code": "credits_required",
"reason": "balance",
"message": "Not enough credits for this call.",
"balance": 1200,
"needed": 3318,
"topup_url": "https://app.makesmores.com/app/console/billing"
}
FieldMeaning
reasonbalance when the workspace is short, key_cap when this key reached its monthly cap
balanceCredits available to this call: purchased and granted credits only for a scan, or what the key has left this month for key_cap
neededWhat the call deducts before it runs: its most possible cost. For a draft, every model's share added up; for a scan, max_credits
topup_urlWhere an owner buys credits

For balance, an owner tops up on Billing. For key_cap, an owner or admin raises the key's cap on API keys, or you use another key. Scans need purchased credits, so a workspace with only free credits gets balance for a scan. See Credits.

Auth and permission errors​

StatusCodeMeaning
401invalid_tokenThe key or token is missing, wrong, revoked or expired. Authenticate again or create a new key
403insufficient_scopeThe key or connection lacks the scope this route needs. Create a key with the scope ticked, or reconnect and approve it
403admin_requiredOnly owners and admins can do this, such as changing trackers or the watched set
403owner_requiredOnly the workspace owner can do this
403pilot_disabledThe product is not open for this workspace yet
402, 403paid_requiredThe action needs paid access. Purchased runs pass it, and so do purchased or granted credits for approved outreach and X topic watches

Limits​

StatusCodeMeaning
429rate_limitedToo many calls in the window. Wait for the seconds in Retry-After
429drafts_queue_fullToo many drafts are open. Poll the open ones and submit again when one finishes
402plan_limit, agent_limit, no_runs_remainingYour plan's limit for agents or runs

Shared limits stay 429 or 403, never 402. Rate limits lists them by product.

Request errors​

StatusCodeMeaning
400invalid_requestThe body or query is not valid: a missing or unknown field, a duplicate key, a wrong type or a null
400draft_type_coming_soon, model_coming_soonThat draft type or model is listed as coming soon and does not run yet. Nothing was stored or charged
404not_found, draft_not_found, tracker_not_found, scan_not_foundNothing with that id in this workspace
404console_offThe console, GET /api/v1/models and GET /api/v1/credits are switched off for now
404scans_offScans are not open for this workspace
409idempotency_conflictThe idempotency_key was used for a different request. Send a new key for new work
409reddit_signals_off, drafts_offThe product is switched off for now
410model_closedThe model was retired. Nothing was charged
422plan_invalid, email_invalid, content_invalidThe request breaks the product's rules. details lists each {field, rule}
503credits_unavailableS'more could not check credits, so the call did not run. Try again shortly
503reddit_signals_unavailable, scoring_unavailable, scoring_busy, scans_unavailableTry again shortly

Each product page lists the codes particular to it.

Retries​

  • Retry 429 after Retry-After, and 503 with backoff.
  • Do not retry 4xx codes other than 429 without changing the request.
  • Drafts and scans take an idempotency_key. Resending the same body with the same key returns the first job and charges nothing more, so retrying a submit after a timeout is safe.