API Authentication Issues
What each VULK API error means — 401, 402, 403, 404, 413, 429 — and what to do about it.
API Authentication Issues
Every error from https://app.vulk.dev/api/v1 is a JSON object with the same three fields:
{ "error": "Invalid, expired or revoked API key.", "code": "UNAUTHORIZED", "status": 401 }error is a sentence for the developer reading a log; code is the value to branch on.
401 UNAUTHORIZED
The answer carries WWW-Authenticate: Bearer realm="VULK API". Three sentences are possible:
- "Missing API key. Send it as "Authorization: Bearer vk_sk_…". Keys are created in Settings › API keys." — the request had no
Authorizationheader. Add it:curl https://app.vulk.dev/api/v1/projects \ -H "Authorization: Bearer vk_sk_your_key_here" - "Invalid API key." — the header is there but the value is not shaped like a VULK key (
vk_sk_followed by the key body). Check for a missingBearer, a truncated paste, quotes or a trailing newline in the environment variable. - "Invalid, expired or revoked API key." — the key is well-formed but VULK does not know it any more. Keys are shown once, at creation: if you lost it, revoke it in Settings › API keys and create a new one.
Only the index, GET /api/v1, answers without a key.
402 PAYMENT_REQUIRED
Your plan or credit balance does not cover the request. Reads never cost credits; this comes from POST /projects, POST /projects/:id/messages, or from reading a project's files on a plan without source export. The sentence names the reason and links to Settings › Billing.
403 FORBIDDEN
"This API key was not granted the projects.write scope. Create a key with it in Settings › API keys." — the key exists but was created without the scope the endpoint needs. Scopes are fixed at creation: create a new key with the right ones, then revoke the old one. The scope each endpoint needs is listed in the endpoint reference.
404 NOT_FOUND
Either the path is not an endpoint (GET /api/v1 lists them), or the project or operation does not belong to the account the key is on. The API never confirms that a project exists on another account.
405 METHOD_NOT_ALLOWED
The path exists but not with that verb. The Allow header lists what the path accepts.
413 PAYLOAD_TOO_LARGE
Request bodies are limited to 64 KB. A brief is at most 8,000 characters.
429 RATE_LIMITED
{ "error": "Rate limit exceeded", "code": "RATE_LIMITED", "status": 429, "retryAfter": 23 }Each key may make 60 requests per minute. Wait retryAfter seconds (also sent as the Retry-After header) and retry. To see where you stand before that happens, read the counters every authenticated answer carries:
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 42
X-RateLimit-Reset: 1760360400X-RateLimit-Reset is Unix time in seconds. Spread work across keys if one integration needs more: an account may hold up to 20 keys.
Keys from the previous workspace
A key created at vulk.dev (the previous VULK workspace) works on app.vulk.dev/api/v1 with its permissions mapped to the new scopes — see API keys. If such a key answers 401, it was revoked, either in the previous workspace or in Settings › API keys, where it is listed as a legacy key.
Browsers and CORS
The API answers requests from any origin, so a browser-based tool can call it. Do not ship a key inside browser code, though: anyone who loads the page can read it. Keep keys on a server, in a CLI, or in an MCP client's configuration.
Good habits
- One key per integration or environment (development, staging, production), named after it, so the list in Settings tells you what each one is for.
- Only the scopes the integration needs; most read-only tools do not need
projects.write. - Keys in environment variables or a secret store, never in version control.
- Revoke a key the moment it may have leaked; revocation is immediate.