Screenshot API · No SDK needed, plain HTTP · Updated
Screenshot API for cURL and the command line
Capture any website as a PNG, JPEG, WebP, or PDF with a single GET request. The whole API is one URL, so it drops straight into bash scripts, cron jobs, and CI pipelines — no headless Chrome to install, no font stack to maintain, nothing to patch when the next browser CVE lands. If you can run curl, you can take a screenshot.
200 free screenshots for the lifetime of the account. No credit card required.
One command between you and the screenshot
Your key goes in the X-Api-Key header, the page you want goes in the url query parameter, and --output writes the returned image to disk.
# Basic capture → screenshot.png curl "https://api.snapshotflow.com/screenshot?url=https://example.com" \ -H "X-Api-Key: $SNAPSHOTFLOW_KEY" \ --output screenshot.png
export SNAPSHOTFLOW_KEY=sk_live_… once in your shell and the examples below run as-is.
How the request works
There are only three moving parts, so it is worth naming them once:
- Endpoint.
GET https://api.snapshotflow.com/screenshotrenders the page in real Chromium on SnapshotFlow's servers and streams the finished file back in the response body. - Authentication. Send your key as the
X-Api-Keyheader (recommended) or as anapi_key=query parameter when a header is awkward — for example inside anog:imageURL. - Output. The response body is the binary image. Pipe it to a file with
--output, or tostdoutfor further processing. Use-sto silence the progress meter in scripts and-fto make cURL fail loudly on HTTP errors.
# Header auth is the clean default curl -sf "https://api.snapshotflow.com/screenshot?url=https://stripe.com" \ -H "X-Api-Key: $SNAPSHOTFLOW_KEY" \ -o stripe.png # Query-param auth, handy when you can't set headers curl -sf "https://api.snapshotflow.com/screenshot?url=https://stripe.com&api_key=$SNAPSHOTFLOW_KEY" \ -o stripe.png
& between query parameters is a job-control character in bash, and an unquoted URL will silently background your command and drop every parameter after the first.
Copy-paste recipes
The most common captures, ready to paste. Each one is the same endpoint with different query parameters.
# Full-page capture (scrolls to load lazy images) curl -sf "https://api.snapshotflow.com/screenshot?url=https://example.com&full_page=true" \ -H "X-Api-Key: $SNAPSHOTFLOW_KEY" -o fullpage.png # Retina / 2x output at a custom viewport curl -sf "https://api.snapshotflow.com/screenshot?url=https://example.com&width=1440&height=900&device_scale_factor=2" \ -H "X-Api-Key: $SNAPSHOTFLOW_KEY" -o retina.png # Dark mode, ads blocked, slimmer WebP curl -sf "https://api.snapshotflow.com/screenshot?url=https://example.com&dark_mode=true&block_ads=true&format=webp&quality=85" \ -H "X-Api-Key: $SNAPSHOTFLOW_KEY" -o dark.webp # Mobile emulation curl -sf "https://api.snapshotflow.com/screenshot?url=https://example.com&viewport_mobile=true&width=390&height=844" \ -H "X-Api-Key: $SNAPSHOTFLOW_KEY" -o mobile.png # Capture a single element by CSS selector curl -sf "https://api.snapshotflow.com/screenshot?url=https://example.com/pricing&selector=.pricing-table" \ -H "X-Api-Key: $SNAPSHOTFLOW_KEY" -o pricing-card.png # Dismiss a cookie banner, then wait for content, then shoot curl -sf "https://api.snapshotflow.com/screenshot?url=https://example.com&click=%23accept-cookies&wait_for_selector=.hero&delay=500" \ -H "X-Api-Key: $SNAPSHOTFLOW_KEY" -o clean.png
URL-encode any selector or value that contains special characters — #accept-cookies becomes %23accept-cookies in the query string. Add --data-urlencode with -G if you would rather let cURL encode parameters for you (shown under cron).
The options you'll reach for most
| Goal | Parameter | Example value |
|---|---|---|
| Whole scrollable page | full_page | true |
| Output format | format | png · jpeg · webp · pdf |
| Compression (jpeg/webp) | quality | 1–100 |
| Viewport size | width / height | 1440 / 900 |
| Retina pixel density | device_scale_factor | 2 |
| Mobile emulation | viewport_mobile | true |
| Force dark theme | dark_mode | true |
| Block ads | block_ads | true |
| One element only | selector | .pricing-table |
| Hide noisy elements | hide_selectors | .cookie-bar,.chat-widget |
| Wait for an element | wait_for_selector | .hero |
| Extra settle time | delay | 500 (ms) |
| Cache the result | cache_ttl | 2592000 (30 days) |
The full list — geolocation, wait_until, click actions, PDF paper formats, and more — lives in the API reference.
Save a page as PDF from the terminal
Switch format=pdf and write to a .pdf file. Combine with full_page=true for the whole document and pick a paper size with pdf_paper_format.
# Full page rendered to an A4 PDF curl -sf "https://api.snapshotflow.com/screenshot?url=https://example.com&format=pdf&full_page=true&pdf_paper_format=a4" \ -H "X-Api-Key: $SNAPSHOTFLOW_KEY" \ --output page.pdf
This is the one-liner behind invoice archives, report snapshots, and "save this page for the record" automation — no print dialog, no browser extension.
Scheduled screenshots with cron
Because a capture is one command, a one-line crontab entry turns it into a daily archive. Date-stamp the filename and you have a visual history of any page over time — pricing changes, competitor launches, compliance evidence.
# crontab -e → capture a pricing page every day at 06:00 0 6 * * * curl -sf -G "https://api.snapshotflow.com/screenshot" \ --data-urlencode "url=https://example.com/pricing" \ --data-urlencode "full_page=true" \ -H "X-Api-Key: $SNAPSHOTFLOW_KEY" \ -o "/var/snapshots/pricing-$(date +\%F).png"
-G with --data-urlencode lets cURL build and encode the query string for you, which keeps long URLs readable. Escape the % in date as \% inside a crontab. Prefer not to manage cron yourself? SnapshotFlow can run scheduled captures server-side too.
Screenshots in CI pipelines
Every CI runner already ships with cURL, so capturing your deployed pages after a release is a single step — no browser install, no headless setup that breaks between runner images. Store the key as a secret, capture, and attach the image to the build.
GitHub Actions
# .github/workflows/deploy.yml (excerpt) - name: Capture homepage after deploy run: | curl -sf "https://api.snapshotflow.com/screenshot?url=https://your-site.com&full_page=true" \ -H "X-Api-Key: ${{ secrets.SNAPSHOTFLOW_KEY }}" \ --output homepage.png - uses: actions/upload-artifact@v4 with: name: homepage-screenshot path: homepage.png
GitLab CI
# .gitlab-ci.yml capture: stage: verify script: - curl -sf "https://api.snapshotflow.com/screenshot?url=$DEPLOY_URL&full_page=true" -H "X-Api-Key: $SNAPSHOTFLOW_KEY" -o homepage.png artifacts: paths: [homepage.png]
For visual review on every pull request, pair this with the /diff endpoint to flag pixel changes against a baseline — see visual regression testing.
JSON responses and base64 with jq
By default the response body is the raw binary image, which is ideal for --output. When you would rather have structured metadata — for instance to know whether a result was served from cache — request JSON and parse it with jq.
# Decode a base64 image field from a JSON response curl -sf "https://api.snapshotflow.com/screenshot?url=https://example.com&response_type=json" \ -H "X-Api-Key: $SNAPSHOTFLOW_KEY" \ | jq -r '.image' | sed 's/^data:image\/png;base64,//' | base64 -d > out.png
Check the exact response shape and field names for your account in the response reference before wiring this into a script.
Batch capture and content extraction
The same key unlocks the rest of the API from cURL. Send many URLs in one POST /batch call, or pull a page's text instead of its pixels.
# Capture several URLs in one request curl -sf "https://api.snapshotflow.com/batch" \ -H "X-Api-Key: $SNAPSHOTFLOW_KEY" \ -H "Content-Type: application/json" \ -d '{"urls":["https://example.com","https://stripe.com"],"format":"png","full_page":true}' # Extract page content as Markdown instead of an image curl -sf "https://api.snapshotflow.com/extract?url=https://example.com&format=markdown" \ -H "X-Api-Key: $SNAPSHOTFLOW_KEY" -o page.md
A simple bash loop also works for ad-hoc lists — read URLs from a file and shoot each one with a numbered filename. Full batch options, webhooks, and job polling are in the docs.
Error handling and retries in scripts
A capture can fail for reasons outside your control — a slow origin, a missing selector, a transient network blip. Make scripts robust with three cURL flags and one retry loop.
-fmakes cURL return a non-zero exit code on HTTP 4xx/5xx instead of writing an error body to your image file.--retry 3 --retry-delay 2retries transient failures with a short backoff.--max-time 60caps how long a single capture may run so a hung job can't stall your pipeline.
# Resilient capture with retries and a hard timeout curl -sf --retry 3 --retry-delay 2 --max-time 60 \ "https://api.snapshotflow.com/screenshot?url=https://example.com&full_page=true" \ -H "X-Api-Key: $SNAPSHOTFLOW_KEY" \ -o shot.png || { echo "capture failed" >&2; exit 1; }
A 401 means the key is missing or wrong, 422 usually means a selector or wait_for_selector wasn't found, and 429 means you've hit a rate limit — back off and retry. The error reference lists every code.
Why not headless Chrome with the --screenshot flag?
google-chrome --headless --screenshot is fine for a quick capture on your laptop. On a server it stops being simple: you install Chrome and a font stack on every box, keep them updated, and accept whatever loads in the first moment of render. Cookie consent walls, ads, and half-loaded lazy images all end up in the shot, and JavaScript-heavy pages frequently capture before the content exists.
The API moves all of that behind one URL. Rendering waits for the page properly, ads can be stripped with block_ads, full_page capture handles lazy loading, and parameters give you formats, viewports, dark mode, retina, and caching. Your servers keep zero browser dependencies — which also means nothing to patch when the next Chrome CVE lands.
| Local headless Chrome | SnapshotFlow API + cURL | |
|---|---|---|
| Install & maintain | Chrome + fonts on every box | Nothing — cURL only |
| Ads & cookie banners | End up in the shot | Blocked / dismissable |
| Lazy-loaded content | Often missing | full_page scrolls first |
| Formats | PNG (and PDF with flags) | PNG · JPEG · WebP · PDF |
| Caching & retina | Roll your own | Built-in parameters |
| Security patching | Your responsibility | Handled for you |
Prefer to keep the engine in-house? SnapshotFlow is self-hostable with Docker, and the same cURL commands work against your own deployment — just point them at your host and set API_KEYS.
Parameter reference (cURL essentials)
| Parameter | Type | Default | What it does |
|---|---|---|---|
url | string | — | The page to capture (required) |
format | enum | png | png · jpeg · webp · pdf |
full_page | boolean | false | Capture the full scrollable height |
width / height | integer | 1280 / 800 | Viewport size in px |
device_scale_factor | float | 1 | Pixel ratio — use 2 for retina |
viewport_mobile | boolean | false | Mobile emulation (touch, mobile meta) |
quality | integer | 80 | 1–100, jpeg/webp only |
dark_mode | boolean | false | Force prefers-color-scheme: dark |
block_ads | boolean | false | Block ads before capture |
selector | string | — | Capture only the matching element |
hide_selectors | string | — | Comma-separated selectors to hide |
click | string | — | Selector to click before capture |
wait_for_selector | string | — | Wait for this element to appear |
wait_until | enum | networkidle2 | load · domcontentloaded · networkidle0/2 |
delay | integer ms | 0 | Extra wait after load (max 10000) |
pdf_paper_format | enum | a4 | a4 · a3 · letter · legal · … |
This is the cURL-focused subset. The complete reference — geolocation, video, diff, batch, and webhooks — is in the full documentation.
Command-line screenshot FAQ
How do I take a screenshot of a website with cURL?
Send one GET request to https://api.snapshotflow.com/screenshot with a url parameter and your API key in the X-Api-Key header, then write the response to a file with --output. The page renders in real Chromium on SnapshotFlow's servers and the finished image streams back. Nothing to install beyond cURL itself, which already ships with macOS, Linux, and Windows 10 and later.
Can I schedule website screenshots with cron?
Yes. Because a capture is a single cURL command, a one-line crontab entry gives you scheduled screenshots. Date-stamp the output filename and you build a visual changelog — archive a competitor's pricing page daily, or snapshot your own homepage after each deploy.
Do I still need headless Chrome with the --screenshot flag?
google-chrome --headless --screenshot works for quick local captures, but on servers it means installing Chrome and fonts, and the output often includes cookie banners and ads. It also has no caching, retina control, or full-page handling for lazy-loaded content. The API gives you those in URL parameters and keeps your servers browser-free.
Can I save the screenshot as a PDF from the command line?
Yes. Add format=pdf and write the response to a .pdf file. Set pdf_paper_format to a4, letter, legal, and more, and combine with full_page=true to render the entire scrollable page.
Can I use the screenshot API in CI pipelines like GitHub Actions?
Yes. Every CI runner already has cURL, so one step captures your staging or production pages after a deploy: store the API key as a secret, curl the endpoint, and upload the image as a build artifact. No browser installation step, no flaky headless setup in CI.
Is there a free tier for command-line screenshots?
Yes. The free plan includes 200 screenshots for the lifetime of the account with every feature: all formats including PDF, full-page capture, dark mode, retina output, ad blocking, and element capture. No credit card is required, and you can self-host the same engine with Docker.
One command away from your first screenshot
Grab a key, paste the cURL line, done. 200 free screenshots for the lifetime of the account, no credit card.