A screenshot of a page every morning gives you a record nobody else keeps: how your home page looked on launch day, when a competitor changed its pricing, whether the status dashboard was green at 9:00, what an ad landing page showed during a campaign. Taking it by hand stops after the third day. This page shows three ways to take it automatically without running a browser yourself — a GitHub Actions workflow, a Zapier or n8n schedule, or plain cron — with our screenshot API doing the capture, and the details that decide whether it works: caching, storage and timing.
The capture itself
Every scheduler below makes the same request:
curl -s -X POST "https://shareglint.com/v1/screenshots?wait=true" \
-H "Authorization: Bearer $SCREENSHOT_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "url": "https://example.com/pricing", "full_page": true, "lazy_load": true,
"block_cookie_banners": true, "format": "jpeg" }'
The JSON response has a url with the finished image. ?wait=true holds the connection until the screenshot is ready; without it, the call returns at once with an id to poll (GET /v1/screenshots/{id}), or add a webhook_url and we call you with screenshot.completed or screenshot.failed. A viewport screenshot uses 1 render of your monthly quota, a full page 2.
Two parameters matter most for a schedule: full_page with lazy_load, so images further down the page are loaded before the capture, and block_cookie_banners, so a consent dialog does not cover the page in every image of your archive.
Option 1: GitHub Actions (no server of your own)
# .github/workflows/screenshots.yml
name: Daily screenshots
on:
schedule:
- cron: "17 6 * * *" # 06:17 UTC every day
workflow_dispatch: # a "Run workflow" button for testing
jobs:
capture:
runs-on: ubuntu-latest
steps:
- name: Capture the pricing page
env:
SCREENSHOT_API_KEY: ${{ secrets.SCREENSHOT_API_KEY }}
run: |
page="https://example.com/pricing?t=$(date -u +%F)" # a new URL each day: never a cached image
url=$(curl -sf -X POST "https://shareglint.com/v1/screenshots?wait=true" \
-H "Authorization: Bearer $SCREENSHOT_API_KEY" -H "Content-Type: application/json" \
-d "{\"url\":\"$page\",\"full_page\":true,\"lazy_load\":true,\"block_cookie_banners\":true,\"format\":\"jpeg\"}" \
| jq -r .url)
mkdir -p shots && curl -sf "$url" -o "shots/pricing-$(date -u +%F).jpg"
- uses: actions/upload-artifact@v7
with:
name: screenshots
path: shots/
Store the API key as a repository secret, never in the file. What GitHub's schedule documentation says about the timing, as of 2026-11-08:
- Schedules use POSIX cron syntax and run in UTC by default; a
timezone:key with an IANA name such as"Europe/Berlin"sets another. - The shortest interval is once every 5 minutes.
- Runs "can be delayed during periods of high loads", and the start of every hour is one of them — which is why the example runs at :17, not :00.
- In a public repository, scheduled workflows are disabled after 60 days without repository activity.
Artifacts made with actions/upload-artifact expire after a retention period too; for a permanent archive, commit the images to a branch, or upload them to S3 or another store in an extra step.
Option 2: Zapier, Make or n8n
All three have a schedule trigger and an HTTP step, so the flow is the same:
- Schedule — every day at a time, every hour, or every Monday.
- HTTP request — the POST above, with the API key in the header.
- Save the file — the image URL into Google Drive, Dropbox, an Airtable attachment field or a Slack message.
Our recipes show the HTTP step for each tool: Zapier, Make and n8n. We have no app inside these tools; the HTTP step is the integration. A no-code schedule suits a handful of pages; for dozens, a script is cheaper to run and easier to change.
Option 3: cron on a server you already have
The curl command above, in a shell script, in crontab -e:
17 6 * * * /opt/shots/capture.sh >> /var/log/shots.log 2>&1
Nothing else is needed — no Chrome on the server, no fonts, no memory for a browser.
Caching: when a schedule returns yesterday's picture
The same URL with the same options within 24 hours returns the existing file without a new capture and without using quota. That saves money on retries and repeated requests, and it means:
- Less often than every 24 hours: usually outside the window, so a fresh capture — but if yesterday's run was delayed and today's is on time, the two can be less than 24 hours apart, and today's run gets yesterday's image.
- More often, or to be safe: add a query parameter that changes per run, such as
?t=2026-11-08(or…T14for hourly), so each run is a new URL and always a new capture. Most sites ignore unknown parameters; check that yours does not show a different page for them.
We checked this on 2026-11-08: a full-page capture of a test URL took about 5 seconds and used 2 renders; sending the identical request again returned the same image id at once and used nothing more.
The API also accepts an Idempotency-Key header: a retried request with the same key and body returns the first response instead of capturing again, which protects you from double captures when a scheduler retries a step.
Storage and naming
Keep your own copy of every image: ours are kept for 365 days on paid plans, an archive is usually meant for longer. Name files by page and UTC date — pricing-2026-11-08.jpg — so they sort in time order, and keep the capture options in the job's code, so next year's images are comparable with this year's. JPEG keeps full-page archives small; PNG is better if you will compare images pixel by pixel.
Knowing when something changed
A schedule gives you images; noticing a change means comparing today's with yesterday's. An image diff such as pixelmatch does that in a few lines, and our visual regression guide measured what makes such comparisons flaky. We do not send change alerts ourselves: the comparison and the notification are your job's last steps. To capture many pages in one run, see bulk website screenshots.