To take automated full page screenshots of websites, send the target URL to a screenshot API that runs a headless browser, scrolls the page to trigger lazy-loaded content, and returns a PNG or base64 image. The fastest path is using a managed actor like Full Page Screenshot — one POST request, one image back, no Puppeteer or Chromium maintenance on your side. For most teams this costs about $0.05 per screenshot and scales to thousands of pages without DevOps.
Quick Answer
The cleanest way to build an automated full page screenshot API workflow is to call a hosted headless-Chrome actor with a JSON payload containing the URL, viewport size, and a delay parameter. The actor renders the page, scrolls to load images and infinite-scroll content, and returns a temporary download URL or base64 string. You can wire this into Zapier, n8n, a cron job, or a CI pipeline with under 20 lines of code. Expect ~5–15 seconds per page depending on weight, and budget around $50 per 1,000 screenshots. No browser binaries, no proxy juggling, no memory leaks.
Why not just use Puppeteer or Playwright?
You can — and for 10 screenshots a week, you should. Puppeteer plus page.screenshot({ fullPage: true }) works fine on a laptop.
The pain shows up around scale and edge cases:
- Lazy-loaded images stay blank unless you scroll the page from top to bottom and wait for
IntersectionObservercallbacks. - Sticky headers repeat down the entire screenshot if you don't hide them before capture.
- CSS animations and skeleton loaders create flicker — you need a stable wait condition, not just
networkidle0. - Memory leaks in long-running Chromium processes will crash a Node worker after ~500 captures.
- Anti-bot pages (Cloudflare, PerimeterX) block default headless fingerprints.
- Concurrency — running 20 Chrome instances on a $20 VPS will OOM in minutes.
A managed screenshot API absorbs all of this. You pay per screenshot instead of paying for an SRE.
How does an automated full page screenshot API actually work?
Under the hood, every commercial screenshot API does roughly the same five steps:
- Spin up a headless Chromium instance with a clean profile.
- Navigate to the target URL with a configurable user agent and viewport.
- Wait for
load,networkidle, or a custom CSS selector. - Auto-scroll the page in increments (typically 500–1000px) with a 100–300ms pause to trigger lazy loading.
- Resize the viewport to the full document height and capture a single PNG.
The Apify actor Full Page Screenshot does exactly this and returns a temporary signed URL you can fetch or pipe straight into S3.
How to call the screenshot API in code
Here's the minimum working example using fetch against the Apify run-sync endpoint. Replace YOUR_TOKEN with your Apify API token.
const res = await fetch(
'https://api.apify.com/v2/acts/apify-screenshot/run-sync-get-dataset-items?token=YOUR_TOKEN',
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
url: 'https://stripe.com/pricing',
fullPage: true,
viewportWidth: 1440,
viewportHeight: 900,
delay: 2000,
format: 'png'
})
}
);
const [result] = await res.json();
console.log(result.screenshotUrl); // temporary download link
That's the whole integration. Pipe screenshotUrl into your CDN, your Slack webhook, or your visual-regression diff tool.
Python version
import requests
payload = {
"url": "https://stripe.com/pricing",
"fullPage": True,
"viewportWidth": 1440,
"delay": 2000
}
r = requests.post(
"https://api.apify.com/v2/acts/apify-screenshot/run-sync-get-dataset-items",
params={"token": "YOUR_TOKEN"},
json=payload,
timeout=60
)
print(r.json()[0]["screenshotUrl"])
How do I handle lazy-loaded images and infinite scroll?
Two parameters matter: delay and the actor's built-in auto-scroll.
delay— milliseconds to wait after scrolling completes. Set this to 1500–3000ms for image-heavy pages (e.g., e-commerce category pages, news sites).- Auto-scroll — enabled by default in the Full Page Screenshot actor. It scrolls in 800px steps with a 200ms pause, which catches 95% of lazy-load implementations using
loading="lazy"orIntersectionObserver.
For infinite-scroll pages (Twitter-style feeds), full-page screenshots are a bad idea — the resulting image can be 50,000+ pixels tall. Cap your viewport height instead, or set fullPage: false and capture a fixed window.
How do I capture mobile screenshots?
Pass mobile viewport dimensions and a mobile user agent. iPhone 14 Pro is 393×852 at 3x DPR; a Pixel 7 is 412×915 at 2.625x.
{
"url": "https://example.com",
"viewportWidth": 393,
"viewportHeight": 852,
"deviceScaleFactor": 3,
"userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15"
}
This is the right approach for visual QA across breakpoints. Run the same URL through three viewports (mobile / tablet / desktop), diff the results, ship with confidence.
How much does an automated screenshot API cost?
Comparing the most common options for 1,000 screenshots/month:
| Service | Price per 1,000 | Free tier | Full-page support |
|---|---|---|---|
| Full Page Screenshot (Apify) | $50 | $5 platform credit | Yes |
| ScreenshotOne | $17–$70 | 100/mo | Yes |
| URLBox | $99+ | 100 trial | Yes |
| ApiFlash | $7–$60 | 100/mo | Yes |
| Self-hosted Puppeteer on a $20 VPS | ~$20 + your time | — | Manual |
The Apify pricing model is pay-per-event at $0.05/screenshot plus platform compute usage (typically a few cents per run). The advantage is you're inside the Apify ecosystem — same token, same dataset storage, same scheduler as any other scraping actor you run.
How do I automate screenshots on a schedule?
Three common patterns:
1. Apify Scheduler (zero infrastructure) Create a schedule in the Apify console, point it at the actor, give it an input JSON with your URL list. Runs every hour/day/week without a server.
2. GitHub Actions cron
on:
schedule:
- cron: '0 9 * * *'
jobs:
screenshot:
runs-on: ubuntu-latest
steps:
- run: curl -X POST "https://api.apify.com/v2/acts/apify-screenshot/runs?token=${{ secrets.APIFY_TOKEN }}" -d @input.json -H "Content-Type: application/json"
3. n8n / Zapier / Make Use the HTTP Request node, paste the same POST body, trigger off a webhook or schedule. Good for non-engineers on the team.
What are real use cases for automated screenshots?
- Visual regression testing — capture production daily, diff against baseline, alert on pixel deltas above 0.5%.
- SEO monitoring — screenshot competitor SERPs and landing pages weekly to track redesigns.
- Compliance archives — legal teams capture marketing pages on publish for audit trails.
- Social card previews — generate OG images for blog posts dynamically.
- PDF generation alternative — full-page PNG is often easier to embed than a multipage PDF.
- AI training data — feed screenshots into multimodal models for layout understanding.
FAQ
Q: How long does a full page screenshot take?
Typical render time is 5–15 seconds for a normal marketing page, longer for image-heavy sites with delay set above 3 seconds. The actor returns synchronously when you use the run-sync endpoint, so you get the image in a single HTTP request.
Q: Can I get the screenshot as base64 instead of a URL?
Yes — set format: "base64" in the input. This is useful when you want to embed the image directly in an email, a PDF, or a Slack block kit message without an extra fetch. PNG-encoded base64 strings for a 1440px page are typically 200–800 KB.
Q: Will the API bypass paywalls or login pages? No, and you shouldn't rely on a screenshot API for authenticated content unless you pass session cookies explicitly. The Full Page Screenshot actor accepts custom cookies and headers, so you can capture logged-in dashboards by forwarding your own session token.
Q: How do I avoid being blocked by Cloudflare or other bot protection?
For most public pages, the actor's default Chromium fingerprint works fine. For aggressive protection, route the request through Apify's residential proxy by setting proxyConfiguration in the input — this adds a few cents per run but resolves nearly all bot-wall issues.
Q: What's the maximum page height I can capture?
The actor handles pages up to roughly 16,000 pixels tall before browsers start refusing to render the canvas. For longer pages (massive blog posts, infinite-scroll feeds) cap the height with viewportHeight and fullPage: false, or split capture into multiple stitched segments.