To purge screenshot cache entries, call POST /v1/cache/purge with the exact keys you saved from X-Cache-Key, a url pattern such as https://example.com/blog/*, or a before timestamp. Then render again with cache.refresh: true if you still need the image. This guide explains what is cached, how each filter matches, the one rule about combining filters that surprises people, and a deploy hook that purges and re-renders changed pages. It is part of the guides; the endpoint reference is cache management.
Tested on October 15, 2026, with Node.js 22.18.0 and curl: the deploy hook and the purge-by-key flow against a local mock of the RenderScreenshot API; SDK purge calls in Node.js, Python, Ruby, PHP and Go against the same mock. Filter semantics described here were checked against the API source, not only the mock.
What is cached
Every successful render is stored and served from the CDN link in X-Cache-URL, with no authentication, until its TTL ends. The default TTL is 24 hours; cache.ttl accepts 3,600 seconds (1 hour) to 2,592,000 (30 days). The response also carries X-Cache-Key, the handle you use to purge that entry.
Separately, a request with cache.available: true asks the API to return an earlier identical render from your account instead of rendering again, for 0 credits. See caching.
So purging is about two things: links you want to stop working (X-Cache-URL), and earlier renders you no longer want reused.
Pick the right tool
| Goal | Use |
|---|---|
| A page changed and its card must update | cache.refresh: true on the next request; purge only if the old image must disappear |
| Remove one known image | keys in a purge, with the value from X-Cache-Key |
| Remove everything for a section of a site | Purge with url: "https://example.com/blog/*" |
| Clean up old renders | Purge with before |
| Stop social networks from showing an old card | Change the image URL; purging cannot reach their copies |
Requests
Keep the key when you render. It is in the X-Cache-Key header (or cache.key with Accept: application/json):
# 1. Render once and keep the cache key (store it next to the record that owns the image) KEY=$(curl -s -D - -o card.png -X POST "$API/v1/screenshot" \ -H "Authorization: Bearer $RS_API_KEY" -H "Content-Type: application/json" \ -d '{"url":"https://example.com/","preset":"og_card"}' \ | awk -F': ' 'tolower($1)=="x-cache-key"{print $2}' | tr -d '\r') echo "key: $KEY" # 2. Later: remove that entry. The key goes in the JSON body, so slashes need no escaping. curl -s -X POST "$API/v1/cache/purge" \ -H "Authorization: Bearer $RS_API_KEY" -H "Content-Type: application/json" \ -d "{\"keys\":[\"$KEY\"]}"; echo
Against the mock this printed the key, then {"purged":1}. Cache keys look like a storage path, with slashes and a file extension (for example screenshots/ab/cd/<hash>_<time>.png). Send them in the purge body as shown; that is the reliable way to remove one entry.
The cache management reference also lists DELETE /v1/cache/:key, which takes the key in the URL path. A key with slashes and a file extension does not travel well in a path, so for keys copied from X-Cache-Key, use the purge body.
Purge by URL pattern:
curl -X POST https://api.renderscreenshot.com/v1/cache/purge \ -H "Authorization: Bearer $RS_API_KEY" -H "Content-Type: application/json" \ -d '{ "url": "https://example.com/blog/*" }'
Purge by date:
curl -X POST https://api.renderscreenshot.com/v1/cache/purge \ -H "Authorization: Bearer $RS_API_KEY" -H "Content-Type: application/json" \ -d '{ "before": "2026-10-01T00:00:00Z" }'
A purge returns {"purged": 12, ...} with the filters you sent, or {"purged": 0, "message": "No matching cache entries found"}.
How each filter matches
keys: exact cache keys, as returned inX-Cache-Key.url: a pattern matched against the URL you asked us to capture, across your account's successful renders. Use*as the wildcard. The match is on the URL string you sent, sohttps://example.com/blogandhttps://example.com/blog/are different; end the pattern with*to cover both, and remember query strings are part of the URL.before: an ISO 8601 timestamp. Every successful render on your account created before it is purged. An unparseable value returns400withinvalid_date.pattern: matches storage paths rather than page URLs. For images cached by the API, theurlandbeforefilters are the practical ones.
Filters add up. An entry is purged if it matches any filter in the request. {"url": "https://example.com/blog/*", "before": "2026-10-01T00:00:00Z"} purges every blog image and every image of any page rendered before October 1. If you want "blog images older than October 1", there is no single request for it; keep the keys of those renders and purge by keys.
After a purge: render again with refresh
Purging does not render anything. The next request for that page renders as normal. If your code uses cache.available to reuse earlier renders, send cache.refresh: true on the first request after a purge. Refresh skips the lookup, renders a new image and replaces what is stored, so later lookups find the fresh render.
This deploy hook does both steps for a list of changed pages:
// purge-on-deploy.mjs: after a deploy, drop cached screenshots for changed pages and render them again. // Node 22+. Usage: RS_API_KEY=rs_live_... node purge-on-deploy.mjs https://example.com/blog/a https://example.com/pricing const API = process.env.RS_API_BASE ?? 'https://api.renderscreenshot.com'; const headers = { Authorization: `Bearer ${process.env.RS_API_KEY}`, 'Content-Type': 'application/json' }; async function post(path, body) { const res = await fetch(`${API}${path}`, { method: 'POST', headers, body: JSON.stringify(body) }); if (!res.ok) throw new Error(`${path} ${res.status}: ${await res.text()}`); return res; } for (const page of process.argv.slice(2)) { const { purged } = await (await post('/v1/cache/purge', { url: page })).json(); // Render again with refresh, so the next cache.available lookup gets this fresh image. const res = await post('/v1/screenshot', { url: page, preset: 'og_card', cache: { refresh: true } }); await res.arrayBuffer(); console.log(`${page}: purged ${purged}, new image ${res.headers.get('x-cache-url')}`); }
Run it from CI after a deploy with the URLs of the pages that changed. Each page costs one credit for the re-render; the purge itself is free.
Social cards: change the URL instead
When an og:image must change, the copy that matters is often not ours: LinkedIn, Slack, X and messaging apps fetch the image once and keep it. Purging our cache does not touch their copies. Change the image URL instead, for example by adding a version of the page content to the URL you capture:
{ "url": "https://example.com/blog/launch?v=2026-10-15", "preset": "og_card" }
A different URL is a different cache key, so this renders a new image, and the old entry expires on its own. Platforms then fetch the new image the next time they scrape the page.
SDK examples
Purge helpers in each official SDK. Each returns the purge result with the purged count.
Node.js
import { Client } from 'renderscreenshot'; const client = new Client(process.env.RS_API_KEY); await client.cache.purge([savedKey]); // by key await client.cache.purgeUrl('https://example.com/blog/*'); // by URL pattern await client.cache.purgeBefore(new Date('2026-10-01T00:00:00Z'));
Python
client.cache.purge([saved_key]) client.cache.purge_url('https://example.com/blog/*') client.cache.purge_before(datetime(2026, 10, 1, tzinfo=timezone.utc))
Ruby
client.cache.purge([saved_key]) client.cache.purge_url('https://example.com/blog/*') client.cache.purge_before(Time.utc(2026, 10, 1))
PHP
$client->cache->purge([$savedKey]); $client->cache->purgeUrl('https://example.com/blog/*'); $client->cache->purgeBefore(new DateTime('2026-10-01T00:00:00Z'));
Java
client.cache.purge(Arrays.asList(savedKey)); client.cache.purgeUrl("https://example.com/blog/*"); client.cache.purgeBefore(Instant.parse("2026-10-01T00:00:00Z"));
Go
cache := client.Cache() _, err = cache.Purge(ctx, []string{savedKey}) _, err = cache.PurgeURL(ctx, "https://example.com/blog/*") _, err = cache.PurgeBefore(ctx, time.Date(2026, 10, 1, 0, 0, 0, 0, time.UTC))
The Java example was not run; the rest ran against the mock API.
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| Purge removed far more than expected | Two filters in one request; they add up | One filter per request |
purged: 0 for a page you rendered |
Pattern does not match the exact URL string (slash, query, http vs https) |
End the pattern with *; check the URL in your request logs |
404 on DELETE for a key you just received |
The key's slashes and extension do not survive in a URL path, or it was already purged | Purge it with keys in the body |
400 invalid_date |
before is not ISO 8601 |
Send 2026-10-01T00:00:00Z |
| Social card still shows the old image | The platform's own cache | Change the image URL; re-scrape with the platform's debugger |
Old image still reused with cache.available |
No fresh render since the purge | Send cache.refresh: true once |
| Private screenshot reachable by link | X-Cache-URL is public until TTL ends |
Short cache.ttl and a purge after download; see authenticated pages |
Related
- Cache management endpoints: request and response fields.
- Caching: TTL,
cache.availableandX-Cache-URL. - Bulk screenshots: re-render many pages after a purge.
- Try purging with a free API key.