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
That's the entire API. Every option — format, viewport, full page, dark mode, retina, ad blocking — is just another query parameter on the same URL. Set 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/screenshot renders 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-Key header (recommended) or as an api_key= query parameter when a header is awkward — for example inside an og:image URL.
  • Output. The response body is the binary image. Pipe it to a file with --output, or to stdout for further processing. Use -s to silence the progress meter in scripts and -f to 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
Quote your URL. Always wrap the whole endpoint in double quotes. The & 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

GoalParameterExample value
Whole scrollable pagefull_pagetrue
Output formatformatpng · jpeg · webp · pdf
Compression (jpeg/webp)quality1100
Viewport sizewidth / height1440 / 900
Retina pixel densitydevice_scale_factor2
Mobile emulationviewport_mobiletrue
Force dark themedark_modetrue
Block adsblock_adstrue
One element onlyselector.pricing-table
Hide noisy elementshide_selectors.cookie-bar,.chat-widget
Wait for an elementwait_for_selector.hero
Extra settle timedelay500 (ms)
Cache the resultcache_ttl2592000 (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.

  • -f makes 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 2 retries transient failures with a short backoff.
  • --max-time 60 caps 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 ChromeSnapshotFlow API + cURL
Install & maintainChrome + fonts on every boxNothing — cURL only
Ads & cookie bannersEnd up in the shotBlocked / dismissable
Lazy-loaded contentOften missingfull_page scrolls first
FormatsPNG (and PDF with flags)PNG · JPEG · WebP · PDF
Caching & retinaRoll your ownBuilt-in parameters
Security patchingYour responsibilityHandled 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)

ParameterTypeDefaultWhat it does
urlstringThe page to capture (required)
formatenumpngpng · jpeg · webp · pdf
full_pagebooleanfalseCapture the full scrollable height
width / heightinteger1280 / 800Viewport size in px
device_scale_factorfloat1Pixel ratio — use 2 for retina
viewport_mobilebooleanfalseMobile emulation (touch, mobile meta)
qualityinteger801–100, jpeg/webp only
dark_modebooleanfalseForce prefers-color-scheme: dark
block_adsbooleanfalseBlock ads before capture
selectorstringCapture only the matching element
hide_selectorsstringComma-separated selectors to hide
clickstringSelector to click before capture
wait_for_selectorstringWait for this element to appear
wait_untilenumnetworkidle2load · domcontentloaded · networkidle0/2
delayinteger ms0Extra wait after load (max 10000)
pdf_paper_formatenuma4a4 · 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.