API reference

Errors, rate limits and retries

Every SubToAPI error is JSON with error, message and request_id. Status codes, what they mean, request size limits, per-plan rate limits and a retry strategy with backoff.

Updated

Errors never leak provider internals. You always get the same small JSON object, a meaningful HTTP status and a request id to quote.

error.json
{
  "error": "rate_limit_exceeded",
  "message": "Too many requests for this API key. Try again shortly.",
  "request_id": "req_8e1e98b6e9e3b542ad4b71ae"
}

Status codes

  • 401

    invalid_api_key

    Missing, malformed, revoked or unknown SubToAPI API key.

  • 403

    entitlement_required

    The account needs an active trial or subscription.

  • 409

    provider_not_connected

    Claude is not connected server-side.

  • 413

    request_too_large

    Request body exceeds the API size limit.

  • 422

    invalid_request

    Malformed JSON or invalid request fields.

  • 429

    rate_limit_exceeded

    The API key exceeded its current plan limit.

  • 502

    provider_error

    The AI provider could not complete the request.

  • 503

    provider_unavailable

    The AI provider is temporarily unavailable.

Rate limits

Limits are per API key and endpoint in a fixed one-minute window: 30 requests/min on the trial, 60 on Basic, 300 on Pro and 1000 on Ultimate. A 429 includes a retry-after header in seconds.

Size limits

Text fields

500k chars

system / content · 500,000 chars each

Conversation messages

200

turns per /v1/conversation call

Tools

50

tool definitions per request

Request body

4 MB

/v1/* · 4,000,000 bytes

Retry strategy

Retry 429, 502 and 503 with exponential backoff and jitter; never retry 4xx validation errors blindly. Requests are not idempotent on the provider side, so cap attempts.

retry.ts
async function withRetry<T>(fn: () => Promise<Response>, attempts = 4): Promise<Response> {
  for (let i = 0; i < attempts; i++) {
    const res = await fn();
    if (res.status !== 429 && res.status < 500) return res;
    const retryAfter = Number(res.headers.get("retry-after")) || 0;
    await new Promise((r) => setTimeout(r, retryAfter * 1000 || 400 * 2 ** i + Math.random() * 200));
  }
  throw new Error("gave up after retries");
}

Frequently asked questions

What does 409 provider_not_connected mean?
Claude is not connected for your team. Open Connection in the dashboard and finish the guided setup; API keys keep working afterwards.
What does 403 entitlement_required mean?
The trial ended or the subscription lapsed. The owner can fix it on the billing page; no keys are lost.