لم تُترجَم هذه الصفحة إلى العربية بعد. يتم عرض النص الإنجليزي الأصلي.

Limits and 402

What happens when the balance runs out

MyIP OpenRouter enforces two kinds of limit: how much you can spend (credits) and how often you can call (request rate). The first returns 402, the second 429.

LimitWhat it governsErrorWhere to check
Account balanceWhat the whole account can spend402 insufficient_creditsGET /api/v1/credits
Per-key limitWhat one API key can spend402 key_limit_exceededGET /api/v1/keylimit_remaining
Request rateRequests per second429 rate_limit_exceededRetry-After on the response

Checking your current limits

bash
curl https://openrouter.myip.co.kr/api/v1/key \
  -H "Authorization: Bearer $MYIP_API_KEY"

The fields that matter. Every amount is in Korean won (KRW).

limitnumber | null

The spending cap configured on this key. null means unlimited.

limit_remainingnumber | null

Remaining allowance. null whenever limit is null.

limit_resetstring | null

How often the cap resets: one of never, daily, weekly, monthly, or null.

usagenumber

What this key has spent inside the current reset window. With never or null, it is the all-time total.

usage_daily / usage_weekly / usage_monthly

Spend today, this week, and this month. Period boundaries are in Asia/Seoul.

The account-wide balance has its own endpoint:

bash
curl https://openrouter.myip.co.kr/api/v1/credits \
  -H "Authorization: Bearer $MYIP_API_KEY"
json
{ "data": { "total_credits": 11000, "total_usage": 42.317 } }

Your balance is total_credits - total_usage.

402 — insufficient credits

Every request checks the balance before anything is sent upstream.

json
{
  "error": {
    "code": 402,
    "message": "크레딧이 부족합니다.",
    "metadata": { "error_type": "insufficient_credits", "balance_krw": "-5000.000000" }
  }
}

The 5,000 KRW buffer

We do not cut you off the instant the balance reaches zero. Requests are allowed down to -5,000 KRW.

That buffer exists because of streaming. Several streamed requests can pass the balance check together and then settle one after another as they finish, briefly pushing the balance negative. The buffer absorbs that overshoot.

The boundary is exclusive: at a balance of -5000 or below, the buffer is spent and new requests are rejected with 402.

Zero balance suspends keys

When settlement leaves the balance at zero or below, every active key on that account is automatically moved to suspended_no_credit. Requests with such a key return 402 insufficient_credits.

A successful top-up reactivates them automatically. You do not need to create new keys.

Clearing a 402

  1. Top up at /settings/credits. The moment the balance is positive, auto-suspended keys come back.
  2. If you hit a per-key cap (key_limit_exceeded), raise the limit at /settings/keys or wait for the limit_reset window to roll over.
  3. Watch it proactively — poll limit_remaining from GET /api/v1/key and the balance from GET /api/v1/credits.

Blocked even after topping up?

There are three distinct key states:

Key statusSet byCleared byResponse
suspended_no_creditSystem (balance ≤ 0)Automatically, on top-up402 insufficient_credits
suspended_adminAn administratorAn administrator only403 key_suspended
revokedYou or an administratorNobody — permanent401 invalid_api_key

A 403 key_suspended has nothing to do with your balance and will not clear on payment; contact us. A revoked key can never be restored, so issue a new one.

429 — too many requests

Rate limiting is applied per IP address. Splitting traffic across accounts or keys does not raise the limit for the same IP. Traffic that overshoots dramatically is cut off by the reverse proxy before it reaches the application, in which case you get the proxy's error page rather than JSON.

When the application itself rejects the request, the body looks like this:

json
{
  "error": {
    "code": 429,
    "message": "요청이 너무 잦습니다. 잠시 후 다시 시도하세요.",
    "metadata": { "error_type": "rate_limit_exceeded", "retry_after_sec": 60 }
  }
}

Whenever metadata.retry_after_sec is present, the response also carries a Retry-After header. Back off exponentially, but honour that value first.

When an upstream provider rate-limits us, the error reaches you as 502 provider_error with the provider's original code in metadata.provider_code. In that case model fallbacks or provider routing are usually the better fix.

Hitting a limit mid-stream

Once the first byte is out, the HTTP status can no longer change, so the error arrives as an SSE event instead:

text
data: {"id":"gen-01J...","object":"chat.completion.chunk","created":1788000000,"model":"lgai/exaone-4.0-32b","provider":"MyIP Local","error":{"code":502,"message":"upstream disconnected","metadata":{"error_type":"provider_error"}},"choices":[{"index":0,"delta":{"content":""},"finish_reason":"error"}]}

Stream-reading code must check each chunk for an error key and for finish_reason: "error". See Streaming for worked examples.

Other limits

ItemValue
Upstream response timeout600 seconds (then 408 timeout)
Request body size25 MB
Local model start-up budget25 seconds by default; then the next candidate, or 503 model_loading if there is none
Pending payment orders10 per account

503 model_loading means a local GPU model is still coming up. Wait metadata.retry_after_sec and retry; it usually succeeds. Cold starts are explained in the FAQ.

Error body shape

Every error has the same shape, and the HTTP status always equals error.code.

json
{
  "error": {
    "code": 402,
    "message": "A human-readable sentence",
    "metadata": { "error_type": "insufficient_credits" }
  }
}

Branch on error.metadata.error_type in code. The message is for humans and its wording can change. The full table is in Errors and debugging.

آخر تحديث ٠٥‏/٠٩‏/٢٠٢٦