Screenshot APIs for visual regression and archiving
Most scraping work ends in text. But a meaningful slice of the web only makes sense as an image: the price that was displayed, the badge that said "in stock", the disclaimer at the bottom of a landing page, the layout that broke after a deploy. Those are questions about pixels, and no amount of markdown extraction answers them.
A screenshot API is the unglamorous primitive that covers this. You send a URL and a viewport, you get back a rendered PNG. This post is about the two jobs people actually use it for — catching visual regressions before users do, and keeping a durable visual record of a page as it was — plus the settings that decide whether your captures are useful or noise.
What a screenshot API actually returns
Fastcrawl's capture endpoint takes a URL and an optional viewport, renders the page in a real browser, and returns the PNG as a base64 data: URI. You write it to a file, or you decode it and push it to object storage. One credit per call, same flat credit as everything else.
curl -X POST https://fastcrawl.net/api/v1/screenshot/ -H "Authorization: Bearer ***" -H "Content-Type: application/json" -d '{"url":"https://example.com/pricing","viewport":{"width":1280,"height":800}}'The response is {"success":true,"url":"...","data":"data:image/png;base64,…"}. Omit viewport and you get the default desktop width; set a narrow one and you get the mobile layout, which is a different page in every sense that matters. TheAPI docs list the full request shape, and failed captures follow the same contract as every other endpoint: a 422 with an actionable error_code and aretryable flag, never a silent empty image.
Visual regression: compare pixels, not markup
DOM diffing is the popular approach to visual regression and it is the wrong one for most teams. A class name change, a reordered flex container, an injected analytics attribute or a rewritten CSS module hash all produce a large markup diff and zero visual change. Your regression suite goes red, someone adds an ignore rule, and within a month the suite is decorative.
Pixel comparison has the opposite failure mode: it is noisy about things that genuinely differ on screen. Ad slots rotate, carousels advance, a relative timestamp reads "2 minutes ago", and a consent banner appears for one capture and not the next. The fix is not a cleverer algorithm, it is controlling what you capture: freeze the clock where the platform allows it, disable animations, and mask the regions you have already decided are allowed to change.
The practical loop is small. Capture the page at a fixed viewport on a schedule, hash the decoded PNG bytes, and store the hash alongside the image. When the hash moves, diff the two images to get a changed-pixel ratio, and only escalate above a threshold you chose. That threshold is the whole tuning knob — a 0.1% floor kills anti-aliasing and font-hinting false positives without hiding a shifted button or a broken grid.
Archiving: evidence you can point at later
The second use case is duller and more valuable. You need to prove what a page said on a given date: a competitor's advertised price, a vendor's published SLA, a job listing's stated salary band, a terms-of-service clause that quietly changed. Text extraction gives you the words; a screenshot gives you the page, including the layout that shows those words were the headline and not a footnote.
For this, capture cadence should follow the thing you are proving. Prices and promos want a daily capture at the same hour. Legal and policy pages want weekly or monthly, plus an out-of-band capture whenever your monitor reports a change. Regulated workflows usually want both a PNG and the rendered PDF — the PDF is easier to attach to a case file, the PNG is easier to skim.
Storage layout matters more than people expect. Keep the object path deterministic (site/url-hash/YYYY-MM-DD.png) and keep a row that records URL, capture time, viewport and content hash. Without that row you end up with a bucket of images and no way to answer "show me this page on the 14th" other than guessing filenames.
Making captures comparable
Two captures of the same URL are only comparable if you controlled the variables. Pin the viewport — 1280×800 for desktop, 390×844 for mobile — and never mix widths in one comparison series, because a responsive layout will produce a huge diff for reasons that have nothing to do with your change. Decide up front whether you want the fold or the full page, and stay consistent.
Then remove the nondeterminism you can. Animation is the biggest source: a hero that fades in over 600ms means two captures a second apart differ by a whole section. Third-party embeds are second: a YouTube thumbnail or an ad iframe can differ between two calls milliseconds apart. Mask them, or accept them as an allowlisted region.
Capture timing is the third variable. Rendering is not instant — a client-side app that fetches its data after mount looks empty if you shoot too early. Capture through the same pipeline you use for scraping, where browser rendering waits for the page to settle, rather than pointing a headless script at the URL and hoping. The trade-off between the two fetch paths is the subject ofHTTP-only vs browser rendering, and it applies to screenshots exactly as it does to text.
Wiring captures into a pipeline
If you already run monitors, the clean pattern is to let the monitor decide when to capture and let a worker do the capturing. The monitor re-fetches on schedule, hashes the normalized content, and fires a webhook only when the page actually changed — no capture, no cost, no storage on quiet days. The webhook wakes your worker, which calls the screenshot endpoint and writes the image plus the metadata row.
{
"mcpServers": {
"fastcrawl": {
"url": "https://mcp.fastcrawl.net/mcp",
"headers": { "Authorization": "Bearer YOUR_KEY" }
}
}
}If your regression checks are driven by an agent rather than a cron, add that config block and the agent gets screenshot and pdf as tools alongside scrape,crawl and map — twelve tools total, one key, no SDK to wire. That is the setup described in connecting an MCP server in one config block, and it is enough for an agent to capture a page, read the diff, and report a layout break in prose.
The scheduling half of this is worth reading before you build it, because the naive version — re-capture everything hourly — is how teams burn a month of credits confirming that nothing changed. Scheduled monitors vs polling covers the cost arithmetic and the webhook shape in detail.
Cost, and when a PDF is the better artifact
Screenshots are cheap relative to their value: one credit per capture, the same credit you pay for a markdown scrape. A daily capture of 50 pages is roughly 1,500 credits a month, which on a flat credit plan is predictable in a way that per-render browser pricing never is. What you should not do is capture on every page load of your app or every request from a user — cache the image and serve the stored copy.
Choose PDF when the artifact needs to be self-contained, paginated, or attached to a document workflow — contracts, filings, screenshots of long pages that nobody will scroll in an image viewer. Choose PNG when you are comparing pixels or when a human will glance at it. Both come from the same API with the same request shape and the same cost, so the decision is about the consumer, not the plumbing.
One last habit worth keeping: store the capture alongside the extracted text for the same URL at the same timestamp. When someone asks in six months why your dataset says a price was $49, the answer is a row and an image, not a re-scrape of a page that has since been rewritten.
Screenshots and PDFs are on every Fastcrawl plan, including free. Start free · Read the docs