Skip to main content

Confirm

Are you sure?

Screenshot API

Full-page screenshots of any URL, one flat price

The page as a browser draws it — the whole page by default, sized and cleaned up the way you need, with a link you can drop straight into an <img>.

By · Last updated: September 2026

TL;DR

POST a URL to /screenshot and get the whole rendered page back as a PNG — top to bottom, lazy images loaded, up to 16,384 px tall. screenshot_options change the viewport, pixel density, format, element, dark mode, hidden elements and CSS; every option is included in the same 1 credit.

Free plan, no credit card. 1,000 credits a month.

Try it now — no signup

Try it on a page

Try your own URL

Free · no signup · 2 runs every 10 minutes

Your result will appear here.

Pick one of the pages above to get started.

The request

One POST, one bearer token

POST /screenshot
curl -X POST https://urlpipe.dev/screenshot \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://example.com/pricing", "sync": true, "screenshot_options": {"viewport_width":390,"device_scale_factor":2,"format":"webp"}, "page_options": {"block_cookie_banners":true}}'
Response (Base64 WebP, truncated)
UklGRlq4AABXRUJQVlA4WAoAAAAgAAAARQUAhAUA…

X-Result-Url: https://urlpipe.dev/…  (no key needed, valid 30 days)

Defaults

What a screenshot captures by default

With no options, you get a full-page PNG: the whole scrollable document, not just the fold. The page is scrolled through first so lazy-loaded images are in the picture. The viewport is 1350 × 797 — a desktop window — so two captures of the same page line up and can be compared. The height follows the page up to 16,384 px; a longer page is captured down to that.

The body of the response is the image, Base64-encoded. Decode it to a file, or put it in a data: URI. If you only want to show it, you don't have to touch the bytes at all — see the image URL below.

screenshot_options

Every screenshot option

All optional, all included in the price. Out-of-range values are refused before anything is spent.

OptionValuesWhat it does
full_pagetrue (default) / falseThe whole page, or the viewport only (a #fragment URL scrolls to its anchor)
full_page_max_height100–16,384 pxThe tallest the image may be
viewport_width320–1920, default 1350Window width — 390 gives a phone layout
viewport_height240–1080, default 797Decides the fold and anything sized to the screen
device_scale_factor1–32 renders a retina image, with the high-resolution assets
selectorCSS selectorCapture one element — a pricing table, a chart
formatpng (default), jpeg, webpWebP is usually far smaller for a long page
quality1–100, default 80JPEG and WebP only
omit_backgroundtrue / falseTransparent where the page sets no background (PNG, WebP)
dark_modetrue / falseRenders with prefers-color-scheme: dark
hide_selectorsup to 50 selectorsHide a chat widget or promo bar in the image
stylesup to 20,000 charactersYour own CSS, injected before capture

What the page waits for and what it loses happens in page_options: block_cookie_banners, block_ads, remove_selectors, wait_for_selector and delay. The difference from hide_selectors: page options remove elements from the page itself, so they are gone from the HTML and Markdown too; hidden selectors only change the image.

Image URL

Put the image straight into an <img>

Every screenshot response carries an X-Result-Url header: a signed link to the same image that needs no API key and stays valid for the 30 days the result is kept. Use it in a page you render, a digest email or a social card, with nothing to proxy. An async request gets the same link in its webhook payload as result_url.

Link, not bytes
const res = await fetch("https://urlpipe.dev/screenshot", {
  method: "POST",
  headers: {
    "Authorization": "Bearer YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ url: "https://example.com", sync: true }),
})

img.src = res.headers.get("X-Result-Url")

Use cases

What people build with it

  • Visual regression checks. The default viewport is fixed, so two captures of the same page line up pixel for pixel. Capture before and after a deploy with max_age: 0 and diff the images in your own pipeline.
  • Social cards and thumbnails. selector captures one element, styles injects CSS to tidy it, and viewport_width with full_page: false gives a fixed-size frame.
  • Evidence and archives. A full-page capture records what a page showed on a given day — a price, a claim, a policy. labels tie each capture to a case or client, and the result stays retrievable for 30 days.
  • Mobile and dark-mode reviews. viewport_width: 390 and device_scale_factor: 2 show the phone layout as a retina image; dark_mode shows the theme a site serves to users who prefer it.
  • Link previews. When a page has no og:image, a viewport capture is the preview — see the metadata API.

Pricing

A flat price, whatever the options

A screenshot costs 1 credit — full page or viewport, PNG or WebP, 1× or 3×, with or without dark mode, CSS and hidden elements. The expensive dimensions are capped rather than metered: 16,384 px of height, a 20 MB image, 10 seconds each of delay and wait_for_selector. A capture you already took is reused for free while it is fresher than max_age, and a failed one costs nothing.

/scrape

Screenshot, metadata and Markdown from one visit

Link previews, archives and content audits usually need more than the picture. /scrape takes a list of operations and reads them all off one page load, so the screenshot, the metadata and the Markdown describe the same render:

POST /scrape
{
  "url": "https://example.com/blog/launch",
  "operations": ["screenshot", "meta", "markdown"],
  "screenshot_options": { "format": "webp", "full_page": false },
  "page_options": { "block_cookie_banners": true }
}

Each operation is billed as usual. Four screenshot options change how the page itself loads — viewport_width, viewport_height, device_scale_factor and dark_mode — so a screenshot with any of them takes its own visit, and the HTML beside it stays the page as it normally renders.

Limits

What it does not do

  • No PDF output. Images only: PNG, JPEG or WebP.
  • No video or scrolling captures. One still image per request.
  • No delivery to your storage. The image comes back in the response, the webhook or the result URL; there is no S3 or bucket upload. Copy it where you need it.
  • No interaction before capture. It can wait for an element, but not click, log in or fill a form.
  • Public pages only. Private and intranet addresses are refused.

Pricing

What it costs

CallCreditsFree (calls/mo)Starter (calls/mo)Pro (calls/mo)Scale (calls/mo)
/screenshot1 credit1,00020,00055,000175,000

Cache hits and failed requests cost nothing. Paid plans are never cut off: past the allowance, extra credits are $1.50 per 1,000 credits. See every plan.

FAQ

Frequently asked questions

Are screenshots full-page by default?
Yes. With no options you get the whole scrollable page as a PNG, up to 16,384 px tall, with lazy-loaded images scrolled into view first. Set full_page to false for the viewport only.
Do options like retina, WebP or dark mode cost extra?
No. Every screenshot costs 1 credit, whichever options you use.
How do I show the screenshot without decoding Base64?
Read the X-Result-Url response header. It is a signed link to the image that needs no API key and stays valid for 30 days, so it goes straight into an img tag.
Can I capture just one element?
Yes — set screenshot_options.selector to a CSS selector. The first match is captured; a selector that matches nothing fails the request and costs nothing.
Can I get a PDF of the page?
No. The screenshot API returns PNG, JPEG or WebP images only.
How do I remove cookie banners from screenshots?
Send page_options.block_cookie_banners: true. The banners of the major consent-management providers are removed from the page before capture, without clicking anything.

Make your first request in five minutes.

Free plan, no card. Confirm your email and your API key is live — you'll be making real requests in minutes.