An API that screenshots any public page
Send a URL or HTML, get an image. Choose the viewport, full page or not, dark mode, and whether to hide cookie banners.
Sign in Full referenceRequest and response
Send JSON with your key in the Authorization header. A good request returns the file itself with a Content-Type, an
X-Render-Usage header such as 12/1000, and X-Render-Cache: hit or miss. A bad one returns
JSON with a code, a plain sentence and a link to the docs.
curl
curl -X POST https://stage-render.starhold.app/v1/screenshot \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"url":"https://example.com","viewport":{"width":1280,"height":800},"format":"png"}' \
-o shot.png
JavaScript (fetch)
import { writeFile } from "node:fs/promises";
const res = await fetch("https://stage-render.starhold.app/v1/screenshot", {
method: "POST",
headers: { Authorization: "Bearer YOUR_API_KEY", "Content-Type": "application/json" },
body: '{"url":"https://example.com","viewport":{"width":1280,"height":800},"format":"png"}',
});
if (!res.ok) throw new Error((await res.json()).error.message);
await writeFile("shot.png", Buffer.from(await res.arrayBuffer()));
Python (requests)
import requests
body = '{"url":"https://example.com","viewport":{"width":1280,"height":800},"format":"png"}'
res = requests.post("https://stage-render.starhold.app/v1/screenshot",
headers={"Authorization": "Bearer YOUR_API_KEY", "Content-Type": "application/json"},
data=body.encode(), timeout=35)
res.raise_for_status()
open("shot.png", "wb").write(res.content)
PHP (curl ext)
<?php
$body = '{"url":"https://example.com","viewport":{"width":1280,"height":800},"format":"png"}';
$ch = curl_init("https://stage-render.starhold.app/v1/screenshot");
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $body,
CURLOPT_HTTPHEADER => ["Authorization: Bearer YOUR_API_KEY", "Content-Type: application/json"],
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 35,
]);
$out = curl_exec($ch);
if (curl_getinfo($ch, CURLINFO_HTTP_CODE) !== 200) { die($out); }
file_put_contents("shot.png", $out);
Go (net/http)
package main
import (
"io"
"net/http"
"os"
"strings"
"time"
)
func main() {
body := `{"url":"https://example.com","viewport":{"width":1280,"height":800},"format":"png"}`
req, _ := http.NewRequest("POST", "https://stage-render.starhold.app/v1/screenshot", strings.NewReader(body))
req.Header.Set("Authorization", "Bearer YOUR_API_KEY")
req.Header.Set("Content-Type", "application/json")
res, err := (&http.Client{Timeout: 35 * time.Second}).Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
if res.StatusCode != 200 {
io.Copy(os.Stderr, res.Body)
os.Exit(1)
}
out, _ := os.Create("shot.png")
io.Copy(out, res.Body)
}
Main options
| Field | What it does |
|---|---|
url or html | exactly one |
viewport, device_scale_factor | 320 to 3840 by 240 to 2160; scale 1, 2 or 3 (width times scale up to 7,680) |
full_page | captures up to 16,384 CSS px tall; longer pages are cut and flagged with X-Render-Truncated: true |
format, quality | png, jpeg or webp; quality 1 to 100 |
dark_mode, block_cookie_banners, block_ads, hide_selectors | page-level switches; up to 20 selectors to hide |
Pricing in short
Free is 50 renders a month. Hobby is $5 for 1,000, Pro is $19 for 10,000 and Scale is $49 for 50,000. Cache hits and failed renders do not count. Full pricing
Questions
Do you follow redirects?
Yes, up to 5, and each hop goes through the same guard, so a redirect to a private address is refused.
What if the page is slow?
A render has 20 seconds. After that you get render_timeout, and nothing is counted against your allowance.
What does the Free plan add?
A small watermark in the bottom-right corner. Paid plans never have one.