Mobile screenshots
To capture what a page looks like on a phone or tablet, you have two tools: a device preset that sets everything in one word, or a custom viewport where you set each property. This guide explains which to use and why a narrow viewport alone is often not enough. It is part of the capture guides.
The minimal request
The requests below need an API key in RS_API_KEY (get one free).
GET, with a device preset:
curl -G "https://api.renderscreenshot.com/v1/screenshot" \ -H "Authorization: Bearer $RS_API_KEY" \ --data-urlencode "url=https://example.com" \ -d device=iphone_14 \ -o iphone.png
POST, with the same preset:
curl -X POST https://api.renderscreenshot.com/v1/screenshot \ -H "Authorization: Bearer $RS_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "url": "https://example.com", "device": "iphone_14" }' \ -o iphone.png
The response is a 1170 x 2532 PNG: a 390 x 844 viewport at a device scale of 3. In a POST body, keep device at the top level next to url. The devices reference explains why a nested viewport.device is not used.
Three ways to get a mobile capture
| Approach | You set | Good for |
|---|---|---|
device preset |
One id, for example iphone_14, pixel_7 or ipad_air |
Realistic captures of a known device |
preset: mobile or mobile_landscape |
One preset that also sets format and cache defaults | Quick portrait or landscape phone shots |
Custom viewport |
width, height, scale, mobile, plus browser.user_agent |
A device that is not in the list, or exact control for tests |
A device preset sets width, height, scale, mobile and the user agent. Explicit viewport values win over the device, so for landscape send the device together with a swapped viewport.width and viewport.height.
Why width alone is not enough
Many teams start with width: 390 and get a desktop page squeezed into a narrow window. That happens because three separate things make a browser behave like a phone:
- The layout viewport. A real phone honours the page's viewport meta tag,
<meta name="viewport" content="width=device-width">. Chrome applies that tag only in mobile emulation, which isviewport.mobile: true. Without it, a page that has no tag still lays out at a desktop width. - Input capabilities. Media queries such as
(hover: none)and(pointer: coarse)describe a touch screen. The renderer does not emulate touch today, for device presets or custom viewports, so a site that shows its mobile menu or swipe controls only after a touch check renders its pointer version. - The user agent. Some sites choose the mobile template on the server by reading the
User-Agentheader. A narrow window with a desktop user agent gets the desktop HTML. Device presets send that device's user agent; with a custom viewport, setbrowser.user_agentyourself. The custom user agent guide shows the effect.
A device preset handles the first and the third, so start there. Switch to a custom viewport only when you need a size that is not in the list.
Device presets
Get the current list from the API rather than from a copy in your code:
curl https://api.renderscreenshot.com/v1/devices \ -H "Authorization: Bearer $RS_API_KEY"
Each entry has an id, a name, a viewport (width and height in CSS pixels, device_scale_factor, is_mobile, has_touch) and a user_agent. A selection:
| Device id | Viewport (CSS px) | Scale | Output image (px) |
|---|---|---|---|
iphone_se |
375 x 667 | 2 | 750 x 1334 |
iphone_14 |
390 x 844 | 3 | 1170 x 2532 |
pixel_7_pro |
412 x 892 | 3.5 | 1442 x 3122 |
ipad_air |
820 x 1180 | 2 | 1640 x 2360 |
ipad_pro_12 |
1024 x 1366 | 2 | 2048 x 2732 |
The output size is always viewport times scale. A large scale makes large files: see retina screenshots for the measured cost, and use viewport.scale: 1 together with a device-sized custom viewport if you only need the layout.
An unknown device id fails with a validation_error (HTTP 400, code invalid_device) before any rendering, so a typo never uses a credit.
Custom viewport
For a device that is not in the list, set the properties yourself:
{ "url": "https://example.com", "viewport": { "width": 360, "height": 780, "scale": 3, "mobile": true }, "browser": { "user_agent": "Mozilla/5.0 (Linux; Android 14; Pixel 8) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Mobile Safari/537.36" } }
| Parameter | Default | Limits | Effect |
|---|---|---|---|
viewport.width |
1280 |
1 to 7,680 | Layout width in CSS pixels |
viewport.height |
800 |
1 to 4,320 | Visible height; also the image height in viewport mode |
viewport.scale |
1 |
1 to 3 | Device pixel ratio; multiplies output pixels |
viewport.mobile |
false |
Applies the meta viewport tag, as a phone does | |
browser.user_agent |
browser default | Makes the server send its mobile HTML |
viewport.touch turns on touch support, and device presets set it for you. viewport.landscape only sets the browser's landscape flag and does not swap the width and height, so set the orientation with width and height.
Custom scale accepts 1 to 3. Device presets can use their real ratio, for example 3.5 for pixel_7_pro.
Full-page mobile captures
Mobile pages are long. Combine a device with full-page capture to get the whole scroll height at the device width:
{ "url": "https://example.com/blog/post", "device": "iphone_14", "capture": { "mode": "full_page" } }
At scale 3, a 6,000 px tall mobile page becomes an 18,000 px tall image. If the image is for a web page or for a vision model, set viewport.scale: 1 next to the device, or resize the image on your side. The full-page guide covers scroll.max_height.
SDK examples
Device preset first, then a custom viewport.
Node.js
import { writeFile } from 'node:fs/promises'; import { Client, TakeOptions } from 'renderscreenshot'; const client = new Client(process.env.RS_API_KEY); const phone = await client.take(TakeOptions.url('https://example.com').device('iphone_14')); await writeFile('iphone.png', phone); const custom = await client.take( TakeOptions.url('https://example.com').width(360).height(780).scale(3).mobile() ); await writeFile('custom.png', custom);
Python
import os from renderscreenshot import Client, TakeOptions client = Client(os.environ['RS_API_KEY']) phone = client.take(TakeOptions.url('https://example.com').device('iphone_14')) custom = client.take( TakeOptions.url('https://example.com').width(360).height(780).scale(3).mobile() ) with open('iphone.png', 'wb') as f: f.write(phone)
Ruby
require 'renderscreenshot' client = RenderScreenshot.client(ENV.fetch('RS_API_KEY')) phone = client.take(RenderScreenshot::TakeOptions.url('https://example.com').device('iphone_14')) custom = client.take( RenderScreenshot::TakeOptions.url('https://example.com').width(360).height(780).scale(3).mobile ) File.binwrite('iphone.png', phone)
PHP
use RenderScreenshot\Client; use RenderScreenshot\TakeOptions; $client = new Client(getenv('RS_API_KEY')); $phone = $client->take(TakeOptions::url('https://example.com')->device('iphone_14')); $custom = $client->take( TakeOptions::url('https://example.com')->width(360)->height(780)->scale(3)->mobile() ); file_put_contents('iphone.png', $phone);
Java
Client client = new Client(System.getenv("RS_API_KEY")); byte[] phone = client.take(TakeOptions.url("https://example.com").device("iphone_14")); byte[] custom = client.take( TakeOptions.url("https://example.com").width(360).height(780).scale(3.0).mobile() ); Files.write(Path.of("iphone.png"), phone);
Go
phone, err := client.Take(ctx, rs.URL("https://example.com").Device("iphone_14")) if err != nil { log.Fatal(err) } custom, err := client.Take(ctx, rs.URL("https://example.com").Width(360).Height(780).Scale(3).Mobile(), ) if err != nil { log.Fatal(err) } os.WriteFile("iphone.png", phone, 0o644) os.WriteFile("custom.png", custom, 0o644)
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| Desktop layout in a narrow image | mobile not set, so the meta viewport tag is ignored |
Use a device preset or set viewport.mobile: true |
| Mobile layout, but tiny text | The page has no meta viewport tag | Expected for such pages; this is what a phone shows too |
Desktop HTML even with mobile: true |
The server picks the template from the user agent | Use a device preset, or set browser.user_agent to a mobile string |
| Hamburger menu or swipe UI missing | Site checks for touch support | Touch is not emulated; capture the mobile menu with page.click on its button, or inject the class the site sets with page.scripts |
| App install banner or cookie sheet covers the page | Mobile-only overlays | block.cookie_banners: true, or page.hide with the banner selector |
| Image is huge | Scale 3 times full page height | Use viewport.scale: 1 |
validation_error naming the device |
Device id not in the list | Check GET /v1/devices |
| Landscape needed | Portrait is the default | Swap viewport.width and viewport.height next to the device, or use preset: mobile_landscape |
Related
- Devices reference and
GET /v1/devices. - Viewport parameters.
- Retina screenshots for scale and file size.
- Presets reference for
mobileandmobile_landscape.