How to Capture a Mobile-Width Website Screenshot with URL2PNG
Set URL2PNG’s viewport to a mobile width, then choose whether to emulate a mobile browser, capture the full page, and wait for late content.
To capture a mobile-width website screenshot with URL2PNG, set its viewport option to the browser dimensions you want, such as 390x844. The first number is the viewport width in pixels and determines how the page lays itself out; the second is its height. If the site identifies devices by browser identity, also send an appropriate mobile user_agent. These options do different jobs: viewport dimensions control the available layout area, while the user agent can affect what the site serves.
URL2PNG’s v6 endpoint takes a signed request. Build and encode the complete query string first, then generate the token from that exact string and your account secret. Keep the secret on your server. See the URL2PNG documentation for current API details and account requirements.
1. Choose the mobile capture dimensions
Use viewport=390x844 for a 390-pixel-wide browser viewport with an 844-pixel height. Substitute dimensions that match the device or test case you need. The dimensions describe the browser viewport in CSS pixels; they do not promise a particular physical device’s rendering in every respect.
| Option | What it controls | When to set it |
|---|---|---|
viewport |
Browser render width and height | Always set an explicit narrow width for a mobile-width layout. |
user_agent |
Browser/device identity sent with the request | Use when the site varies content based on user-agent detection. |
fullpage |
Viewport-only versus full-document capture | Set false for a screen-sized image; set true to attempt the full document. |
thumbnail_max_width |
Maximum output image width / scaling | Use to constrain the resulting image. It does not change the browser layout width. |
delay |
Fixed wait after document readiness and asset loading | Use when content commonly appears shortly after initial page readiness. |
say_cheese |
Waits for an element with ID url2png-cheese |
Prefer when you control the page and can mark capture readiness. |
unique |
Varies the request to force a fresh capture | Set a changing value, such as a timestamp, when cached output may be stale. |
URL2PNG documentation lists a 30-day default TTL (2592000 seconds) in its quickstart. Because the documentation shows differing defaults in some places and examples, provide the settings that matter to your capture rather than relying on implicit defaults.
2. Generate a signed URL in Python
This runnable Python example builds a URL2PNG v6 request for a viewport-only capture. Set the API key, secret, and target URL in environment variables. The token is the MD5 hash of the encoded query string concatenated with the secret. If you add or change an option, rebuild the query string and token together.
import hashlib
import os
from urllib.parse import urlencode
import requests
api_key = os.environ["URL2PNG_API_KEY"]
secret = os.environ["URL2PNG_SECRET"]
target_url = "https://example.com/"
params = {
"url": target_url,
"viewport": "390x844",
"fullpage": "false",
}
query = urlencode(params)
token = hashlib.md5((query + secret).encode("utf-8")).hexdigest()
request_url = f"https://api.url2png.com/v6/{api_key}/{token}/png/?{query}"
response = requests.get(request_url, timeout=90)
response.raise_for_status()
with open("mobile-shot.png", "wb") as image_file:
image_file.write(response.content)
Install the dependency with python -m pip install requests. Run it with URL2PNG_API_KEY and URL2PNG_SECRET set in the process environment. Keep these credentials out of browser code, source control, and client-visible URLs.
3. Make the same request with cURL
cURL can download an already signed URL. Generate the URL on a trusted server using the exact query-encoding and token process above, then pass the complete URL as URL2PNG_SIGNED_URL:
curl --fail --location "$URL2PNG_SIGNED_URL" -o mobile-shot.png
Do not hand-edit the query string after generating its token. URL encoding is significant: changing encoding, parameter values, or query text changes the input to the hash. For a production workflow, generate the signed URL programmatically with URL2PNG’s official language examples and keep the secret server-side.
4. Make the same request with Node.js
This Node.js example creates the query with URLSearchParams, signs that exact encoded query, fetches the image, and writes it to disk. It requires a Node.js version with built-in fetch.
import { createHash } from "node:crypto";
import { writeFile } from "node:fs/promises";
const apiKey = process.env.URL2PNG_API_KEY;
const secret = process.env.URL2PNG_SECRET;
if (!apiKey || !secret) throw new Error("Set URL2PNG_API_KEY and URL2PNG_SECRET");
const params = new URLSearchParams({
url: "https://example.com/",
viewport: "390x844",
fullpage: "false",
});
const query = params.toString();
const token = createHash("md5").update(query + secret, "utf8").digest("hex");
const requestUrl = `https://api.url2png.com/v6/${apiKey}/${token}/png/?${query}`;
const response = await fetch(requestUrl);
if (!response.ok) throw new Error(`URL2PNG returned HTTP ${response.status}`);
await writeFile("mobile-shot.png", Buffer.from(await response.arrayBuffer()));
5. Pick user-agent and page readiness behavior
When to set a mobile user agent
Start with the narrow viewport. Add user_agent only if the target responds differently according to browser identity, such as serving device-specific markup or behavior. Use a current, appropriate mobile browser user-agent for the scenario you are testing. A historical example copied from an older integration article may no longer represent a current device.
A mobile viewport and mobile user agent are separate variables. If a page still looks like its desktop version at a narrow width, first check whether its CSS is responsive at that width; then investigate whether the server or client selects a separate experience from the user agent.
Wait for dynamic content
Use say_cheese=true when you control the target and can add an element with ID url2png-cheese when the page is ready for capture. Otherwise, use delay for a fixed wait. Neither method guarantees that every third-party widget, animation, or late network response has finished, so choose a delay based on the page’s known behavior and check the resulting image.
6. Choose viewport-only or full-page output
For a screenshot representing what fits on a mobile screen, set fullpage=false. Set fullpage=true to ask URL2PNG to capture the full document canvas. Full-page capture does not determine the page’s width; keep the mobile viewport setting either way.
Long pages can produce tall images and may take longer to render or transfer. Lazy-loaded images may not appear until scrolled into view; the cited URL2PNG documentation does not promise that full-page mode will trigger every lazy-loading implementation. If below-the-fold content matters, verify the output and use a page-specific readiness strategy where possible.
7. Refresh cached screenshots
URL2PNG documents a default cache TTL of 30 days. If you need a fresh render after the page changes, use the unique option with a changing value such as a timestamp. Because this value changes the signed query, regenerate the token after adding it. Avoid varying it on every request when you want cache reuse.
8. Troubleshooting
| Symptom | Likely cause | What to do |
|---|---|---|
| The page still has a desktop layout | The viewport is wide, or the site selects its layout using user-agent detection. | Check the actual viewport value and encoded query. If needed, add a suitable mobile user_agent. |
| The screenshot is narrow but content looks wrong | The width is right, but the page may rely on a different device identity or have a non-responsive layout. | Test the target in a browser at the same width; then decide whether a mobile user agent is needed. |
| Authentication or token error | The token was computed from a different query string, secret, or encoding than the request uses. | Finalize all parameters, encode once, hash the exact encoded query concatenated with the secret, and avoid editing the URL afterward. |
| Image shows stale content | A cached result may be returned within the documented TTL. | Add a changing unique value and regenerate the token. |
| Popup, chart, or embedded content is missing | The content appeared after the capture point or depends on third-party loading. | Use a readiness marker if you control the page; otherwise increase the fixed delay and verify again. |
| Full-page image is unexpectedly tall or absent below the fold | Full-page mode captures the document canvas, but lazy content may require scrolling or additional time. | Confirm fullpage=true, inspect page behavior, and test a suitable readiness strategy. |
| Image output is smaller than expected | thumbnail_max_width may be constraining output dimensions. |
Remove or adjust that output-width option; keep in mind it does not change render width. |
9. Performance, reliability, and cost considerations
- Keep dimensions intentional: Use the viewport height needed for the screen being represented; full-page output can be substantially larger.
- Wait only as long as the page requires: A fixed delay adds time to every capture. A page-controlled readiness marker gives a clearer condition when available.
- Use caching for repeated unchanged captures: The documented default TTL is 30 days. Use
uniqueonly when freshness matters. - Make signing deterministic: Build the final option set, encode it once, then sign that query. This avoids mismatches and makes failures easier to diagnose.
- Protect credentials: The secret is needed to sign requests; perform signing on a trusted server rather than exposing it to site visitors.
- Plan for variable page behavior: Third-party assets, bot checks, redirects, and slow pages can affect a capture. The cited material does not provide a performance benchmark or reliability guarantee, so validate the pages and timing your own workflow depends on.
10. Cloudinary integration
Cloudinary documents URL2PNG as an add-on. In that workflow, select the url2png delivery type and use the website URL as the public ID, then append documented options such as viewport and user agent. Cloudinary describes the resulting screenshots as cached and delivered through its CDN. Its documentation says dynamic screenshot transformation URLs are required to be eagerly generated or signed by default to help control access and avoid unplanned dynamic URL costs. This Cloudinary-specific setup is not required when calling URL2PNG directly.
Or skip the browser setup
ScreenshotNeo provides a screenshot API and MCP server. Its API accepts a URL in one GET request and can return PNG, JPEG, WebP, or PDF. For a mobile-width screenshot, set the viewport width and height explicitly. See the ScreenshotNeo API documentation for options and setup.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
Cookie banners are accepted like a visitor and removed before the shot, along with newsletter popups and chat widgets; each cleanup step can be turned off. Bot checks, blank pages, timeouts, failed loads, and cache hits cost nothing, and response headers report the page verdict and billing status. An MCP server lets AI agents use screenshot tools. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000. Every feature is on every plan.
Sign up free for 1,000 screenshots a month, with no card required.
FAQ
Does a 390-pixel viewport guarantee an iPhone screenshot?
No. It requests a browser viewport width and height. A matching mobile user agent may affect device-specific behavior, but the dimensions alone do not guarantee emulation of every physical device characteristic.
Does fullpage=true make the layout mobile?
No. Set viewport for the layout width and height. fullpage controls capture height.
Can I use URL2PNG’s direct API through Cloudinary?
Cloudinary has a documented add-on workflow for URL2PNG. Follow that path when it suits your existing Cloudinary integration; direct API requests do not require it.


