Screenshot any website as it appears on iPhone, iPad, or Android
How to capture mobile and tablet screenshots of any website with a screenshot API. Correct viewport sizes for iPhone, iPad, and Android, the difference between device=mobile and just setting width, full-page captures, and Node.js code examples.
The first time I tried capturing a mobile screenshot, I just set the viewport width to 390 pixels and called it done. It came out looking off, though. Fonts were too small, elements weren't arranged the way they appear on an actual iPhone. It took me a bit to figure out that mobile rendering isn't just about screen width. Device pixel ratio, user agent string, and how the site decides which version to serve all play a part.
In this article I'll show you how to get mobile screenshots that actually look like what a real user sees on their phone. Just one HTTP request with the right parameters, and you're done.
Three things that make mobile rendering different from desktop
Viewport is the obvious one. An iPhone 14 Pro uses 393×852 CSS pixels. A Samsung Galaxy S24 is 360×780. If you only pass the width, the API falls back to a default height, which is usually fine for viewport screenshots. For full-page captures, height doesn't matter at all because the screenshot extends to the bottom of the page regardless.
Then there's device pixel ratio (DPR). Modern phones render at 2x or 3x density. A website on an iPhone with DPR 3 draws fonts, icons, and SVGs three times sharper than at DPR 1. Skip the DPR setting, and your screenshot will look blurry compared to a real phone. The tradeoff is file size: a DPR 3 capture weighs noticeably more.
And finally, user agent. A lot of sites check the user agent string to decide whether to show the mobile or desktop layout. Without a mobile user agent, you might get the desktop version squeezed into 390 pixels instead of the actual mobile layout with its hamburger menu and touch-friendly buttons.
A basic mobile screenshot with the ScreenshotRun API
Here's the simplest possible request. iPhone 14 Pro width, PNG format:
curl "https://screenshotrun.com/api/v1/screenshots/capture?\
url=https://vercel.com&\
width=393&\
height=852&\
device=mobile&\
response_type=image" \
-H "Authorization: Bearer YOUR_API_KEY" \
-o vercel-mobile.pngThe device=mobile parameter does two things at once: it sets a mobile user agent and enables touch emulation. Without it, Vercel would serve the desktop layout, just crammed into a narrow window.
What you get back is a screenshot that looks like an actual mobile browser. Navigation collapses into a hamburger menu, the "Deploy" and "Get a Demo" buttons stack vertically for the smaller screen, and the whole layout adapts properly.

Picking the right dimensions for common devices
I've collected the viewport sizes I reach for most often. All of these are in CSS pixels, not physical ones:
iPhone 14 / 14 Pro: 393 × 852
iPhone SE (3rd gen): 375 × 667
Samsung Galaxy S24: 360 × 780
iPad (10th gen): 820 × 1180
iPad Pro 12.9": 1024 × 1366
For most use cases, two configs are enough: 393×852 for phones and 820×1180 for tablets. Unless you need pixel-perfect accuracy for a specific device, these two will cover the vast majority of mobile users.
Here's the iPad example:
curl "https://screenshotrun.com/api/v1/screenshots/capture?\
url=https://vercel.com&\
width=820&\
height=1180&\
format=webp&\
response_type=image" \
-H "Authorization: Bearer YOUR_API_KEY" \
-o vercel-ipad.webpNotice there's no device=mobile here. For tablets, just passing the dimensions is enough. If you add device=mobile, the API will set a phone user agent, and the site might serve the phone layout instead of the tablet one. In the screenshot you can see that Vercel at 820px width serves the desktop layout with full navigation and large headings — which is exactly how most sites look on an iPad.

Mobile screenshot in Node.js — barely more than a few lines
If you're working from Node.js, here's a minimal script:
const fs = require('fs');
const params = new URLSearchParams({
url: 'https://stripe.com',
width: '393',
height: '852',
device: 'mobile',
format: 'png',
response_type: 'image'
});
async function captureMobile() {
const response = await fetch(
`https://screenshotrun.com/api/v1/screenshots/capture?${params}`,
{ headers: { Authorization: 'Bearer YOUR_API_KEY' } }
);
const buffer = Buffer.from(await response.arrayBuffer());
fs.writeFileSync('stripe-mobile.png', buffer);
console.log('Done: stripe-mobile.png');
}
captureMobile();On the screenshot you can see Stripe's mobile version: hamburger menu in the top right, large "Get started" and "Sign up with Google" buttons stacked full-width, client logos lined up in a single column. Looks just like it would on a real iPhone.

The same approach works in Python, Go, PHP, or really any language that can send a GET request. I wrote separate guides for each if you want the full code for your stack. There's also a more detailed Node.js tutorial that covers additional options like custom headers and timeouts.
Full-page mobile screenshots — when a single viewport isn't enough
A viewport screenshot only shows the first screen. But sometimes you need the entire page: for documentation, for QA review, or to show a client how their mobile site looks from the header all the way down to the footer.
Just add full_page=true:
curl "https://screenshotrun.com/api/v1/screenshots/capture?\
url=https://stripe.com&\
width=393&\
device=mobile&\
full_page=true&\
format=webp&\
response_type=image" \
-H "Authorization: Bearer YOUR_API_KEY" \
-o stripe-mobile-full.webpHeight doesn't matter here — the API scrolls to the bottom of the page and captures everything. I'd recommend WebP for full-page mobile screenshots because these files can get really long, and a PNG of Stripe's full homepage weighs several megabytes.

One thing to watch out for: pages with infinite scroll (Twitter feeds, Pinterest boards) will keep loading and can produce a massive file. For those, stick with a viewport screenshot at a fixed height.
Capturing desktop and mobile versions of the same pages in one script
A common scenario: you need screenshots of the same pages in both desktop and mobile versions. Maybe for a portfolio, a client presentation, or to keep an eye on your responsive layout. Here's a script that handles both:
const fs = require('fs');
const pages = ['https://yoursite.com', 'https://yoursite.com/pricing'];
const viewports = [
{ name: 'desktop', width: 1280, height: 800 },
{ name: 'mobile', width: 393, height: 852, device: 'mobile' }
];
async function capture(url, viewport) {
const slug = new URL(url).pathname.replace(/\//g, '-') || 'home';
const params = new URLSearchParams({
url,
width: String(viewport.width),
height: String(viewport.height),
format: 'webp',
response_type: 'image',
...(viewport.device && { device: viewport.device })
});
const res = await fetch(
`https://screenshotrun.com/api/v1/screenshots/capture?${params}`,
{ headers: { Authorization: `Bearer ${process.env.API_KEY}` } }
);
const filename = `${slug}-${viewport.name}.webp`;
fs.writeFileSync(filename, Buffer.from(await res.arrayBuffer()));
console.log(` ✓ ${filename}`);
}
async function run() {
for (const url of pages) {
for (const vp of viewports) {
await capture(url, vp);
}
}
}
run();Two pages times two viewports makes four screenshots. Takes about 10 seconds. If you want to speed things up, wrap it in Promise.all, but for a handful of requests the sequential version is fast enough. And if you're generating these on a schedule, you might want to look into caching your screenshots so you're not re-capturing pages that haven't changed.
Mistakes I've made with mobile screenshots (so you don't have to)
Forgot device=mobile. Got a desktop layout in a narrow window. Site didn't switch to mobile navigation, text was tiny, buttons weren't touch-friendly. Fix is obvious once you know about it: add the parameter.
Cookie banner covered half the screen. On mobile, those consent banners take up even more real estate than on desktop. I pass block_cookie_banners=true now and don't think about it anymore. I went into more detail on dealing with popups in my article about situations where a screenshot catches what a log can't.
Screenshot loaded with system fonts. Custom fonts hadn't finished loading before the capture happened. Adding delay=1500 gives them time. On mobile emulation, fonts sometimes take a bit longer because the browser simulates a slower connection.
Full-page screenshot missed lazy-loaded images. Images below the fold didn't load because lazy loading only triggered for the visible area. The API handles this automatically by scrolling through the page, but if your site has custom lazy-load logic, you might need to add a delay parameter on top of that.
When a mobile screenshot API makes sense and when it doesn't
The API works well when you need to quickly grab the mobile version of any public site. Link directory previews, website preview thumbnails, competitor monitoring, QA checks before deploy, generating OG images for social sharing. If you prefer working from the command line, I also have a guide on taking screenshots with plain curl.
If you need to test a specific physical device running a specific version of iOS or Android, that's a different problem. BrowserStack, LambdaTest, and similar services run real devices for that. A screenshot API emulates a mobile browser through Chromium rather than launching actual Safari on an actual iPhone. For 95% of tasks there's no visible difference, but if your bug only shows up on Safari iOS 17.4, the API won't help there.
If you want to give it a try, the free ScreenshotRun tier comes with 300 screenshots per month. Plenty to experiment and see if it fits your workflow.
Thanks for reading. If you have questions about specific devices or viewport sizes, I'd be happy to help.
Vitalii Holben