Skip to content

Errors and limits

What it answers when it refuses

Every refusal carries a code and a message that names the thing that was wrong — the missing scope, the rule that refused, the allowance that ran out. A refusal is never a silent empty result.

Token limit
600/1h
Agent limit
1200/1h
Idempotency
24h
Page size
100 max

Every code

  • 400

    invalid_parameter

    A query parameter is malformed, or a cursor is not one this API issued.

  • 400

    invalid_body

    The body is not a JSON object.

  • 400

    invalid_request

    The request is well formed but the rules refuse it; the message says which rule.

  • 401

    unauthorized

    No token, or one that is unknown, revoked or expired. Each is said the same way.

  • 402

    plan_required

    The workspace’s plan does not include this: API access, or room for more contacts.

  • 403

    forbidden

    The token does not carry the scope the endpoint needs; the message names it.

  • 404

    not_found

    Nothing with that id in this workspace.

  • 409

    conflict

    The record is in a state that refuses this, such as a test that has already been decided, or a request with the same Idempotency-Key is still being answered.Retry shortly, with the same key.

  • 422

    idempotency_key_reused

    The Idempotency-Key was used with a different request in the last 24 hours. A new request needs a new key.

  • 429

    rate_limited

    More than 600 requests from one token in the hour. Retry-After says when to try again.Retry after the time Retry-After gives.

  • 500

    internal

    Something failed on our side, and it has been recorded.Retry with the same Idempotency-Key.

ShapeExample
{ "error": { "code": "forbidden", "message": "This token does not carry read:growth." } }

Rate limits

  • Workspace token
    600 requests an hour, per token.
  • Agent credential
    1200 an hour, per credential. Higher, because an agent asks before every action it takes.
  • X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, Retry-After
    On every answer, not only on a 429 — so you can slow down before you are stopped.

Being over a plan’s allowance is plan_required, not rate_limited, and the message says what remains. They are different problems: one is solved by waiting, the other never is.

Retrying a write safely

Send Idempotency-Key on a write. If the same request arrives again within 24 hours, the first answer is returned rather than the work being done twice, and Idempotent-Replayed says so.

  • Same key, same request
    The first answer, replayed. Nothing happens a second time.
  • Same key, different request
    Refused with idempotency_key_reused. A new request needs a new key — otherwise a typo could silently inherit an unrelated result.
  • Still in flight
    Refused with conflict. Try again shortly.
A safe retryExample
curl -X POST "$XYRANIK_HOST/api/v1/actions" \
  -H "Authorization: Bearer xyr_live_…" \
  -H "Idempotency-Key: 1f0c9e2a-…" \
  -H "Content-Type: application/json" \
  -d '{ … }'

Generate the key once per logical operation — a UUID — and reuse it for every retry of that operation.

What gets counted

Every call is counted where it was answered, whatever it was answered with — succeeded, refused, rate-limited or failed — so your own usage page shows refusals rather than hiding them. Counting began on 2026-09-11; nothing before that date is reconstructed.

Your workspace’s own figures are in your developer settings, which open after you sign in.