The API works the same in production as on your laptop. What changes is the traffic: more requests, slow pages, and nobody watching. Work through these six steps before launch.
Use one key per environment
Create one API key for each place that calls the API: Production server, Staging, Nightly job. Store each key in that environment's secret store, never in the repository.
To rotate a key without downtime:
- Create the new key.
- Deploy it to the environment.
- Check in API Keys that Last used of the old key stops changing.
- Click Revoke on the old key.
All keys share the account's credits and rate limit. Keys for the browser are different: use signed URLs there, never an rs_live_ key.
Set your client timeout above the page timeout
The API waits up to wait.timeout for a page (30 seconds by default, 60 at most), and a render takes a few more seconds. If your HTTP client gives up first, you get an error while the render finishes on the server and uses a credit. Set the client timeout to about 90 seconds:
Node.js
const client = new Client(process.env.RS_API_KEY, { timeout: 90_000 }); // milliseconds
Python
client = Client(os.environ['RS_API_KEY'], timeout=90.0) # seconds
curl
curl --max-time 90 ...
Retry only the errors that can succeed
| HTTP | Examples | Retry? |
|---|---|---|
| 400, 401, 403, 404 | invalid_url, missing_credentials, insufficient_credits |
Never. Fix the request or alert a person. |
| 422 | timeout, navigation_failed |
Once, after a short wait |
| 422 | blocked, ssl_error, content_check_failed |
No |
| 429 | too_many_requests, concurrent_limit |
Yes, after Retry-After seconds |
| 500, no response | internal_error, network error |
Yes, with exponential backoff |
Failed requests use no credits, so retries cost time, not money. A complete retry helper:
Node.js
import { Client, TakeOptions, RenderScreenshotError } from 'renderscreenshot'; const client = new Client(process.env.RS_API_KEY, { timeout: 90_000 }); const shouldRetry = (status) => !status || status === 408 || status === 422 || status === 429 || status >= 500; export async function takeWithRetry(options, maxAttempts = 3) { for (let attempt = 1; ; attempt++) { try { return await client.take(options); } catch (error) { if (!(error instanceof RenderScreenshotError)) throw error; if (!shouldRetry(error.httpStatus) || attempt >= maxAttempts) throw error; const wait = error.retryAfter ? error.retryAfter * 1000 : Math.random() * 1000 * 2 ** (attempt - 1); await new Promise((resolve) => setTimeout(resolve, wait)); } } }
Python
import os import random import time from renderscreenshot import Client, RenderScreenshotError client = Client(os.environ['RS_API_KEY'], timeout=90.0) def should_retry(status): return not status or status in (408, 422, 429) or status >= 500 def take_with_retry(options, max_attempts=3): for attempt in range(1, max_attempts + 1): try: return client.take(options) except RenderScreenshotError as e: if not should_retry(e.http_status) or attempt == max_attempts: raise time.sleep(e.retry_after or random.uniform(0, 2 ** (attempt - 1)))
For blocked and content_check_failed, check error.code before you retry: their status is 422, but a retry gets the same result.
Stay under the rate limit
The rate limit counts requests per minute for the whole account:
| Plan | Requests per minute |
|---|---|
| Free | 10 |
| Starter | 60 |
| Growth | 120 |
| Scale | 200 |
Every response has X-RateLimit-Remaining. A 429 has Retry-After, the seconds until the window resets. Keep the number of parallel workers small: a render takes 2 to 5 seconds, so 4 workers already send about 60 requests a minute. More workers only produce more 429 errors.
Alert before the credits run out
At 0 credits, every request fails with 403 insufficient_credits until the reset or an upgrade. Check the balance once an hour. The call is free:
curl -sS https://api.renderscreenshot.com/v1/usage -H "Authorization: Bearer $RS_API_KEY"
{ "plan": { "id": "starter", "name": "Starter", "credits": 2500 }, "credits": 2500, "used": 2112, "remaining": 388, "period_end": "2026-11-05T08:00:00Z", "rate_limit": { "limit": 60, "remaining": 59, "reset": 1792644660, "window": 60 } }
Alert when remaining is below what you use in two or three days. Watch credits and change plans shows the same numbers in the dashboard.
Log the request ID
Every response has a request ID: the X-Request-Id header, the id field in JSON responses, and request_id in errors. Log it with your own job ID. In Logs, each request shows its ID, options and result:
When you contact support, send the request ID. It finds the exact request in seconds.