Screenshots on the RenderScreenshot CDN are kept for the cache time, at most 30 days. To keep them for good, or to keep them in your own cloud account, connect a bucket. After that, every screenshot is copied to it, with no change to your API requests.
Custom storage is part of the Starter, Growth and Scale plans.
Create a bucket and an access key
Any S3-compatible storage works. In your provider's console, create a bucket and an access key that can read and write objects in it. Then note the endpoint:
| Provider | Endpoint | Region |
|---|---|---|
| Amazon S3 | Leave empty | The bucket's region, for example us-east-1 |
| Cloudflare R2 | https://<account_id>.r2.cloudflarestorage.com |
auto or empty |
| DigitalOcean Spaces | https://<region>.digitaloceanspaces.com |
The Spaces region |
| Backblaze B2 | https://s3.<region>.backblazeb2.com |
The B2 region |
| Wasabi | https://s3.<region>.wasabisys.com |
The Wasabi region |
| MinIO | Your server URL | Your region setting |
For Cloudflare R2: open R2 Object Storage, click Create bucket, then Manage R2 API Tokens → Create API token with Object Read & Write on that bucket. Copy the access key ID and the secret. The storage reference has the steps for every provider.
Give the key access to this one bucket only. RenderScreenshot needs to write objects and to check that the bucket exists, nothing more.
Connect the bucket
Open Storage in the dashboard. Enter the bucket name, the endpoint, the region, the access key ID and the secret, then click Save Configuration.
The secret is stored encrypted and is never shown again.
Test the connection
Click Test Connection. The status changes to connected, or shows the error from your provider:
| Error | Fix |
|---|---|
Forbidden |
The key is wrong, or it cannot list the bucket. On AWS, add s3:ListBucket on the bucket. |
Bucket not found |
Check the name (case-sensitive) and, for R2, the account ID in the endpoint. |
Http301Error |
The AWS region is not the bucket's region. |
Failed to open TCP connection |
The endpoint URL is wrong. Include https://. |
Choose where files go
The path template decides the file name in your bucket. It must contain {hash} and {ext}:
| Template | Example file |
|---|---|
{hash}.{ext} (default) |
a1b2c3d4.png |
{year}/{month}/{domain}-{hash}.{ext} |
2026/10/example.com-a1b2c3d4.png |
captures/{year}/{month}/{day}/{uuid}.{ext} |
captures/2026/10/25/550e8400-....png |
Variables: {hash}, {ext}, {year}, {month}, {day}, {timestamp}, {domain}, {uuid}.
Set the account default on the Storage page. To change it for one request, or to skip the upload, use storage in the request:
{ "url": "https://example.com/pricing", "preset": "full_page", "storage": { "path": "pricing/{year}-{month}-{day}.{ext}", "acl": "private" } }
| Field | Values |
|---|---|
storage.enabled |
false skips the upload for this request |
storage.path |
A path template for this request |
storage.acl |
private (default) or public-read |
Take a screenshot and find the file
Send any request. You get the image back at once, as before. A background job then copies it to your bucket, usually within seconds. If the provider has a temporary problem, the job tries again, up to 5 times. A permanent error, such as a deleted bucket, is recorded and not retried.
Open your bucket and look for the path from step 4. With the template {year}/{month}/{domain}-{hash}.{ext}, a screenshot of example.com taken in October 2026 is at 2026/10/example.com-<hash>.png.
When to use the CDN link instead
| You need | Use |
|---|---|
| An image for a few days, for example a preview | The free CDN link (X-Cache-URL) |
| A permanent archive, or files in your own account | Custom storage |
| Private screenshots that only your app can read | Custom storage with acl: private |
Next steps
- Capture pages on a schedule into dated folders.
- Capture many pages at once.
- Watch your credits and change plans.