Screenshot API — POST a URL, get an image back
Shot renders any public URL to a PNG, JPEG or WebP — full page, a single element, ads blocked. No SDK, no browser to manage. Free 20 renders a day, no credit card.
Quick answer: Shot renders any public URL to a PNG, JPEG or WebP — full page or a single element, ads blocked. Free 20 renders a day, no credit card.
POST /v1/screenshot.POST /v1/screenshotSix behaviours, each one doing exactly what it says — no SDK, no browser to manage.
Scrolls the page step by step to trigger lazy loading, then captures the whole document — not just the viewport.
Pass a CSS selector and get only that element — a chart, a card, a hero section.
Trackers and ad networks are blocked by default, so captures are clean and faster.
Repeat renders of the same URL are served from a 1-hour cache. Typical render: under 2 seconds.
Private IPs, localhost, cloud metadata endpoints and redirect tricks are all blocked. No anonymous endpoint.
One endpoint, one Bearer YOUR_KEY, image bytes back. JSON metadata mode with Accept: application/json.
One endpoint. Synchronous, with image bytes or JSON metadata back.
https://shot.clioos.com/v1/screenshot30s hard timeoutcurl -X POST https://shot.clioos.com/v1/screenshot \
-H "Authorization: Bearer YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"url": "https://example.com", "full_page": true}' \
--output shot.pngimport requests
r = requests.post(
"https://shot.clioos.com/v1/screenshot",
headers={"Authorization": "Bearer YOUR_KEY"},
json={"url": "https://example.com", "full_page": True},
timeout=60,
)
r.raise_for_status()
open("shot.png", "wb").write(r.content)const res = await fetch("https://shot.clioos.com/v1/screenshot", {
method: "POST",
headers: {
"Authorization": "Bearer YOUR_KEY",
"Content-Type": "application/json",
},
body: JSON.stringify({ url: "https://example.com", full_page: true }),
});
if (!res.ok) throw new Error(`Shot failed: ${res.status}`);
const { writeFileSync } = await import("node:fs");
writeFileSync("shot.png", Buffer.from(await res.arrayBuffer()));package main
import (
"bytes"
"encoding/json"
"net/http"
"os"
)
func main() {
body, _ := json.Marshal(map[string]any{
"url": "https://example.com", "full_page": true,
})
req, _ := http.NewRequest("POST",
"https://shot.clioos.com/v1/screenshot", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer YOUR_KEY")
req.Header.Set("Content-Type", "application/json")
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
if res.StatusCode != http.StatusOK {
panic("Shot failed: " + res.Status)
}
f, _ := os.Create("shot.png")
defer f.Close()
buf := new(bytes.Buffer)
buf.ReadFrom(res.Body)
f.Write(buf.Bytes())
}| Parameter | Default | Notes |
|---|---|---|
url | — | Required. http/https only. |
width / height | 1280 / 800 | width 320–2000, height 200–4000. |
full_page | true | Capture the whole page. |
format | png | png / jpeg / webp. jpeg & webp take quality 10–100. |
device_scale_factor | 1 | 1–2 (retina). |
selector | — | CSS selector: capture only that element. |
delay | 1s | Extra wait after load, 0–5s. |
block_ads | true | Block ad/tracker requests. |
dark_mode | false | Prefer dark color scheme. |
Responses carry X-Render-Ms, X-Cache: HIT/MISS and X-RateLimit-Remaining. Errors are JSON.
| Status | Meaning |
|---|---|
| 200 | Image bytes (or JSON metadata with Accept: application/json). |
| 400 | Bad parameter. |
| 401 | Bad key. |
| 402 | Daily limit reached. |
| 422 | Unsafe URL. |
| 429 | Rate limited (with Retry-After). |
| 504 | Render timeout. |
| 502 | Target unreachable (upstream status included). |
| Limit | Value |
|---|---|
| Per key, per minute | 5 requests |
| Per key, per day | 20 renders |
| Per IP, per day | 50 renders |
| Cache | 1 hour — cached repeats do not consume quota |
We do not run JavaScript you send us, and we do not accept custom headers or cookies.
Probe period: free. Every key gets 20 renders/day, no credit card, no expiry while the probe runs. Paid packs are not on sale yet — the probe is measuring what people actually need before we set prices.
If you would pay for a larger pack, tell us below. It only registers interest — no payment, no commitment.
Register interest only — no payment is taken, and paid packs do not exist yet.
Every free key gets 20 renders per day, plus a 50 renders per day limit per IP. No credit card is required while the probe runs.
PNG, JPEG and WebP. JPEG and WebP take a quality setting from 10 to 100. You can capture the full page or a single element with a CSS selector.
Yes. The same URL and parameters are served from a 1-hour cache, and cached renders do not consume your daily quota.
Yes: 5 requests per minute per key, 20 renders per day per key, and 50 renders per day per IP. Over the limit you get a 429 or 402 response with a Retry-After header.
No. Private IPs, localhost, cloud metadata endpoints and redirect tricks are blocked. Only public http/https URLs are rendered.
Your email is used only to send your verification code and API key, and to tell you when paid packs launch. It is never sold or shared.
Render logs (the URL you asked for, when, and whether it succeeded) are kept for quota enforcement and statistics while the probe runs. Cached screenshots expire after 1 hour. Expired unused verification codes are deleted automatically.
To delete your account, API key and render history, email contact@clioos.com from the same address and we will remove them.