Capture any page exactly as your users see it.
Send a URL or raw HTML. Get back a pixel-true PNG, JPEG, WebP or PDF from a warm, pinned Chrome — on the device, locale, fonts and emoji you choose.
100 webshots a month freeNo credit cardHosted in the EU
{
"url": "https://moxial.ovh",
"preset": "macbook-air""iphone""galaxy""ipad",
"full_page": falsetrue
}
Authorization: Bearer ••••3f9a
- content-type
- image/jpeg
- viewport
- 1440 × 9001440 × 9,710393 × 852393 × 14,984360 × 780360 × 15,278820 × 1180820 × 11,102
- density
- @2×@3×@3×@2×
- x-webshot-id
- 01JD7Q…
Real captures of moxial.ovh at each preset's viewport, density and user agent.
- for a warm render of a simple page
- ~460ms
- real devices, or any size you set
- 15presets
- PNG, JPEG, WebP and print-grade PDF
- 4formats
- of CSS injected into your page
- 0lines
Every detail of the capture, one parameter away.
The device, the moment, the format, the fonts. Each one is explicit, so the picture you get is the page your users get.
Real devices, not resized windows.
A preset brings viewport, pixel density, touch and user agent together — so sites serve their real mobile layout, not a squeezed desktop.



Waits for the page, not a timer.
Every step is bounded inside one timeout_ms, so an open websocket never costs you a good webshot.
Clean by default.
Ads, trackers and consent walls are dropped before they load. Hide anything else with a selector.
The format your pipeline wants.
Compressed by default, each its own way — PNG losslessly, never larger than it started.
The whole page, or one element.
Full-page captures scroll the page first, so what loads on scroll is really there.
Fonts and emoji, true to the platform.
System text and emoji come from the machine, and most screenshot servers are bare Linux. Pick the platform per request instead — nothing is injected into your page.
Any locale, any timezone.
One locale drives the headers and the JavaScript alike. Same moment, three pages — plus color_scheme for dark mode.
One call. The picture is the response.
- 1 Send a URL — or the page itselfAny public URL, or up to 20 MB of inline HTML for pages that only exist on your laptop or CI runner.
- 2 A warm browser takes itChrome is already running with a tab waiting. No cold start you didn't cause.
- 3 The image comes straight backIn the response body. Nothing to poll and no bucket to fetch from — unless you ask us to keep a copy.
curl https://webshotter.com/api/v1/webshots \ -H "Authorization: Bearer $WEBSHOTTER_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "url": "https://moxial.ovh", "preset": "macbook-airiphonegalaxyipad", "full_page": falsetrue }' \ -o moxial.jpg
const res = await fetch("https://webshotter.com/api/v1/webshots", { method: "POST", headers: { Authorization: `Bearer ${process.env.WEBSHOTTER_TOKEN}`, "Content-Type": "application/json", }, body: JSON.stringify({ url: "https://moxial.ovh", preset: "macbook-airiphonegalaxyipad", full_page: falsetrue, }), }); await writeFile("moxial.jpg", Buffer.from(await res.arrayBuffer()));
import os, requests res = requests.post( "https://webshotter.com/api/v1/webshots", headers={"Authorization": f"Bearer {os.environ['WEBSHOTTER_TOKEN']}"}, json={ "url": "https://moxial.ovh", "preset": "macbook-airiphonegalaxyipad", "full_page": FalseTrue, }, ) open("moxial.jpg", "wb").write(res.content)
$image = Http::withToken(env('WEBSHOTTER_TOKEN')) ->post('https://webshotter.com/api/v1/webshots', [ 'url' => 'https://moxial.ovh', 'preset' => 'macbook-airiphonegalaxyipad', 'full_page' => falsetrue, ]) ->body(); file_put_contents('moxial.jpg', $image);
200 · image/jpeg · MacBook Air · @2×iPhone · @3×Galaxy · @3×iPad · @2×
Wait ran out? GET /api/v1/webshots/{id}/image
Built for the pipeline, not the demo.
The unglamorous guarantees that decide whether a webshot service survives contact with production.
target_timeout. A 503 is us — and it isn't counted against you.
store: true and it waits in EU storage until you delete it.
What teams point it at
All use cases
1200 × 630
Social preview images
OG images rendered from the real page — no second design to keep in sync.
0 px changed
Visual regression
Two runs, same bytes. When the diff lights up, the page really changed.
Design QA across devices
See the page as an iPad or a Galaxy user would, without owning either.
from CI
Docs and changelogs
Retake every screenshot in your docs on each release, so none is ever a version behind.
Pay per picture. Start at zero.
Metered by render — the one unit you can predict before you call. Reading back what you already paid for is free.
- One emoji set
- 7-day retention
- 10-second render ceiling
- Community support
- All eight emoji sets
- PDF output
- 30-day retention
- 15-second render ceiling
- Email support
- Everything in Hobby
- Priority render lane
- 90-day retention · 3 seats
- 30-second render ceiling
- Webhooks Soon
- Schedules and visual diff Soon
- Everything in Pro
- Reserved render capacity
- Ten seats · custom retention
- Invoicing and a signed DPA
Questions, answered plainly.
Anything else: support@webshotter.com
Which devices can I capture as?
Can I capture a page on localhost or behind a VPN?
What about pages that render with JavaScript?
wait_for_selector for an element you name, and delay_ms for an animation nothing announces.Do you keep my webshots?
"store": true and we keep it in EU object storage until you delete it.