Code examples
Copy-paste snippets for the 5 most common languages.
cURL
curl https://websitescreenshotapi.net/api/v1/screenshot \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{"url":"https://example.com","full_page":true,"block_ads":true}'
PHP
<?php
$ch = curl_init('https://websitescreenshotapi.net/api/v1/screenshot');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('SCREENSHOTAPI_KEY'),
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode([
'url' => 'https://example.com',
'full_page' => true,
]),
CURLOPT_TIMEOUT => 60,
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);
$data = json_decode($body, true);
if ($status === 200) {
echo "OK: {$data['url']} (expires {$data['expires_at']})\n";
} else {
echo "ERROR [{$data['error']}]: {$data['message']}\n";
}
Python (requests)
import os
import requests
API_KEY = os.environ["SCREENSHOTAPI_KEY"]
r = requests.post(
"https://websitescreenshotapi.net/api/v1/screenshot",
headers={"Authorization": f"Bearer {API_KEY}"},
json={"url": "https://example.com", "full_page": True, "block_ads": True},
timeout=60,
)
data = r.json()
if r.ok:
print(f"OK: {data['url']} (expires {data['expires_at']})")
else:
print(f"ERROR [{data['error']}]: {data['message']}")
Python with retries (production-ready)
import time
from requests import Session
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
session = Session()
session.headers.update({"Authorization": f"Bearer {API_KEY}"})
session.mount("https://", HTTPAdapter(max_retries=Retry(
total=3, backoff_factor=1, status_forcelist=[502, 503, 504],
)))
def screenshot(url, **opts):
body = {"url": url, **opts}
r = session.post("https://websitescreenshotapi.net/api/v1/screenshot", json=body, timeout=60)
if r.status_code == 429:
time.sleep(int(r.headers.get("Retry-After", 5)))
return screenshot(url, **opts)
r.raise_for_status()
return r.json()
Node.js (fetch / native)
const API_KEY = process.env.SCREENSHOTAPI_KEY;
async function screenshot(url, opts = {}) {
const res = await fetch('https://websitescreenshotapi.net/api/v1/screenshot', {
method: 'POST',
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ url, ...opts }),
});
if (res.status === 429) {
const retry = parseInt(res.headers.get('Retry-After') || '5', 10);
await new Promise(r => setTimeout(r, retry * 1000));
return screenshot(url, opts);
}
if (!res.ok) {
const err = await res.json().catch(() => ({}));
throw new Error(`${err.error}: ${err.message}`);
}
return res.json();
}
const data = await screenshot('https://example.com', { full_page: true });
console.log(data.url, data.size_bytes, 'bytes');
Ruby
require 'net/http'
require 'json'
API_KEY = ENV.fetch('SCREENSHOTAPI_KEY')
uri = URI('https://websitescreenshotapi.net/api/v1/screenshot')
req = Net::HTTP::Post.new(uri, {
'Authorization' => "Bearer #{API_KEY}",
'Content-Type' => 'application/json',
})
req.body = { url: 'https://example.com', full_page: true }.to_json
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) }
data = JSON.parse(res.body)
if res.code == '200'
puts "OK: #{data['url']}"
else
puts "ERROR [#{data['error']}]: #{data['message']}"
end
Go
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"time"
)
type Resp struct {
URL string `json:"url"`
ExpiresAt string `json:"expires_at"`
Format string `json:"format"`
SizeBytes int `json:"size_bytes"`
Width int `json:"width"`
Height int `json:"height"`
DurationMs int `json:"duration_ms"`
CreditsRemaining int `json:"credits_remaining"`
}
func screenshot(url string) (*Resp, error) {
body, _ := json.Marshal(map[string]any{
"url": url,
"full_page": true,
})
req, _ := http.NewRequest("POST", "https://websitescreenshotapi.net/api/v1/screenshot", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer "+os.Getenv("SCREENSHOTAPI_KEY"))
req.Header.Set("Content-Type", "application/json")
client := &http.Client{Timeout: 60 * time.Second}
res, err := client.Do(req)
if err != nil { return nil, err }
defer res.Body.Close()
raw, _ := io.ReadAll(res.Body)
if res.StatusCode != 200 {
return nil, fmt.Errorf("HTTP %d: %s", res.StatusCode, raw)
}
var v Resp
json.Unmarshal(raw, &v)
return &v, nil
}
func main() {
v, err := screenshot("https://example.com")
if err != nil { panic(err) }
fmt.Printf("%+v\n", v)
}
Async batch (Python)
Submit ≤ 500 URLs at once and poll or wait for the webhook:
import requests, os, time
API_KEY = os.environ["SCREENSHOTAPI_KEY"]
r = requests.post(
"https://websitescreenshotapi.net/api/v1/bulk",
headers={"Authorization": f"Bearer {API_KEY}"},
json={
"urls": ["https://example.com", "https://github.com", "https://stackoverflow.com"],
"format": "png",
"block_ads": True,
"viewport_width": 1280,
"webhook_url": "https://yoursite.com/wh/batch",
"webhook_secret": "shared-secret-32-chars",
},
timeout=10,
)
batch_id = r.json()["batch_id"]
print(f"Batch queued: {batch_id}")
# Poll until done
while True:
s = requests.get(
f"https://websitescreenshotapi.net/api/v1/batch/{batch_id}?include=results",
headers={"Authorization": f"Bearer {API_KEY}"},
).json()
if s["status"] in ("completed", "failed"):
break
print(f" {s['processed']}/{s['total']} done")
time.sleep(2)
for r in s["results"]:
print(f" {r['input_url']} → {r['url']}")
Webhook signature verification
When you supply webhook_secret, we sign callbacks with HMAC-SHA256.
import hmac, hashlib
def verify_signature(raw_body: bytes, sig_header: str, secret: str) -> bool:
expected = "sha256=" + hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, sig_header)
import crypto from 'node:crypto';
export function verify(rawBody, sigHeader, secret) {
const expected = 'sha256=' + crypto.createHmac('sha256', secret)
.update(rawBody).digest('hex');
return crypto.timingSafeEqual(Buffer.from(sigHeader), Buffer.from(expected));
}
The signature is in the X-ScreenshotAPI-Signature header. The body is the raw POST body — don't re-serialize.
More
- Need an SDK? We don't ship one — REST is enough. If you want one, contact us.
- Postman collection
- OpenAPI 3.0 spec