Title: Take a website screenshot in Python — working code
Open Graph Title: Take a website screenshot in Python — working code
X Title: Take a website screenshot in Python — working code
Description: Screenshot any website in Python: a full-page PNG from real Chrome, the async variant with a webhook, and retries. Tested code.
Open Graph Description: Screenshot any website in Python: a full-page PNG from real Chrome, the async variant with a webhook, and retries. Tested code.
X Description: Screenshot any website in Python: a full-page PNG from real Chrome, the async variant with a webhook, and retries. Tested code.
Mail addresses
contact@urlpipe.dev
Opengraph URL: https://urlpipe.dev/code/python/screenshot
Domain: urlpipe.dev
{"@context":"https://schema.org","@graph":[{"@type":"Organization","@id":"https://urlpipe.dev/#organization","name":"URLpipe","url":"https://urlpipe.dev","logo":"https://urlpipe.dev/icon.png","sameAs":["https://github.com/URLpipe"],"founder":{"@type":"Person","name":"Roger Campos","url":"https://rogercampos.com"}},{"@type":"WebSite","@id":"https://urlpipe.dev/#website","url":"https://urlpipe.dev","name":"URLpipe","publisher":{"@id":"https://urlpipe.dev/#organization"}}]}
{"@context":"https://schema.org","@graph":[{"@type":"TechArticle","@id":"https://urlpipe.dev/code/python/screenshot#article","headline":"Take a website screenshot in Python — working code","description":"Screenshot any website in Python: a full-page PNG from real Chrome, the async variant with a webhook, and retries. Tested code.","url":"https://urlpipe.dev/code/python/screenshot","mainEntityOfPage":"https://urlpipe.dev/code/python/screenshot","datePublished":"2026-09-24","dateModified":"2026-09-24","author":{"@type":"Person","name":"Roger Campos","url":"https://rogercampos.com"},"publisher":{"@id":"https://urlpipe.dev/#organization"},"image":"https://urlpipe.dev/og-image.png"},{"@type":"FAQPage","mainEntity":[{"@type":"Question","name":"Why is the screenshot response Base64 and not an image?","acceptedAnswer":{"@type":"Answer","text":"So the same body works in JSON, in a webhook payload and in an
data URI. Decode it with base64.b64decode, or skip decoding and use the X-Result-Url header, a direct link to the image."}},{"@type":"Question","name":"How do I take a mobile screenshot?","acceptedAnswer":{"@type":"Answer","text":"Set screenshot_options.viewport_width to a phone width such as 390 and device_scale_factor to 2 or 3. The viewport can be anything from 320 to 1920 pixels wide."}},{"@type":"Question","name":"Can I screenshot one element instead of the whole page?","acceptedAnswer":{"@type":"Answer","text":"Yes: screenshot_options takes a CSS selector and captures just that element. A selector that matches nothing is a 422, and costs nothing."}},{"@type":"Question","name":"Do I need an SDK to call URLpipe from Python?","acceptedAnswer":{"@type":"Answer","text":"No SDK is needed: requests is the only dependency, because the API is one POST per job. If you'd rather use one, the official client installs with pip install urlpipe and adds retries that never bill twice, typed errors and webhook verification."}}]},{"@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https://urlpipe.dev/"},{"@type":"ListItem","position":2,"name":"Code recipes","item":"https://urlpipe.dev/code"},{"@type":"ListItem","position":3,"name":"Python","item":"https://urlpipe.dev/code/python"},{"@type":"ListItem","position":4,"name":"Take a screenshot of a website in Python","item":"https://urlpipe.dev/code/python/screenshot"}]},{"@type":"HowTo","name":"Take a screenshot of a website in Python","description":"To take a screenshot of a website in Python, POST its URL to https://urlpipe.dev/screenshot with sync set to true, decode the Base64 body with base64.b64decode and write the bytes to a .png file. It is a full-page capture from real Chrome for 1 credit; leave sync out and the result goes to your webhook instead.","url":"https://urlpipe.dev/code/python/screenshot","tool":[{"@type":"HowToTool","name":"Python 3.9+"}],"step":[{"@type":"HowToStep","name":"Before you start","text":"One dependency, requests. Put your API key in the environment so it never lands in the file.","url":"https://urlpipe.dev/code/python/screenshot#setup"},{"@type":"HowToStep","name":"Save a screenshot as a PNG file","text":""sync": True holds the connection open until the result is ready and answers with it. Pass timeout= every time: requests has no default, and a page that never finishes loading would otherwise hang your process. The body is the image as Base64 text, so res.text goes through base64.b64decode before it is written in binary mode.","url":"https://urlpipe.dev/code/python/screenshot#sync"},{"@type":"HowToStep","name":"The async variant: a token, a webhook and a poll","text":"Leave out sync and the answer is a token, straight away. for … else is the idiom for "ran out of attempts": the else runs only when the loop never hit break.","url":"https://urlpipe.dev/code/python/screenshot#async"},{"@type":"HowToStep","name":"Handle errors and retries","text":"raise_for_status() is fine for a script; a service wants to tell the failures apart. Check the status before calling res.json() — an error from a proxy in front of the API may not be JSON. sys.exit(message) prints to stderr and exits 1.","url":"https://urlpipe.dev/code/python/screenshot#errors"}]}]}
| view-transition | same-origin |
| csrf-param | authenticity_token |
| csrf-token | ooL0yaQxAFg3V6a-1j3uRY5sfrwCQHydMQpKtde58fS41srUEi-aHiD8l1kWRozdpoBwRoKVbhSYx3mvUEa1Kg |
| csp-nonce | 92a85e6045cb72cb6d1b364da2fa28b4 |
| og:type | website |
| og:site_name | URLpipe |
| og:locale | en_US |
| og:image | https://urlpipe.dev/og-image.png |
| og:image:width | 1200 |
| og:image:height | 630 |
| og:image:alt | URLpipe — URL in. Clean data out. |
| twitter:card | summary_large_image |
| twitter:image | https://urlpipe.dev/og-image.png |
| twitter:image:alt | URLpipe — URL in. Clean data out. |
| theme-color | #3b82f6 |
Links:
Viewport: width=device-width,initial-scale=1