Skip to main content

Overview

ION applies fair-use guardrails to the /graphql endpoint so one caller’s heavy queries can’t degrade the API for everyone. To make guardrails discoverable rather than surprising, responses that touch a guardrail carry a limits block inside GraphQL extensions:
The block contains one field:
  • violations: a list of guardrails your request bumped against. Only present when non-empty. A clean request omits the block entirely.
Static caps (page size, depth, aliases, tokens, root fields) carry actual and limit numbers as shown. Rate-limit entries carry only the rule, retryAfter when applicable, and a message. The underlying rate budget is plan-dependent and intentionally not published in responses; follow the guidance in message rather than keying off a threshold.

When the block appears

The limits block is part of the response extensions, alongside anything else ION reports there. Treat it as optional and additive: parse it if present, ignore it if absent. Your existing data / errors handling does not change. A guardrail surfaces one of two ways, depending on where ION is in rolling a limit out to your org: Advisory is your window to adapt a query before it starts getting rejected. Treat any violations entry as “fix this and you’ll be fine when enforcement turns on.”
A successful response with no violations carries no limits block at all. If you see extensions.limits, there’s something to address.

The violations array

Each entry is an object identified by its rule. Every entry carries a human-readable message; the other fields depend on the rule family.

Structural (query shape)

Static caps on the shape of the query document: page size, nesting depth, aliases, document size, and number of top-level fields.
actual and limit are always present on structural entries. field is included where a specific field is implicated (for example, pagination or depth) to point you at the offending part of the query.

Rate limit

A per-tenant token bucket meters sustained throughput. Because it’s a throughput signal, this entry carries the retry hint you need to back off:
Back off for retryAfter seconds, then resume. Spacing requests out helps you stay under the sustained rate.
A rate_limit entry without retryAfter is a permanent block: { "rule": "rate_limit", "message": "Access blocked. Contact support if you believe this is in error." }. Retrying doesn’t succeed until an operator lifts the block. Surface the message to the user and contact FirstResonance support, and don’t build a retry loop around it.

What enforcement looks like

When a guardrail is enforced rather than advisory, the same condition comes back as a GraphQL error carrying a machine-readable code, and the request is rejected. Key off errors[].extensions.code, not the message text: Full payload shapes and remediation live in Error Codes. The field vocabulary matches the advisory violations entries, so one parser can handle both paths. For any integration, and especially unattended or agentic workflows that generate queries dynamically:
  1. Check extensions.limits.violations on every response. No limits block means everything is clean; if the block is present, treat each entry as a to-do before enforcement flips on.
  2. On a transient rate_limit (or HTTP 429), honor retryAfter with backoff and jitter. Don’t hot-loop.
  3. On a rate_limit without retryAfter (or the BLOCKED error code), stop retrying. This is a permanent block that only lifts when an operator changes the configuration. Surface the message and contact support.
  4. On structural violations, adapt the query. Use smaller pages, fewer fields, paginate, or split the operation. Retrying the same shape won’t help.
  5. Don’t hard-code specific limit values. Structural actual and limit numbers are stable but plan-dependent; read them from the response so your client stays correct as plans evolve.
  • Error Codes: enforcement payloads (QUERY_COMPLEXITY_EXCEEDED, RATE_LIMITED, BLOCKED) and HTTP status reference.
  • Pagination: cursor paging to keep page sizes down.
  • Build a Production Integration: retry, backoff, and resilience patterns for unattended clients.