Authentication
Every request carries a personal API key in a header. The key belongs to a person inside one organisation, and it can never reach more than that person can.
The header
Send the key as x-api-key. There is no Authorization: Bearer form, and there is no query parameter — v1 had one and v2 removed it, because a credential in a query string ends up in access logs, browser history and Referer headers. A request that still sends ?api-key= gets a 401 naming the change.
curl https://api.ragextract.com/v2/verify \
-H "x-api-key: psk_live_7Kq2mB4nR8vXpL3d"Getting a key
Personal keys are minted in account settings → API keys in the Ragextract app. The key is shown once, at creation; only its hash is stored, so a lost key is replaced rather than recovered. Keys start psk_.
Account settings is not reachable at the moment — sign-in to app.ragextract.com is temporarily blocked, so a new key cannot currently be created. The API itself is live and everything on these pages is accurate; the door to the credential is what is shut. This paragraph comes down when it opens.
A second, older credential family exists: workspace keys (sk_), minted in a workspace's Access tab, which are what the frozen v1 API takes. They also authenticate v2, but only for the one workspace they are bound to and always at read & write — naming any other workspace id returns 404, and scopes do not apply to them at all. Build against a personal key; the prefix is what the server branches on, and /v2/verify will tell you which kind you are holding.
What a key reaches
Two things decide it, and the order matters. First, the live permission its owner has in each workspace — read, read & write, or manage — which is whatever sharing currently grants them. Second, the key's own scopes. The effective level is the lower of the two.
- A key with no scopes inherits everything its owner can reach. That is the default for a newly minted key.
- Adding any scope turns the key into an allow-list. Granting a key access to one workspace removes its access to every other one. This surprises people the first time; it is the point.
- A scope can only ever subtract. Minting a key is never a privilege escalation, and revoking someone's access to a workspace narrows every key they hold without anyone touching the keys.
- A table scope adds to a workspace scope rather than replacing it, and does not open the workspace — a table-scoped key can read that table and cannot list the workspace's files.
Sharing a workspace covers the levels themselves.
Revocation takes up to a minute
Identity, levels and scopes are cached for 60 seconds. Deleting a key, changing a scope, or removing someone's workspace access all take effect within that window rather than instantly. Sixty seconds is the shortest cache the platform allows, not a tuned figure. Two checks are deliberately not cached and do apply immediately: an expired key, and an archived workspace.
Rate limits
Limits are per key, by request method:
- 100 requests/minute for
GET. - 15 requests/minute for
POST,PATCH,PUTandDELETE. - 300 requests/minute for upload session parts, which have their own budget so that one large multipart upload cannot exhaust the write limit mid-file.
Over the limit is 429. Counting happens at the edge location serving you rather than globally, so treat these as abuse friction rather than an exact quota — the real spending control is credits. Note there is no header telling you how much of the budget is left; back off on a 429 and retry.
Keep the key server-side
The v2 API answers cross-origin requests from anywhere, which makes it easy to call a Ragextract endpoint from a browser. Do not. A psk_ key is a person's credential across every workspace they can reach — the prefix is deliberately not pk_, which in the wider ecosystem means "publishable". Anything in a page's JavaScript is readable by anyone who opens that page.
Confirming a key
Verify a key
GET/v2/verify
Requires None — no API key
Answers what this key is and what it may do, without provoking a series of 403s to find out. It says nothing secret: the organisation and the key kind are things the holder already knows, and the point is to confirm them.
kind—personalfor apsk_key.unscoped—truewhen the key carries no scope rows, and so inherits everything its owner can reach.scopes— the allow-list, when there is one.levelis 1 read, 2 read & write, 3 manage.
A key that has been deleted or has expired gets 401 here, which makes this the endpoint to health-check against.
curl https://api.ragextract.com/v2/verify \
-H "x-api-key: psk_live_7Kq2mB4nR8vXpL3d"{
"success": true,
"total": 1,
"data": {
"kind": "personal",
"orgId": "org_4pN8dW1zJ6hT2yUx",
"unscoped": false,
"scopes": [
{ "type": "workspace", "id": "wks_7Kq2mB4nR8vXpL3d", "level": 2 },
{ "type": "table", "id": "rev_Qm5xC9bV3nK7sAeR", "level": 1 }
]
}
}