Skip to content

Fryri documentation

Authentication and keys

Updated 2026-08-27

An API key authenticates every request to https://api.fryri.com/v1. Create a key in the developer console, then send it as a Bearer token in the Authorization header. A request without a live key fails with 401 unauthorized.

Request

curl https://api.fryri.com/v1/usage \
  -H "Authorization: Bearer $FRYRI_API_KEY"

Response

{
  "key": {
    "scope": "key",
    "api_key_id": 104,
    "period_days": 30,
    "total_tokens": 89209,
    "total_cost_usd": 0.06452,
    "total_requests": 17,
    "by_request_type": [
      {"request_type": "api_ask", "tokens": 0, "cost_usd": 0.06, "requests": 1},
      {"request_type": "api_search", "tokens": 0, "cost_usd": 0.0025, "requests": 1},
      ...
    ]
  },
  "account": {
    "scope": "account",
    "api_key_id": null,
    "period_days": 30,
    ...
  }
}

Billing#

API spend comes from your prepaid credit balance. GET /v1/wallet shows the balance and GET /v1/usage shows spend; both stay reachable even when the balance is empty, so you can always check state and top up.

Errors#

Every error under /v1 returns one envelope. error.code is stable and machine-readable; error.message is prose and may change. error.retryable says whether the same call, sent again unchanged, can succeed. request_id is the id to quote when reporting a problem; the X-Request-ID header carries the same value.

HTTPcoderetryableRecovery
400bad_requestfalseFix the request as error.message describes.
401unauthorizedfalseSend a live key as Authorization: Bearer $FRYRI_API_KEY.
402insufficient_creditsfalseAdd credit through POST /v1/wallet/topup-link (echoed in error.topup_endpoint), then retry.
403forbiddenfalseThe key cannot act on this resource; use the owning account's key.
404not_foundfalseCheck the id; a deleted resource reads as missing.
409conflictfalseRead the current state, then resend.
422invalid_requestfalseFix the field named in error.message ({field}: {reason}); error.errors lists every violation.
429rate_limitedtrueWait for the Retry-After header, then retry.
500internal_errortrueRetry once; quote request_id if it persists.
502upstream_errortrueRetry.
503unavailabletrueRetry after a pause.
504upstream_timeouttrueRetry.

On a streaming POST /v1/chat ("stream": true) an error after the first byte arrives as an event of "type": "error" with the same error.code and error.message fields. Its codes are byok_key_rejected (your provider refused the key you passed) and byok_request_rejected (your provider refused the request, usually a wrong model slug). upstream_error means the model returned an error or the stream ended early; internal_error is a failure on Fryri's side.

Unauthenticated request

curl https://api.fryri.com/v1/usage
{
  "error": {
    "code": "unauthorized",
    "message": "Invalid or missing API key. Pass it as 'Authorization: Bearer fryri_sk_...'.",
    "retryable": false
  },
  "request_id": "ed1de53e1bf7413891649e7e825f5629"
}

Invalid body

curl -X POST https://api.fryri.com/v1/capture \
  -H "Authorization: Bearer $FRYRI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{}'
{
  "error": {
    "code": "invalid_request",
    "message": "Provide exactly one of `text` or `content_base64`.",
    "retryable": false
  },
  "request_id": "b73721361a2c406fa8d36ee66dbd13c1"
}

The machine-readable contract#

Endpoints under /v1/evolving (beta) may change. Every other endpoint is stable.