Guides
Code examples
Working screenshot API snippets for cURL, Node.js, PHP, Python, Go and Ruby.
Every example captures https://example.com and saves the result. Keep your key in an
environment variable, never in source control.
Each snippet here is the short version. The three most-used languages have a full guide covering streaming, batching, queued renders and the failure modes worth handling:
Node.js
fetch, streams, backoff on 429.
Python
requests, httpx, bounded concurrency.
PHP
Laravel Http, cURL, signed webhooks.
cURL
curl -G https://ssnap.cc/api/v1/screenshot \
-H "Authorization: Bearer $SSNAP_API_KEY" \
--data-urlencode "url=https://example.com" \
--data-urlencode "format=png" \
--data-urlencode "full_page=true" \
--output example.pngcurl -X POST https://ssnap.cc/api/v1/screenshot \
-H "Authorization: Bearer $SSNAP_API_KEY" \
-H "Content-Type: application/json" \
-d '{"url":"https://example.com","response":"url","cache_ttl":3600}'Node.js
import { writeFile } from 'node:fs/promises';
const response = await fetch('https://ssnap.cc/api/v1/screenshot', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.SSNAP_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
url: 'https://example.com',
format: 'png',
full_page: true,
cache_ttl: 3600,
}),
});
if (!response.ok) {
// Errors are always JSON, whatever the requested output format.
const { error, code } = await response.json();
throw new Error(`ssnap ${response.status} ${code}: ${error}`);
}
console.log('cached:', response.headers.get('x-screenshot-cached'));
await writeFile('example.png', Buffer.from(await response.arrayBuffer()));const response = await fetch('https://ssnap.cc/api/v1/screenshot', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.SSNAP_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ url: 'https://example.com', response: 'url' }),
});
const { url, cached } = await response.json();PHP
<?php
$response = Http::withToken(env('SSNAP_API_KEY'))
->post('https://ssnap.cc/api/v1/screenshot', [
'url' => 'https://example.com',
'format' => 'png',
'full_page' => true,
'cache_ttl' => 3600,
]);
if ($response->failed()) {
throw new RuntimeException($response->json('code') . ': ' . $response->json('error'));
}
file_put_contents('example.png', $response->body());<?php
$ch = curl_init('https://ssnap.cc/api/v1/screenshot');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('SSNAP_API_KEY'),
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode([
'url' => 'https://example.com',
'format' => 'png',
]),
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);
if ($status !== 200) {
throw new RuntimeException($body);
}
file_put_contents('example.png', $body);Python
import os
import requests
response = requests.post(
"https://ssnap.cc/api/v1/screenshot",
headers={"Authorization": f"Bearer {os.environ['SSNAP_API_KEY']}"},
json={
"url": "https://example.com",
"format": "png",
"full_page": True,
"cache_ttl": 3600,
},
timeout=60,
)
if response.status_code != 200:
payload = response.json()
raise RuntimeError(f"ssnap {payload.get('code')}: {payload.get('error')}")
with open("example.png", "wb") as file:
file.write(response.content)Go
package main
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"os"
)
func main() {
body, _ := json.Marshal(map[string]any{
"url": "https://example.com",
"format": "png",
"full_page": true,
})
req, _ := http.NewRequest("POST", "https://ssnap.cc/api/v1/screenshot", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer "+os.Getenv("SSNAP_API_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(fmt.Sprintf("ssnap returned %d", res.StatusCode))
}
file, _ := os.Create("example.png")
defer file.Close()
file.ReadFrom(res.Body)
}Ruby
require "net/http"
require "json"
uri = URI("https://ssnap.cc/api/v1/screenshot")
request = Net::HTTP::Post.new(uri)
request["Authorization"] = "Bearer #{ENV.fetch('SSNAP_API_KEY')}"
request["Content-Type"] = "application/json"
request.body = { url: "https://example.com", format: "png", full_page: true }.to_json
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
http.request(request)
end
raise response.body unless response.code == "200"
File.binwrite("example.png", response.body)Handling rate limits
async function capture(params, attempt = 0) {
const response = await fetch('https://ssnap.cc/api/v1/screenshot', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.SSNAP_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(params),
});
if (response.status === 429) {
const { code } = await response.clone().json();
// A monthly quota does not reset within a retry window, so do not loop on it.
if (code === 'quota_exceeded' || attempt >= 3) return response;
const wait = Number(response.headers.get('retry-after') ?? 5);
await new Promise((resolve) => setTimeout(resolve, wait * 1000));
return capture(params, attempt + 1);
}
return response;
}See API error codes for which codes are worth retrying, and Rate limits and quotas for the two limits this guards against.