Exemples d'intégration
Copiez-collez le code dans votre langage. Remplacez VOTRE_CLÉ_API par votre clé API (12 caractères, gratuite).
PHP
Capture simple (hébergée)
<?php
$api_key = "VOTRE_CLÉ_API";
$url = "https://example.com";
// 1. Soumettre
$ch = curl_init("https://api.shotbot.net/capture");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => ["Content-Type: application/json"],
CURLOPT_POSTFIELDS => json_encode([
"key" => $api_key, "url" => $url, "format" => "webp",
]),
]);
$res = json_decode(curl_exec($ch), true);
curl_close($ch);
if (empty($res["token"])) {
exit("Erreur : " . ($res["error"] ?? "inconnue"));
}
$token = $res["token"];
// 2. Attendre (polling)
do {
sleep(3);
$status = json_decode(file_get_contents(
"https://api.shotbot.net/capture/" . $token
), true);
} while (($status["status"] ?? "") !== "done");
// 3. Utiliser l'image
echo "<img src=\"" . htmlspecialchars($status["image"]) . "\" loading=\"lazy\">";
Carte sociale en 1 ligne (preset)
Le paramètre preset remplit automatiquement viewport_width,
output_size et crop_height. Presets disponibles :
og (1200×630), mobile (390×844), youtube_thumbnail (1280×720),
square (1080×1080), reel (1080×1920), pinterest (1000×1500),
tablet (768×1024), desktop_hd, twitter_header,
linkedin_banner, hero_banner (4 derniers réservés Pro).
<?php
$ch = curl_init("https://api.shotbot.net/capture");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => ["Content-Type: application/json"],
CURLOPT_POSTFIELDS => json_encode([
"key" => "VOTRE_CLÉ_API",
"url" => "https://example.com",
"preset" => "og", // 1200×630, carte OpenGraph
]),
]);
$res = json_decode(curl_exec($ch), true);
curl_close($ch);
echo $res["token"];
Capture avec options supplémentaires
<?php
$ch = curl_init("https://api.shotbot.net/capture");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => ["Content-Type: application/json"],
CURLOPT_POSTFIELDS => json_encode([
"key" => "VOTRE_CLÉ_API",
"url" => "https://example.com",
"format" => "webp",
"viewport_width" => 1280,
"output_size" => 640,
"ratio" => "16:9",
"hidpi" => true,
"color_scheme" => "dark",
"render_region" => "ca-montreal", // Pro : fr-paris (défaut) | ca-montreal | sg-singapore | au-sydney | vn-hanoi
"fullpage" => false,
]),
]);
$res = json_decode(curl_exec($ch), true);
curl_close($ch);
echo $res["token"]; // token pour suivre le statut
Mode Callback (marque blanche)
Pour vérifier que le POST entrant provient bien de Shotbot, fournissez un
callback_secret à la soumission : Shotbot le renverra tel quel
dans le POST de réception (champ callback_secret).
<?php
// Soumettre une capture callback
$ch = curl_init("https://api.shotbot.net/capture/callback");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => ["Content-Type: application/json"],
CURLOPT_POSTFIELDS => json_encode([
"key" => "VOTRE_CLÉ_API",
"url" => "https://example.com",
"callback_url" => "https://votre-site.com/shotbot-callback.php",
"callback_secret" => "un-secret-aléatoire",
"format" => "webp",
"viewport_width" => 1280,
"ratio" => "16:9",
]),
]);
$res = json_decode(curl_exec($ch), true);
curl_close($ch);
// $res["token"] disponible pour suivi optionnel
<?php
// Shotbot envoie un POST multipart quand la capture est prête.
// Champs disponibles : token, url, status, format, callback_secret
// Fichier : $_FILES["file"] contient l'image
$EXPECTED_SECRET = "un-secret-aléatoire"; // identique à callback_secret envoyé
if (!hash_equals($EXPECTED_SECRET, $_POST["callback_secret"] ?? "")) {
http_response_code(403);
exit("Forbidden");
}
if (($_POST["status"] ?? "") !== "OK" || !isset($_FILES["file"])) {
http_response_code(400);
exit;
}
$token = preg_replace("/[^A-Za-z0-9]/", "", (string)($_POST["token"] ?? ""));
$ext = preg_replace("/[^a-z]/", "", (string)($_POST["format"] ?? "webp"));
if ($token === "") {
http_response_code(400);
exit;
}
$dest = __DIR__ . "/shots/{$token}.{$ext}";
move_uploaded_file($_FILES["file"]["tmp_name"], $dest);
// Enregistrer en base, notifier un webhook, etc.
http_response_code(200);
echo "OK";
Python
Capture simple
import requests, time
API_KEY = "VOTRE_CLÉ_API"
url = "https://example.com"
# 1. Soumettre
res = requests.post("https://api.shotbot.net/capture", json={
"key": API_KEY, "url": url, "format": "webp",
"viewport_width": 1280, "ratio": "16:9",
})
res.raise_for_status()
token = res.json()["token"]
# 2. Attendre
while True:
time.sleep(2)
status = requests.get(f"https://api.shotbot.net/capture/{token}").json()
if status["status"] == "done":
print(status["image"])
break
Carte sociale en 1 ligne (preset)
import requests
res = requests.post("https://api.shotbot.net/capture", json={
"key": "VOTRE_CLÉ_API",
"url": "https://example.com",
"preset": "og", # 1200x630, carte OpenGraph
})
token = res.json()["token"]
Télécharger la capture
import requests, time
API_KEY = "VOTRE_CLÉ_API"
url = "https://example.com"
res = requests.post("https://api.shotbot.net/capture", json={"key": API_KEY, "url": url})
token = res.json()["token"]
while True:
time.sleep(2)
s = requests.get(f"https://api.shotbot.net/capture/{token}").json()
if s["status"] == "done": break
img_data = requests.get(s["image"]).content
with open("capture.webp", "wb") as f:
f.write(img_data)
print("Capture enregistrée.")
Node.js
Capture et polling (ESM)
const API_KEY = "VOTRE_CLÉ_API";
const url = "https://example.com";
// 1. Soumettre
const res = await fetch("https://api.shotbot.net/capture", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ key: API_KEY, url, format: "webp", viewport_width: 1280 }),
});
const { token } = await res.json();
// 2. Attendre
const wait = (ms) => new Promise((r) => setTimeout(r, ms));
let image;
while (true) {
await wait(2000);
const s = await fetch(`https://api.shotbot.net/capture/${token}`).then((r) => r.json());
if (s.status === "done") { image = s.image; break; }
}
console.log(image);
Carte sociale en 1 ligne (preset)
const res = await fetch("https://api.shotbot.net/capture", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
key: "VOTRE_CLÉ_API",
url: "https://example.com",
preset: "og", // 1200×630, carte OpenGraph
}),
});
const { token } = await res.json();
Polling côté navigateur
<!-- Votre backend soumet la capture et stocke le token.
Ce script surveille le statut et met à jour l'image quand c'est prêt. -->
<script>
async function waitForCapture(token, imgEl) {
while (true) {
await new Promise((r) => setTimeout(r, 2000));
const s = await fetch(`https://api.shotbot.net/capture/${token}`).then((r) => r.json());
if (s.status === "done") { imgEl.src = s.image; return; }
}
}
const img = document.getElementById("shot");
if (img) waitForCapture(img.dataset.capture, img);
</script>
<img id="shot" data-capture="a3f1c9…" alt="Screenshot" loading="lazy">
Go
Capture simple
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"time"
)
const apiKey = "VOTRE_CLÉ_API"
func main() {
// 1. Soumettre
payload, _ := json.Marshal(map[string]any{
"key": apiKey,
"url": "https://example.com",
"format": "webp",
})
resp, _ := http.Post("https://api.shotbot.net/capture",
"application/json", bytes.NewReader(payload))
defer resp.Body.Close()
var capture struct {
Token string `json:"token"`
}
json.NewDecoder(resp.Body).Decode(&capture)
// 2. Attendre
for {
time.Sleep(2 * time.Second)
r, _ := http.Get("https://api.shotbot.net/capture/" + capture.Token)
body, _ := io.ReadAll(r.Body)
r.Body.Close()
var s map[string]any
json.Unmarshal(body, &s)
if s["status"] == "done" {
fmt.Println(s["image"])
return
}
}
}
Mode Callback
payload, _ := json.Marshal(map[string]any{
"key": apiKey,
"url": "https://example.com",
"callback_url": "https://votre-site.com/shotbot-callback",
"callback_secret": "un-secret-aléatoire",
"format": "webp",
"ratio": "16:9",
})
http.Post("https://api.shotbot.net/capture/callback",
"application/json", bytes.NewReader(payload))
const expectedSecret = "un-secret-aléatoire"
http.HandleFunc("/shotbot-callback", func(w http.ResponseWriter, r *http.Request) {
r.ParseMultipartForm(32 << 20) // 32 MB max
// Vérification du secret partagé
if subtle.ConstantTimeCompare(
[]byte(r.FormValue("callback_secret")),
[]byte(expectedSecret),
) != 1 {
http.Error(w, "forbidden", http.StatusForbidden)
return
}
if r.FormValue("status") != "OK" {
http.Error(w, "not OK", http.StatusBadRequest)
return
}
token := r.FormValue("token")
ext := r.FormValue("format")
file, _, err := r.FormFile("file")
if err != nil {
http.Error(w, "no file", http.StatusBadRequest)
return
}
defer file.Close()
dst, _ := os.Create("shots/" + token + "." + ext)
defer dst.Close()
io.Copy(dst, file)
fmt.Fprintln(w, "OK")
})
cURL
Capture simple
# 1. Soumettre (retourne le token)
curl -s -X POST https://api.shotbot.net/capture \
-H "Content-Type: application/json" \
-d '{"key":"VOTRE_CLÉ_API","url":"https://example.com","format":"webp"}'
# {"token":"a3f1c9...","status":"queued","eta_seconds":20}
# 2. Vérifier le statut
curl -s "https://api.shotbot.net/capture/a3f1c9..."
# {"status":"done","image":"https://static.shotbot.net/..."}
# 3. Télécharger
curl -o capture.webp "https://static.shotbot.net/a/a3/a3f1c9….webp"
Carte sociale en 1 ligne (preset)
# preset=og remplit automatiquement viewport_width / output_size / crop_height (1200×630).
curl -s -X POST https://api.shotbot.net/capture \
-H "Content-Type: application/json" \
-d '{"key":"VOTRE_CLÉ_API","url":"https://example.com","preset":"og"}'
Cadre décoratif autour de l'image (frame)
Paramètre frame | composite un cadre côté serveur autour de l'image.
Variantes : brackets, shadow, browser_chrome,
mobile (mockup smartphone), tablet, shotbot_brand.
Ignoré pour PDF. Les comptes gratuits reçoivent une marque shotbot.fr en bas
à droite (sauf shotbot_brand, intrinsèquement marqué) | retirée avec Shotbot Pro.
# Carte OG (1200×630) avec ombre portée
curl -s -X POST https://api.shotbot.net/capture \
-H "Content-Type: application/json" \
-d '{"key":"VOTRE_CLÉ_API","url":"https://example.com","preset":"og","frame":"shadow"}'
# Capture mobile dans un mockup smartphone (idéal pour landings d'app mobile)
curl -s -X POST https://api.shotbot.net/capture \
-H "Content-Type: application/json" \
-d '{"key":"VOTRE_CLÉ_API","url":"https://example.com","preset":"mobile","frame":"mobile"}'
Avec options supplémentaires
curl -s -X POST https://api.shotbot.net/capture \
-H "Content-Type: application/json" \
-d '{"key":"VOTRE_CLÉ_API","url":"https://example.com",
"format":"avif","viewport_width":390,"ratio":"9:16",
"hidpi":true,"color_scheme":"dark",
"block_ads":true,"dismiss_cookies":"reject","scroll_before_capture":true,
"scroll_offset_px":600,"wait_time":25}'
Mode Callback
curl -s -X POST https://api.shotbot.net/capture/callback \
-H "Content-Type: application/json" \
-d '{"key":"VOTRE_CLÉ_API","url":"https://example.com",
"callback_url":"https://votre-site.com/callback",
"callback_secret":"un-secret-aléatoire",
"format":"webp","ratio":"16:9"}'
Sortie PDF
# Document PDF A4 portrait, marge 10 mm
curl -s -X POST https://api.shotbot.net/capture \
-H "Content-Type: application/json" \
-d '{"key":"VOTRE_CLÉ_API","url":"https://example.com/facture/12345",
"format":"pdf","pdf_page_size":"A4","pdf_margin_mm":10,
"pdf_scale":1.00,"pdf_landscape":false}'
# Récupérer le PDF une fois prêt :
# https://static.shotbot.net/{t1}/{t2}/{token}.pdf
Capture privée (jamais sur le CDN)
Passez "private":true quand la page capturée est sensible
(intranet, espaces clients, tableaux de bord). Le résultat n'est pas
publié sur static.shotbot.net : la réponse de statut
expose une URL download qui diffuse le fichier. Vous pouvez
le récupérer autant de fois que voulu jusqu'à son expiration automatique,
après quoi l'URL retourne 410 Gone.
# 1. Soumettre avec private:true
TOKEN=$(curl -s -X POST https://api.shotbot.net/capture \
-H "Content-Type: application/json" \
-d '{"key":"VOTRE_CLÉ_API","url":"https://intranet.example.com",
"format":"webp","private":true}' | jq -r .token)
# 2. Polling | la réponse "done" contient `download`, pas `image`/`preview`
curl -s "https://api.shotbot.net/capture/$TOKEN"
# {"status":"done","private":true,
# "download":"https://api.shotbot.net/capture/.../file", ...}
# 3. Récupérer les octets (répétable jusqu'à l'expiration automatique côté serveur)
curl -o capture.webp "https://api.shotbot.net/capture/$TOKEN/file"
Batch : jusqu'à 500 URLs en une requête
L'endpoint batch traite jusqu'à 500 URLs en une seule requête (5 000 avec Shotbot Pro). Les options globales s'appliquent à toutes les captures ; chaque URL peut y définir ses propres options. La déduplication automatique évite de recompter deux fois la même URL soumise dans le même batch.
Équité entre clients : la file batch (Q3) utilise un planificateur fair-share : à chaque slot libre, le worker choisit le job le plus ancien parmi le client avec le moins de captures en cours. Un grand batch ne monopolise pas tous les workers ; les autres clients continuent d'avancer en parallèle. Pour une file réservée sans partage, voir Shotbot Dédié.
Limite de taille de page : les pages dont le document HTML dépasse 10 Mo (20 Mo
avec Shotbot Pro) sont automatiquement rejetées avec le code response_too_large. Le job échoue en
quelques secondes plutôt qu'après 30 s de timeout, préservant les ressources des workers.
PHP : batch simple
<?php
$ch = curl_init("https://api.shotbot.net/capture/batch");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => ["Content-Type: application/json"],
CURLOPT_POSTFIELDS => json_encode([
"key" => "VOTRE_CLÉ_API",
"format" => "webp", // défaut pour toutes les captures
"viewport_width" => 1280,
"ratio" => "16:9",
"jobs" => [
["url" => "https://example.com"],
["url" => "https://example.org"],
["url" => "https://example.net"],
],
]),
]);
$res = json_decode(curl_exec($ch), true);
curl_close($ch);
echo "Soumis : " . $res["submitted"] . "\n";
echo "Dédupliqués : " . $res["deduplicated"] . "\n";
echo "En attente : " . $res["waitlisted"] . "\n";
foreach ($res["jobs"] as $capture) {
echo $capture["url"] . " → " . $capture["token"] . "\n";
}
PHP : avec options par capture et callback
<?php
$urls = [
["url" => "https://boutique.example.com/produit-1"],
["url" => "https://boutique.example.com/produit-2", "viewport_width" => 390, "ratio" => "9:16"],
["url" => "https://boutique.example.com/produit-3", "format" => "avif"],
// ... jusqu'à 500
];
$ch = curl_init("https://api.shotbot.net/capture/batch");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => ["Content-Type: application/json"],
CURLOPT_POSTFIELDS => json_encode([
"key" => "VOTRE_CLÉ_API",
"format" => "webp", // défaut surchargeable par capture
"callback_url" => "https://votre-site.com/batch-callback",
"callback_secret" => "un-secret-aléatoire",
"jobs" => $urls,
]),
]);
$res = json_decode(curl_exec($ch), true);
curl_close($ch);
// Shotbot notifie callback_url pour chaque capture terminée.
// Le champ callback_secret est renvoyé tel quel pour vérifier l'origine.
Python : batch
import requests
res = requests.post("https://api.shotbot.net/capture/batch", json={
"key": "VOTRE_CLÉ_API",
"format": "webp",
"jobs": [
{"url": "https://example.com"},
{"url": "https://example.org", "viewport_width": 390},
{"url": "https://example.net", "hidpi": True},
],
})
data = res.json()
print(f"Soumis : {data['submitted']} | Dédupliqués : {data['deduplicated']}")
for capture in data["jobs"]:
print(capture["url"], "→", capture["token"])
cURL : batch
curl -s -X POST https://api.shotbot.net/capture/batch \
-H "Content-Type: application/json" \
-d '{"key":"VOTRE_CLÉ_API","format":"webp","jobs":[
{"url":"https://example.com"},
{"url":"https://example.org"},
{"url":"https://example.net"}
]}'
# {"submitted":3,"waitlisted":0,"deduplicated":0,"errors":[],"jobs":[...]}
Format de réponse batch
{
"submitted": 3,
"waitlisted": 0,
"deduplicated": 0,
"errors": [],
"jobs": [
{ "index": 0, "url": "https://example.com", "token": "a3f1c9…", "status": "queued" },
{ "index": 1, "url": "https://example.org", "token": "b7e2d1…", "status": "queued" },
{ "index": 2, "url": "https://example.net", "token": "c8f4a0…", "status": "queued" }
]
}
Les captures du batch partent en file dédiée (Queue 3), isolée des captures unitaires.
Avec un callback_url, chaque capture terminée notifie votre serveur individuellement.
Sans callback, interrogez chaque token via GET /capture/{token}.
Vous voulez que chaque capture atterrisse directement dans votre bucket ? Voir le guide Export vers S3 / Scaleway / R2.
Ruby
Capture simple
require "net/http"
require "json"
API_KEY = "VOTRE_CLÉ_API"
url = "https://example.com"
# 1. Soumettre
uri = URI("https://api.shotbot.net/capture")
req = Net::HTTP::Post.new(uri, "Content-Type" => "application/json")
req.body = { key: API_KEY, url: url, format: "webp", viewport_width: 1280 }.to_json
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |h| h.request(req) }
token = JSON.parse(res.body)["token"]
# 2. Attendre
loop do
sleep 2
s = JSON.parse(Net::HTTP.get(URI("https://api.shotbot.net/capture/#{token}")))
break puts s["image"] if s["status"] == "done"
end
CLI
Installation
pip install shotbot
npm install -g shotbot
Capture simple
shotbot capture --url=https://example.com # privée + enregistrée dans le dossier courant
Avec options
shotbot capture --url=https://example.com --format=webp --viewport=1440 --full-page
shotbot capture --url=https://example.com --output=capture.png # choisir le nom du fichier
shotbot capture --url=https://example.com --cdn # URL CDN publique, sans fichier local
shotbot capture --url=https://example.com --color-scheme=dark --wait=5
Sans installation (npx)
npx shotbot capture --url=https://example.com
CI / scripts
export SHOTBOT_API_KEY== htmlspecialchars($k) ?>
shotbot capture --url=https://example.com --output=avant.png
# déploiement…
shotbot capture --url=https://example.com --output=apres.png
Serveur MCP
Shotbot expose un serveur MCP (Model Context Protocol) que Claude Code, Claude Desktop, Cursor et Windsurf reconnaissent nativement. Demandez vos captures en langage naturel, sans écrire de code.
Installation
claude mcp add --scope user shotbot --transport http "https://api.shotbot.net/mcp?key== htmlspecialchars($k) ?>"
Outils disponibles
capture — capture une URL (format, viewport, frame, région…)
batch — soumet jusqu'à 5 000 URLs en une requête
get_status — interroge l'état d'une capture par token
account_status — quota restant, statut Pro, limite batch
Exemple d'usage (Claude Code)
Fais une capture de https://www.permalink.fr/ en format WebP, viewport 1280, avec le cadre browser_chrome.
Export vers S3
Envoyez chaque capture directement dans votre bucket via le mécanisme de callback. Shotbot poste le fichier à votre endpoint dès que le rendu est prêt. Compatible AWS S3, Scaleway, OVH, Cloudflare R2, Backblaze B2.
Soumettre avec callback_url
<?php
$ch = curl_init("https://api.shotbot.net/capture");
curl_setopt_array($ch, [CURLOPT_RETURNTRANSFER=>true, CURLOPT_POST=>true,
CURLOPT_HTTPHEADER=>["Content-Type: application/json"],
CURLOPT_POSTFIELDS=>json_encode([
"key" => "= htmlspecialchars($k) ?>",
"url" => "https://example.com",
"format" => "webp",
"callback_url" => "https://votreapp.com/callback-s3.php",
"callback_secret" => "votre_secret_ici",
]),
]);
$res = json_decode(curl_exec($ch), true);
Handler de réception (PHP)
<?php
require 'vendor/autoload.php'; // composer require aws/aws-sdk-php
use Aws\S3\S3Client;
if (!hash_equals('votre_secret_ici', $_POST['callback_secret'] ?? '')) {
http_response_code(403); exit;
}
if (($_POST['status'] ?? '') !== 'OK') { http_response_code(200); exit; }
$token = preg_replace('/[^A-Za-z0-9]/', '', $_POST['token'] ?? '');
$fmt = $_POST['format'] ?? 'jpg';
$s3 = new S3Client(['version'=>'latest','region'=>'fr-par',
'endpoint'=>'https://s3.fr-par.scw.cloud', // null pour AWS S3
'credentials'=>['key'=>getenv('S3_KEY'),'secret'=>getenv('S3_SECRET')]]);
$s3->putObject(['Bucket'=>'mon-bucket',
'Key' => 'screenshots/' . date('Y/m/d/') . $token . '.' . $fmt,
'Body' => fopen($_FILES['file']['tmp_name'], 'rb'),
'ContentType' => 'image/' . $fmt]);
http_response_code(200);
Cadres décoratifs
Le paramètre frame habille la capture d'un cadre ou d'un mockup appareil (format image, ignoré en PDF). Valeurs disponibles, groupées par usage :
browser_chromeTutoriels, captures de pagebrowser_chrome_darkTutoriels sur fond sombremobileAperçu responsive mobilemobile_lightSmartphone, châssis clairtabletAperçu responsive tablettetablet_lightTablette, châssis clairlaptopPrésentations, hero produitroundedCoins adoucis, taille inchangéeshadowArticles de blog, documentationpolaroidTémoignages, ambiance rétrogradientCartes sociales, images OGshotbot_brandImage de marque ShotbotDisponible sur tous les comptes. Cadres sans watermark avec Shotbot Pro.
JSON{
"url": "https://example.com",
"format": "png",
"frame": "browser_chrome"
}
Galerie de rendus réels : cadres sur captures réelles →
Vous utilisez l'API legacy (add.shotbot.net) ? Les exemples v1 sont dans l'onglet API Legacy de la documentation.
E-commerce & catalogues
Batch 500 URLs, AVIF, callback marque blanche.
Monitoring visuel
Planifiées 6h/24h/7j pour suivi d'évolution.
Archivage légal
Snapshots horodatés, immuables, sous token.
Agents IA
MCP natif pour Claude Code, Claude Desktop, Cursor, Windsurf.
Claude Code
Installez le serveur MCP en une commande, puis demandez vos captures en langage naturel.