Sometimes you just need a PNG of a webpage from a script. A CI job that attaches a render of the deploy preview to every build. A cron task that snapshots a status dashboard every morning. A monitoring check that grabs the marketing homepage so you can eyeball it later. None of that should require installing Chromium, wiring up Puppeteer, or babysitting a headless browser on a build agent.
The whole thing is one HTTP request. You hand a URL to the API, it drives a real browser on its side, and you get an image back. Below are the patterns I reach for, from a single curl line to small Node and Python snippets you can drop into a script.
One cURL call
The endpoint is https://screenshotink.com/v1/capture. Pass it a URL and a format, authenticate with a bearer token, and you get JSON back with a link to the rendered image:
curl "https://screenshotink.com/v1/capture" \
-H "Authorization: Bearer sk_live_…" \
-d url="https://github.com" -d format=png
The response looks like this:
{ "image_url": "https://screenshotink.com/i/9f2a…png" }
That image_url is a hosted copy of the capture. You can open it, embed it, or download it. For a one-off check this is all you need.
Save the image straight to disk
Most of the time you don't want the JSON — you want a file on disk. Pipe the response through jq to pull out image_url, then hand that to a second curl to download the bytes:
curl -s "https://screenshotink.com/v1/capture" \
-H "Authorization: Bearer sk_live_…" \
-d url="https://github.com" -d format=png \
| jq -r '.image_url' \
| xargs curl -s -o github.png
echo "saved github.png"
-s keeps curl quiet, jq -r prints the raw URL without quotes, and xargs feeds it to the download call. Swap github.png for whatever filename your script needs — a timestamp, a build number, a commit SHA.
A reusable shell function
If you do this often, wrap it in a shell function and drop it in your .bashrc or .zshrc. Pass a URL, get a file named after the host:
shot() {
local url="$1"
local name="${2:-$(echo "$url" | sed -E 's#https?://##; s#/.*##').png}"
curl -s "https://screenshotink.com/v1/capture" \
-H "Authorization: Bearer $SCREENSHOTINK_KEY" \
-d url="$url" -d format=png \
| jq -r '.image_url' \
| xargs curl -s -o "$name"
echo "saved $name"
}
Put your token in SCREENSHOTINK_KEY once (export SCREENSHOTINK_KEY=sk_live_…) and then it's just:
shot https://github.com
shot https://news.ycombinator.com hn.png
In a script (Node / Python)
Inside a real program you'll usually skip the shell entirely. Here's the same call in Node, writing the file with fetch (built in since Node 18):
const res = await fetch("https://screenshotink.com/v1/capture", {
method: "POST",
headers: {
"Authorization": "Bearer sk_live_…",
"Content-Type": "application/x-www-form-urlencoded",
},
body: new URLSearchParams({ url: "https://github.com", format: "png" }),
});
const { image_url } = await res.json();
const img = await fetch(image_url);
await Bun.write?.("github.png", img) ??
require("fs").writeFileSync("github.png", Buffer.from(await img.arrayBuffer()));
And in Python with requests:
import requests
res = requests.post(
"https://screenshotink.com/v1/capture",
headers={"Authorization": "Bearer sk_live_…"},
data={"url": "https://github.com", "format": "png"},
)
image_url = res.json()["image_url"]
img = requests.get(image_url)
with open("github.png", "wb") as f:
f.write(img.content)
Both follow the same two-step shape as the shell version: post the capture request, read image_url from the JSON, fetch the bytes.
Useful options
The request takes a handful of parameters that cover the cases you'll actually hit:
full_size=true— capture the entire scrollable page, not just the viewport. Good for archiving a whole article or a long landing page. There's a dedicated full-page screenshot tool too.width=— set the browser width in pixels to render at a specific breakpoint.width=390gives you a phone-sized layout,width=1440a desktop one.format=—png,jpeg, orpdf. PDF is handy when you want a printable, vector-ish copy of a page.max_height=— cap the height of a full-page capture so a runaway infinite-scroll page doesn't produce a 40,000-pixel image.
A few things happen on their own so you don't have to script around them: lazy-loaded images are scrolled into view before the shot is taken, and common cookie-consent banners are dismissed automatically so they don't sit on top of your capture. Rendering runs on EU-hosted infrastructure. The free tier is 100 captures a month with no card required, which is plenty for a personal cron job or a small CI pipeline.
That's the whole surface. One endpoint, a token, a URL — everything else is just choosing where to write the file.
Prefer a UI? website screenshot tool.
Capture it with the API
Everything here runs on the ScreenshotInk API — 100 free captures a month, no card.