invalidapikey
The request carried an API key, but the API does not recognize it. The key may be mistyped, cut short, padded with a hidden character, or from another account. Retrying sends the same key and gets the same answer. This page is part of the error code reference.
| HTTP status | 401 Unauthorized |
error.type |
authentication_error |
error.retryable |
false |
| Credits used | 0 |
Example response
{ "error": { "type": "authentication_error", "code": "invalid_api_key", "message": "API key not recognized", "retryable": false, "docs": "https://renderscreenshot.com/docs/errors/invalid-api-key" }, "request_id": "req_abc123" }
Current behaviour: the API does not return invalid_api_key today. An unknown, mistyped or malformed key returns missing_credentials with the message No valid API key or signature provided, and so does a revoked key. Everything on this page applies to that response too. Handle every 401 the same way, and your code keeps working when the specific code ships.
Common causes
- Hidden characters. A trailing newline or
\rfrom a secrets file, a Windows.envfile, orechowithout-n. The key looks right when printed but does not match. - A cut-off or wrapped key. Copied from a terminal or a chat message that broke the line.
- The wrong key type. API requests with a Bearer header take an
rs_live_key.rs_pub_andrs_secret_keys are for signed URLs. - A stale environment. The key was rotated, but a container, CI secret or serverless function still has the old value until it redeploys.
- The header format. It must be
Authorization: Bearer rs_live_...with exactly one space.Bearer Bearer ...or a key in quotes fails. See Bearer token authentication.
How to fix it
- Check the shape of the key in the same environment as the failing process (container, CI job, server), not on your laptop:
# Is the variable set, and does it have the documented shape? if [[ "$RS_API_KEY" =~ ^rs_live_[A-Za-z0-9]{32}$ ]]; then echo "key shape OK" else echo "key shape wrong: length ${#RS_API_KEY}, starts with '${RS_API_KEY:0:8}'" printf '%s' "$RS_API_KEY" | od -c | tail -n 3 # shows hidden \n, \r or spaces fi
- Call an endpoint that only needs a key and uses no credit, such as
GET /v1/presets:
curl -s -o /dev/null -w '%{http_code}\n' https://api.renderscreenshot.com/v1/presets -H "Authorization: Bearer $RS_API_KEY"
A 200 means the key works. A 401 means it does not.
- If the key is wrong or lost, create a new one in the dashboard under API Keys. The full key is shown only once, so store it straight into your secret manager. See API keys.
- Deploy the new key everywhere, then revoke the old one.
Fail fast at startup
A worker with a bad key fails every job, and a retry loop can hide that for hours. Check the key once at boot and stop the process if it is rejected. The script never prints the full key.
// key-preflight.mjs (Node 22+): run once at boot, before any worker starts. const API = process.env.RS_BASE_URL ?? 'https://api.renderscreenshot.com'; const key = (process.env.RS_API_KEY ?? '').trim(); if (!key) { console.error('RS_API_KEY is empty. Set it in the environment of this process.'); process.exit(1); } // GET /v1/presets needs a valid key and uses no credit. const res = await fetch(`${API}/v1/presets`, { headers: { Authorization: `Bearer ${key}` } }); if (res.status === 401) { const { error, request_id } = await res.json(); // Show only the prefix and the last 4 characters, never the full key. console.error(`Key ${key.slice(0, 8)}...${key.slice(-4)} rejected: ${error.code} (${request_id})`); process.exit(1); } if (!res.ok) throw new Error(`Preflight failed with HTTP ${res.status}`); console.log('API key OK');
The script trims whitespace. If you trim here, pass the same trimmed value to the rest of your app, or fix the secret at the source.
In code that uses the Node SDK, every 401 is a configuration error, so stop instead of retrying:
import { Client, TakeOptions, RenderScreenshotError } from 'renderscreenshot'; const client = new Client(process.env.RS_API_KEY); try { await client.take(TakeOptions.url('https://example.com')); } catch (error) { if (error instanceof RenderScreenshotError && error.httpStatus === 401) { // Every 401 is a configuration problem. Stop the worker and alert a person. console.error('RenderScreenshot rejected the API key. Check RS_API_KEY.'); process.exit(1); } throw error; }
Tested on 2026-10-15 with bash and zsh, Node 22.18 and renderscreenshot 1.1.0 for Node, against a local mock. The shape check flagged a key with a trailing \r (length 41); the preflight passed a good key, rejected an unknown key with exit code 1, and stopped on an empty variable. On 2026-10-18 the preflight endpoint changed from GET /v1/usage (not live at the time) to GET /v1/presets; that version was checked against the API source, not run.
Related
- API keys for key types and rotation.
- Error handling and retries for why 4xx errors other than 422 and 429 are never retried.