New Text font profiles for every platform

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

POST /api/v1/webshots
{
  "url": "https://moxial.ovh",
  "preset": "macbook-air""iphone""galaxy""ipad",
  "full_page": falsetrue
}
Authorization: Bearer ••••3f9a
moxial.ovh captured as MacBook Air, viewport moxial.ovh captured as MacBook Air, full page
moxial.ovh captured as iPhone, viewport moxial.ovh captured as iPhone, full page
moxial.ovh captured as Galaxy, viewport moxial.ovh captured as Galaxy, full page
moxial.ovh captured as iPad, viewport moxial.ovh captured as iPad, full page
HTTP/2 200
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…
The response body is the image.

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
Features

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.

preset

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.

15 frozen presets · custom up to 3840 px at 1–3×
moxial.ovh captured as a MacBook Air
macbook-air1440 × 900
moxial.ovh captured as an iPad
ipad820
moxial.ovh captured as an iPhone
iphone393
navigate DOM ready wait_for_selector load · lazy-image pass network idle images decoded · fonts delay_ms shutter

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.

requests ad network · ads analytics · trackers cookie wall · consent your page · css, img hide_selectors: ".chat"

Clean by default.

Ads, trackers and consent walls are dropped before they load. Hide anything else with a selector.

.jpg Default. A webshot is a picture.
.png Lossless, transparent on request.
.webp Smaller files, same look.
.pdf Selectable text, vector edges.

The format your pipeline wants.

Compressed by default, each its own way — PNG losslessly, never larger than it started.

The full-page capture of moxial.ovh, 9,710 pixels tall, shown as a strip
full_page: true all 9,710 px of it, lazy images included
clip_selector: "#pricing" or just the one element you name

The whole page, or one element.

Full-page captures scroll the page first, so what loads on scroll is really there.

font_profile · emoji_set

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.

apple android oneui windows neutral
emoji_set: "apple" iOS, iPadOS and macOS
Five emoji — rocket, wave, party popper, heart, flag — rendered with the Apple set
emoji_set: "android" Android and Google products
Five emoji — rocket, wave, party popper, heart, flag — rendered with the Android set
emoji_set: "twemoji" X, Discord, WordPress and much of the open web
Five emoji — rocket, wave, party popper, heart, flag — rendered with the Twemoji set
emoji_set: "whatsapp" WhatsApp on every platform
Five emoji — rocket, wave, party popper, heart, flag — rendered with the WhatsApp set
emoji_set: "samsung" Samsung Galaxy devices
Five emoji — rocket, wave, party popper, heart, flag — rendered with the Samsung set
emoji_set: "facebook" Facebook and Messenger
Five emoji — rocket, wave, party popper, heart, flag — rendered with the Facebook set
emoji_set: "microsoft" Windows, Teams and Office
Five emoji — rocket, wave, party popper, heart, flag — rendered with the Microsoft set
emoji_set: "blobmoji" Nothing, any more
Five emoji — rocket, wave, party popper, heart, flag — rendered with the Blobmoji set
Oct 3, 2026, 8:05 AM en-US · America/New_York
3 oct. 2026, 14:05 fr-FR · Europe/Paris
2026/10/03 21:05 ja-JP · Asia/Tokyo

Any locale, any timezone.

One locale drives the headers and the JavaScript alike. Same moment, three pages — plus color_scheme for dark mode.

How it works

One call. The picture is the response.

  1. 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. 2 A warm browser takes itChrome is already running with a tab waiting. No cold start you didn't cause.
  3. 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
Under the surface

Built for the pipeline, not the demo.

The unglamorous guarantees that decide whether a webshot service survives contact with production.

pinned chrome Same request, same bytes The browser is pinned and its version stamped on every response. A diff between two runs is the page changing, never the machine.
422 503 Failures that say whose fault A 422 is the page, with a named reason like target_timeout. A 503 is us — and it isn't counted against you.
1 render · 1 context Nothing leaks between renders Every render gets a fresh browser context — cookies, storage, service workers — destroyed the moment it's done.
dns · connect · egress Your network stays yours Private, loopback and cloud-metadata addresses are refused three times over: at DNS, at connect, and at the firewall.
expiry · domains · ips Keys you can fence in Give each API key an expiry, a domain allowlist and an IP allowlist. Wrong place is a 403, never mistaken for a wrong key.
idempotency_key Retry without paying twice Retry within 60 s with the same key and get the same stored render back. Any webshot can be fetched again by its ID.
warm pool No cold starts you didn't cause Browsers stay up with tabs waiting ahead of demand, so a simple page comes back in about 460 ms.
ovhcloud · france EU-hosted, kept only if you ask An API render streams straight to you. Send store: true and it waits in EU storage until you delete it.
Pricing

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.

Free $0 forever 100 webshots / month Enough to find out whether it is right for you.
  • One emoji set
  • 7-day retention
  • 10-second render ceiling
  • Community support
Hobby $9 per month 2,000 webshots / month A side project, a docs site, one CI job.
  • All eight emoji sets
  • PDF output
  • 30-day retention
  • 15-second render ceiling
  • Email support
Recommended
Pro $39 per month 15,000 webshots / month A product team shipping previews or watching pages.
  • Everything in Hobby
  • Priority render lane
  • 90-day retention · 3 seats
  • 30-second render ceiling
  • Webhooks Soon
  • Schedules and visual diff Soon
Scale $149 per month 75,000 webshots / month Anyone whose pipeline stops when we do.
  • 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?
15 presets across MacBooks, Windows and Linux desktops, iPhones, a Galaxy and iPads — each with its real viewport, pixel density, touch support and user agent. Or set your own size from 320 to 3840 px at 1×, 2× or 3×.
Can I capture a page on localhost or behind a VPN?
Send the HTML itself — up to 20 MB, inline. We refuse to fetch private addresses on purpose, so a screenshot API can never become a door into your network.
What about pages that render with JavaScript?
Every capture already waits for load, network idle, decoded images and painted fonts. Add wait_for_selector for an element you name, and delay_ms for an animation nothing announces.
Do you keep my webshots?
Only if you ask. An API render streams straight to you and its file is swept minutes later. Send "store": true and we keep it in EU object storage until you delete it.
Why would fonts or emoji ever look wrong?
A page's own web fonts load as normal. But system fonts and emoji come from the machine, and a screenshot server is usually a bare Linux box. We ship platform fonts and one colour-emoji font per browser, so an iPhone capture reads like an iPhone.
Do you inject CSS into my page?
Never. The page loads exactly as it would in a normal browser. The only things we touch are the ones you ask for — like hiding a selector or blocking ads.