Errors

Error reference

Every Pluto API error is a JSON envelope with a stable, machine-readable code:

{
  "error": {
    "code": "quota_exceeded",
    "message": "Period quota exhausted for current tier",
    "fix_hint": "Wait for period reset or upgrade tier. Check /v1/usage for details.",
    "docs_url": "https://docs.joinpluto.com/billing"
  }
}

Fields:

  • code (always) — snake_case error identifier. Branch on this, not on message.
  • message (always) — human-readable description.
  • retry_after (sometimes) — seconds to wait before retrying (rate limits).
  • fix_hint (sometimes) — what to change; written for both humans and agents.
  • docs_url (sometimes) — where to read more.

Status codes

Success: 200 OK, 201 Created, 202 Accepted (async work queued — poll the returned URL), 204 No Content (deletes).

StatusCommon codesMeaning + what to do
400bad_request, validation_failedMalformed JSON or a body that fails schema validation. validation_failed messages list each bad field as path: issue, joined by ;. Fix the request; don't retry as-is.
401unauthorizedMissing/malformed Authorization header, invalid key, revoked key, or expired session key. Send Authorization: Bearer sk_live_....
402quota_exceededPeriod quota (actions / agent runs / workflows) or your tier's dollar spend cap is exhausted. Check GET /v1/usage; wait for the period reset or upgrade. Rarely, this is returned when the usage meter is temporarily unreadable — Pluto fails closed rather than allowing unmetered spend — so a retry after a short wait is reasonable if /v1/usage shows you under quota.
403forbiddenThe key's scopes don't cover the operation. Mint a key with the required scope (or the * wildcard).
404not_foundResource doesn't exist or belongs to another tenant.
409conflict, already_exists, account_not_connectedState conflict — e.g. creating a contact that already exists, or calling a social endpoint before connecting that platform's account. The fix_hint says which.
410claim_expired, claim_consumedOne-time checkout key claims: the key was already retrieved, or the pending claim expired. Contact support with your Stripe session id.
422idempotency_collisionThe Idempotency-Key you sent was already used with a different request body. Use a fresh key per logical operation.
429rate_limitedPer-minute rate limit hit. See below.
500server_errorSomething broke on Pluto's side. Retry with exponential backoff; contact support if it persists.
501(varies)Documented capability that isn't live yet — e.g. deploy_mode_not_wired from POST /v1/onpage/apply when asked for its cf_worker/owned_source deploy modes. The message and fix_hint say what to use instead. What you can call is exactly what the API Reference ↗ shows as live.
503stripe_unconfigured, admin_not_configuredA server-side dependency isn't configured. Contact support.

A code is more stable than its status: a few codes arrive with different statuses depending on the endpoint — account_not_connected, for example, is returned with 409 (TikTok/YouTube insights), 400 (Meta insights), or 404 (Instagram DMs). Branch on error.code.

Rate limits (429)

Per-minute rate limits are enforced on specific endpoint groups — currently contacts, sequences, inbox, DMs (Discord/Facebook/Telegram), comment automations, content articles, listing agent runs, and call detail. On those endpoints the limit is per tenant, per endpoint over a 60-second sliding window, with the ceiling set by your tier:

TierRequests / minute
Free30
Hobby120
Pro600
Scale3,000
EnterpriseCustom

Endpoints outside those groups do not return 429 today — they are bounded by your period quotas and spend cap instead (402, below).

A 429 response includes:

  • Retry-After header — seconds to wait before retrying (also retry_after in the body).
  • X-RateLimit-Remaining: 0 and X-RateLimit-Limit headers.

Correct client behavior: sleep Retry-After seconds, then retry the identical request. Rate limits are transient — nothing is consumed from your monthly quota by a 429.

Quota exhaustion (402)

Quotas are per billing period (currently the calendar month, UTC). Three separate ceilings can trigger quota_exceeded:

  1. Actions — every metered request counts as one.
  2. Agent runs / workflow runs — counted separately on top of actions.
  3. Spend cap — a hard dollar ceiling per tier on total metered cost.
{
  "error": {
    "code": "quota_exceeded",
    "message": "Period quota exhausted for current tier",
    "fix_hint": "Wait for period reset or upgrade tier. Check /v1/usage for details.",
    "docs_url": "https://docs.joinpluto.com/billing"
  }
}

Unlike 429, a 402 will not clear by waiting a few seconds — check GET /v1/usage to see which counter is exhausted and when the period resets (period_end).

Retry cheat sheet

GotRetry?
429Yes — after Retry-After seconds.
500Yes — exponential backoff.
502/503/timeoutsYes — exponential backoff.
402Only after the period resets or an upgrade.
400, 401, 403, 404, 409, 422No — fix the request, key, or state first.

For write endpoints that accept an Idempotency-Key header, reuse the same key when retrying so a retried request can never double-apply.