GRANSKA

Read the quota

GEThttps://api.granska.cloud/v1/quotas

Reads this tenant's run limit, what it has spent this period, when the period resets, and how many workers one analysis may target.

Bearer tokenSpends no quota

Reading the counter without touching it

limits is what your tenant is allowed per period; usage is where it currently stands. resetAt is a Unix timestamp in seconds — the instant the period turns over — and periodKey names the current period, so a client can tell "the counter went up" from "the period rolled".

This call spends nothing. Poll it as often as you find useful.

Only POST /v1/analyze and POST /v1/action spend runs. Everything else in the API is free, including this endpoint, listing sources and reading a finished job.

How many reviewers one analysis may target

limits.maxWorkersPerProfile is how many reviewers one of your profiles may hold, and so how many dynamicContext.workerContext may name on POST /v1/analyze. It differs between organisations, which is why the analyze reference states no number for it. It is always answered, and always as the number a request is held to: an organisation with no ceiling of its own reads the default here rather than nothing. Name one reviewer more and the analysis is refused with a 400 before any job exists.

The four configuration limits

profiles is how many profiles your organisation holds and how many it may hold. Creating one past the ceiling is a 409; editing the ones you have is never affected, so an organisation that is at or above its ceiling keeps working on everything it already built.

snippets is the same pair for the legal sources your organisation authored — the ones you wrote, never the ones you inherit from us, which are uncounted and unlimited. It behaves exactly like profiles: past the ceiling, POST /v1/snippets is a 409, and editing what you already hold is never refused. Sources are deleted in the admin panel, so this is the number to watch before a bulk import.

actions is the same pair for the actions your organisation owns — again never the ones you inherit. Past the ceiling, POST /v1/actions is a 409, and PATCH /v1/actions/:id on what you already hold is never refused for it. Actions are deleted in the admin panel.

configWrites is a floor against runaway clients, not a plan allowance — do not design against it and do not price against it. These calls tick it, per hour: POST /v1/profiles, PATCH /v1/profiles/:id, DELETE /v1/profiles/:id, POST /v1/snippets, PATCH /v1/snippets/:id, POST /v1/actions, PATCH /v1/actions/:id, and GET /v1/laws/:jurisdiction/:work — that last one only when it has to fetch a law the library did not already hold, never when it answers from the library. The floor is set where no human and no scheduled integration reaches it, and it spends no runs: reorganising your profiles never costs you an analysis you paid for. If you have a legitimate reason to write more, tell us and we raise the number for your organisation.

Request
curl https://api.granska.cloud/v1/quotas \
  -H "Authorization: Bearer $TOKEN"
Response
{
  "limits": {
    "maxRunsPerPeriod": 500,
    "periodType": "MONTH",
    "maxWorkersPerProfile": 3
  },
  "usage": {
    "usedRuns": 13,
    "remainingRuns": 487,
    "resetAt": 1786838400,
    "periodKey": "MONTH_2026-8"
  },
  "profiles": {
    "stored": 6,
    "max": 50
  },
  "snippets": {
    "stored": 34,
    "max": 400
  },
  "actions": {
    "stored": 12,
    "max": 200
  },
  "configWrites": {
    "used": 2,
    "max": 60,
    "remaining": 58,
    "resetAt": 1786838400
  }
}

The rate-limit headers are not here

This is the endpoint integrators most expect to find X-RateLimit-Limit and its two siblings on, and it is the one endpoint that does not set them. They appear on the calls that are metered: POST /v1/analyze and POST /v1/action, which spend runs; POST /v1/profiles, PATCH /v1/profiles/:id, DELETE /v1/profiles/:id, POST /v1/snippets, PATCH /v1/snippets/:id, POST /v1/actions and PATCH /v1/actions/:id, which tick the hourly write floor; and GET /v1/laws/:jurisdiction/:work, which ticks that same floor when it has to fetch a law the library did not hold. Nowhere else.

Read the limits from this body, and read the headers off those seven. On a write route the headers describe the write floor, not your run quota: the two counters never touch.

A rejected request on one of the first six has already been charged, so the headers on a 400 are telling you the truth about what it cost. The law route is the exception in both directions — it reports the floor even when the call is free, and a request it refuses on its way in carries no headers at all. See Errors.

Errors

ErrorWhen
401
UNAUTHORIZED
The Authorization header is missing, is not a readable bearer token, or names no tenant.
401
TOKEN_EXPIRED
The access token was issued by this gateway and has since expired. Not probed: it needs a token older than its own lifetime.
500
INTERNAL_ERROR
An unexpected server-side failure. Not probable from outside — reaching it means something is wrong.

Send it without writing a client

If your organisation already has an account, an administrator can read the current quota from the API tester at /admin/api-tester.