Scheduled captures give you a dated picture of a page: your own pages after each release, a competitor's pricing page, a partner's listing, or a page you must keep for compliance. You will write one short script and run it every day.
List the pages and count the credits
Each capture uses one credit. Multiply before you start:
| Pages | Runs per day | Credits per month | Plan that covers it |
|---|---|---|---|
| 5 | 1 | 150 | Free (150 a month) |
| 20 | 1 | 600 | Starter (2,500) |
| 40 | 2 | 2,400 | Starter (2,500) |
| 100 | 4 | 12,000 | Growth (12,000) |
Put the URLs in a file, one per line:
# pages.txt https://renderscreenshot.com/pricing https://renderscreenshot.com/changelog https://example.com
Write the capture script
The script asks for the whole page (full_page preset), closes cookie banners, and saves each image as captures/<date>/<host>-<path>.png. Failed pages do not stop the run.
Bash and curl
#!/usr/bin/env bash # capture.sh: one full-page capture per URL in pages.txt set -uo pipefail day=$(date -u +%F) mkdir -p "captures/$day" grep -v '^#' pages.txt | while read -r url; do [ -z "$url" ] && continue name=$(echo "$url" | sed -E 's#^https?://##; s#[^a-zA-Z0-9]+#-#g; s#-$##') status=$(curl -sS -X POST https://api.renderscreenshot.com/v1/screenshot \ -H "Authorization: Bearer $RS_API_KEY" \ -H "Content-Type: application/json" \ -d "{\"url\": \"$url\", \"preset\": \"full_page\", \"block\": {\"cookie_banners\": true}}" \ --max-time 120 -o "captures/$day/$name.png" -w '%{http_code}') echo "$status $url" [ "$status" = "200" ] || rm -f "captures/$day/$name.png" done
Node.js
// capture.mjs: node capture.mjs import { mkdir, readFile, writeFile } from 'node:fs/promises'; import { Client, TakeOptions } from 'renderscreenshot'; const client = new Client(process.env.RS_API_KEY, { timeout: 120_000 }); const day = new Date().toISOString().slice(0, 10); await mkdir(`captures/${day}`, { recursive: true }); const urls = (await readFile('pages.txt', 'utf8')) .split('\n').map((line) => line.trim()).filter((line) => line && !line.startsWith('#')); for (const url of urls) { const name = url.replace(/^https?:\/\//, '').replace(/[^a-z0-9]+/gi, '-').replace(/-$/, ''); try { const image = await client.take(TakeOptions.url(url).preset('full_page').blockCookieBanners()); await writeFile(`captures/${day}/${name}.png`, image); console.log('ok', url); } catch (error) { console.error('failed', url, error.code ?? error.message); } }
The full_page preset scrolls through the page first, so images that load on scroll appear in the capture.
Run it every day
cron
# crontab -e: every day at 06:00 UTC 0 6 * * * cd /srv/captures && RS_API_KEY=rs_live_... ./capture.sh >> capture.log 2>&1
GitHub Actions
# .github/workflows/captures.yml name: Daily captures on: schedule: - cron: '0 6 * * *' workflow_dispatch: jobs: capture: runs-on: ubuntu-latest timeout-minutes: 20 steps: - uses: actions/checkout@v4 - name: Capture pages env: RS_API_KEY: ${{ secrets.RS_API_KEY }} run: bash capture.sh - uses: actions/upload-artifact@v4 with: name: captures-${{ github.run_id }} path: captures/ retention-days: 90
Add RS_API_KEY under Settings → Secrets and variables → Actions in the repository. GitHub keeps the files for 90 days. To keep them longer, save screenshots to your bucket with a dated path such as {year}/{month}/{day}/{domain}-{hash}.{ext}.
Check the runs
Usage in the dashboard shows the requests per day. A daily job is a flat line. A gap means a run did not happen.
Logs shows each capture. A failed capture shows the reason and costs no credits. The most common reason for a scheduled job is a timeout on a slow page; fix blank or incomplete screenshots shows the fix.
Next steps
- Remove cookie banners and ads from the captures.
- Capture many pages at once when the list grows past a few dozen URLs.
- Watch your credits so the job never stops at the end of the month.