API Documentation
URL de base : https://yhfdghkh6544.com/v1. Tous les endpoints utilisent HTTP standard. Les paramètres peuvent être transmis dans la chaîne de requête (GET) ou dans le corps JSON (POST).
Démarrage rapide
- Inscrivez-vous gratuitement, puis créez une API Key sur la page « API Key » de la console.
- Placez votre API Key dans l’en-tête
X-Access-Key, puis appelez l’endpoint de capture :
curl "https://yhfdghkh6544.com/v1/screenshot?url=https://example.com&full_page=true" \
-H "X-Access-Key: $SNAPI_KEY" \
-o screenshot.pngAuthentification
Chaque requête nécessite une API Key, à transmettre de l’une des façons suivantes :
- En-tête
X-Access-Key: snp_…(recommandé pour les appels côté serveur) - Paramètre de requête
access_key=snp_… - URL signée (pour intégrer l’URL dans une page web sans exposer le secret)
L’API Key n’est affichée en clair qu’une seule fois, lors de sa création. Conservez-la en lieu sûr. En cas de fuite, vous pouvez la désactiver dans la console.
API de capture
/v1/screenshot/v1/screenshotParamètres propres aux captures :
| Paramètre | Type | Par défaut | Description |
|---|---|---|---|
| format | enum | png | png, jpeg ou webp. |
| device_scale_factor | number | 1 | Facteur d’échelle, 1–3. La valeur 2 offre une netteté Retina, pour un fichier environ 4 fois plus volumineux. |
| quality | integer | 80 | Qualité JPEG / WebP, 1–100. |
| full_page | boolean | false | Capture la page entière. Fait défiler la page écran par écran pour déclencher le chargement différé, jusqu’à une hauteur de 20,000 pixels. |
| selector | string | — | Capture uniquement le premier élément correspondant à ce sélecteur CSS. Incompatible avec full_page. |
Paramètres communs aux captures et aux PDF :
| Paramètre | Type | Par défaut | Description |
|---|---|---|---|
| url | string | — | URL de la page à capturer, en http ou https. Utilisez ce paramètre ou html, mais pas les deux. Le fragment # de l’URL est conservé : vous pouvez ainsi capturer directement une page d’une application monopage utilisant le routage par fragment, comme https://app.com/#/report/1. |
| html | string | — | HTML fourni directement (2 MB maximum). Uniquement en POST. |
| viewport_width | integer | 1280 | Largeur de la fenêtre d’affichage, 320–3840. En dessous de 768, le rendu se fait en mode mobile. |
| viewport_height | integer | 800 | Hauteur de la fenêtre d’affichage, 200–3840. |
| wait_until | enum | networkidle | Stratégie d’attente : domcontentloaded (la plus rapide, adaptée aux pages statiques), load, networkidle (suit toutes les requêtes dès l’ouverture de la page et ne lance la capture qu’après 500 millisecondes sans requête en cours une fois la page chargée, avec une attente supplémentaire de 8 secondes maximum ; adaptée aux applications monopages). |
| wait_for_selector | string | — | Après le chargement de la page, attend qu’un élément correspondant à ce sélecteur CSS soit présent et visible avant de lancer la capture, pendant 10 secondes maximum. Sinon, renvoie selector_not_found (non facturé). Adapté aux applications monopages dont le contenu s’affiche après un délai ou une animation, sans requête réseau, comme .chart-ready. |
| delay | integer | 0 | Délai supplémentaire en millisecondes après la fin de l’attente, 0–10000. Utile pour les animations ou les composants chargés à la demande. |
| block_ads | boolean | true | Bloque les requêtes vers les domaines publicitaires et de suivi. |
| block_cookie_banners | boolean | true | Masque les fenêtres de consentement aux cookies. |
| dark_mode | boolean | false | Émule prefers-color-scheme: dark. |
| hide_selectors | string | — | Liste de sélecteurs CSS des éléments à masquer, par exemple .ad,#popup. |
| cache | boolean | true | Indique s’il faut consulter le cache. La valeur false force un nouveau rendu, dont le résultat est tout de même mis en cache. |
| cache_ttl | integer | 86400 | Durée de validité du cache en secondes, 60–2592000. |
| response_type | enum | binary | binary renvoie directement le fichier ; json renvoie l’URL du fichier et les métadonnées. |
Capturer une application monopage (React, Vue, etc.) :Le JavaScript de la page s’exécute dans un véritable navigateur avant la capture. Par défaut, networkidle attend la fin du chargement des données. Si le contenu s’affiche ensuite après un délai ou une animation, ajoutez wait_for_selector pour désigner un élément indiquant que le contenu est prêt. Si aucun élément ne convient, utilisez delay pour prolonger l’attente.
API PDF
/v1/pdf/v1/pdfChaque PDF consomme 2 unités du quota. Avant l’impression, la page est parcourue pour déclencher le chargement différé. En plus des paramètres communs, les paramètres suivants sont disponibles :
| Paramètre | Type | Par défaut | Description |
|---|---|---|---|
| paper | enum | a4 | Format du papier : a4 ou letter. Si la page définit une règle @page, celle-ci est prioritaire. |
| print_background | boolean | true | Imprime les couleurs et les images d’arrière-plan. |
| pdf_media | enum | print utilise les styles d’impression ; screen utilise les styles d’affichage à l’écran (rendu identique à l’écran). |
curl -X POST "https://yhfdghkh6544.com/v1/pdf" \
-H "X-Access-Key: $SNAPI_KEY" \
-H "Content-Type: application/json" \
-d '{"html": "<h1>Rapport trimestriel</h1><p>Contenu…</p>", "paper": "a4"}' \
-o report.pdfRéponses
Par défaut (response_type=binary), le fichier est renvoyé directement, avec le Content-Type correspondant au format. En-têtes de réponse :
X-Request-Id: identifiant de la requête, à fournir pour le dépannageX-Snapi-Cache:HITouMISSX-Snapi-Truncated: true: la capture de la page entière a été tronquée au-delà de 20,000 pixelsX-Snapi-Format-Changed: webp: un PNG de plus de 5 MB est automatiquement converti en WebP (sauf si format=png est explicitement spécifié)
Avec response_type=json, la réponse contient l’URL du fichier et les métadonnées :
curl -X POST "https://yhfdghkh6544.com/v1/screenshot" \
-H "X-Access-Key: $SNAPI_KEY" \
-H "Content-Type: application/json" \
-d '{"url": "https://example.com", "response_type": "json"}'
# Renvoie
{
"url": "https://yhfdghkh6544.com/i/3f/3f9c….png",
"cache_hit": false,
"duration_ms": 1284,
"format": "png",
"width": 1280,
"height": 800,
"request_id": "req_01j…"
}URL signées
Vous souhaitez intégrer l’URL d’une capture directement dans <img src>, sans exposer votre API Key ? Calculez la signature sur votre serveur avec le secret associé à votre API Key (disponible sur la page API Key de la console) :
canonical = Triez tous les paramètres, sauf signature, par ordre lexicographique de leur nom,
formatez chaque entrée en k=urlencode(v) (RFC 3986, espaces encodés en %20), puis reliez-les avec &
signature = hex(HMAC-SHA256(secret, canonical))Une signature valide suffit à authentifier la requête. Toute modification d’un paramètre (par exemple url) invalide la signature. Le paramètre facultatif expires (horodatage Unix en secondes) est inclus dans la signature ; une fois expiré, la requête renvoie signature_expired. Pour actualiser le cache, modifiez un paramètre sans effet (comme v=2), puis recalculez la signature. Les URL signées ne prennent en charge que GET.
import crypto from "node:crypto";
// Encodage RFC 3986 (espaces encodés en %20), identique à celui utilisé côté serveur
const enc = (v) =>
encodeURIComponent(v).replace(/[!'()*]/g, (c) => "%" + c.charCodeAt(0).toString(16).toUpperCase());
function signedUrl(params, accessKey, secret) {
const all = { ...params, access_key: accessKey };
const canonical = Object.keys(all).sort().map((k) => `${k}=${enc(String(all[k]))}`).join("&");
const signature = crypto.createHmac("sha256", secret).update(canonical).digest("hex");
return `https://yhfdghkh6544.com/v1/screenshot?${canonical}&signature=${signature}`;
}
const src = signedUrl({ url: "https://example.com", viewport_width: 1200 }, KEY, SECRET);
// <img src={src} alt="Aperçu de la page web" />Cache
Les requêtes dont les paramètres sont identiques utilisent le cache (un paramètre omis équivaut à sa valeur par défaut explicite ; les paramètres d’authentification et de cache sont exclus de la comparaison). En cas de résultat en cache, le mode binaire renvoie une redirection 302 vers l’URL du fichier, avec Cache-Control ; le navigateur suit automatiquement la redirection et met le fichier en cache. Le mode JSON renvoie cache_hit: true.
Les résultats servis depuis le cache ne sont pas facturés.Les requêtes simultanées avec les mêmes paramètres ne déclenchent qu’un seul rendu.
Limites et facturation
- Seules les requêtes réussies (2xx) non servies depuis le cache sont facturées : 1 unité pour une capture, 2 pour un PDF.
- Les délais dépassés, échecs de rendu, erreurs du site cible, paramètres invalides et échecs d’authentification ne consomment pas de quota.
- Le quota est réinitialisé chaque mois civil (UTC). L’offre gratuite inclut 100 unités par mois ; une fois le quota épuisé, les requêtes renvoient quota_exceeded.
- Tout dépassement du nombre de requêtes par seconde ou de requêtes simultanées renvoie 429. L’en-tête Retry-After indique le délai d’attente conseillé en secondes.
- Durée maximale de rendu : 30 secondes ; volume téléchargé par page : 50 MB maximum ; taille du fichier de sortie : 50 MB maximum.
Consultez les limites de chaque offre : Tarifs.
Codes d’erreur
Toutes les réponses d’erreur sont au format JSON :
{
"error": {
"code": "render_failed",
"message": "target responded with an error status",
"upstream_status": 404,
"request_id": "req_01j…"
}
}| HTTP | code | Signification | Facturé |
|---|---|---|---|
| 400 | invalid_params | Paramètre manquant ou hors limites ; message précise lequel | Non |
| 400 | html_too_large | html dépasse 2 MB | Non |
| 401 | unauthorized | API Key manquante, invalide ou désactivée | Non |
| 401 | signature_invalid | Signature incorrecte (paramètres modifiés ou secret incorrect) | Non |
| 401 | signature_expired | L’URL signée a dépassé la date d’expiration expires | Non |
| 403 | url_blocked | Accès interdit à l’URL cible (par exemple une adresse réseau privée ou localhost) | Non |
| 408 | render_timeout | Le rendu a dépassé 30 secondes | Non |
| 422 | render_failed | Le site cible a renvoyé une erreur 4xx/5xx, une erreur DNS ou une erreur de certificat ; inclut upstream_status | Non |
| 422 | selector_not_found | Aucun élément ne correspond à selector, ou le délai d’attente de wait_for_selector est dépassé | Non |
| 422 | too_many_redirects | Plus de 5 redirections | Non |
| 429 | rate_limited | Limite de requêtes par seconde dépassée ; réessayez en suivant Retry-After | Non |
| 429 | concurrency_exceeded | Limite de requêtes simultanées dépassée | Non |
| 429 | quota_exceeded | Quota mensuel épuisé (offre gratuite) | Non |
| 429 | too_many_batches | Plus de 5 lots en cours | Non |
| 500 | internal | Erreur serveur ; contactez-nous en joignant request_id | Non |
Lots asynchrones
/v1/batch/v1/batch/{id}Envoyez de 1 à 100 URL en une requête. L’endpoint renvoie immédiatement un identifiant de tâche (HTTP 202), puis les captures sont réalisées une par une en arrière-plan. Idéal pour les captures par lots sans garder la connexion ouverte. Vous pouvez interroger régulièrement l’endpoint de suivi ou renseigner webhook_url pour recevoir une notification une fois le traitement terminé. L’authentification est identique à celle des endpoints synchrones (uniquement par en-tête ou paramètre access_key, sans prise en charge des URL signées).
| Paramètre | Type | Par défaut | Description |
|---|---|---|---|
| type | enum | screenshot | screenshot (capture d’écran) ou pdf. |
| urls | string[] | — | URL à traiter, de 1 à 100. Si l’une d’elles est invalide, la requête entière renvoie 400 ; message indique sa position. |
| options | object | {} | Paramètres communs à toutes les URL, identiques à ceux des endpoints synchrones (par exemple full_page, format, viewport_width, cache_ttl). Ne peut pas contenir url, html ni response_type. |
| webhook_url | string | — | Facultatif. URL à notifier une fois le traitement terminé. Voir « Notification de fin ». |
curl -X POST "https://yhfdghkh6544.com/v1/batch" \
-H "X-Access-Key: $SNAPI_KEY" \
-H "Content-Type: application/json" \
-d '{
"type": "screenshot",
"urls": ["https://example.com", "https://example.org"],
"options": { "full_page": true, "format": "webp" },
"webhook_url": "https://your-app.com/hooks/snapi"
}'
# Renvoie 202
{
"id": "bat_01j…",
"type": "screenshot",
"status": "queued",
"total": 2, "succeeded": 0, "failed": 0,
"created_at": "2026-10-18T09:30:00Z", "completed_at": null,
"webhook": { "url": "https://your-app.com/hooks/snapi", "status": "none", "attempts": 0 },
"items": [
{ "index": 0, "url": "https://example.com", "status": "pending", "result": null, "error": null },
{ "index": 1, "url": "https://example.org", "status": "pending", "result": null, "error": null }
]
}Suivi de la progression : le statut status du lot passe par queued (en attente), running (en cours), puis completed (traitement terminé, avec ou sans erreurs). Chaque URL a pour statut pending, running, succeeded ou failed. En cas de réussite, result contient l’URL du fichier ; en cas d’échec, error contient le code d’erreur (identique à ceux des endpoints synchrones).
curl "https://yhfdghkh6544.com/v1/batch/bat_01j…" -H "X-Access-Key: $SNAPI_KEY"
{
"id": "bat_01j…",
"status": "completed",
"total": 2, "succeeded": 1, "failed": 1,
"completed_at": "2026-10-18T09:30:12Z",
"items": [
{
"index": 0, "url": "https://example.com", "status": "succeeded", "duration_ms": 1830,
"result": { "url": "https://yhfdghkh6544.com/i/3f/3f9c….webp", "mime": "image/webp", "width": 1280, "height": 2410, "cache_hit": false },
"error": null
},
{
"index": 1, "url": "https://example.org", "status": "failed", "duration_ms": 640,
"result": null,
"error": { "code": "render_failed", "message": "target responded with an error status" }
}
]
}- La facturation est identique à celle des endpoints synchrones : chaque URL traitée avec succès consomme 1 unité pour une capture ou 2 pour un PDF. Les résultats en cache et les échecs ne consomment pas de quota.
- Si le quota est déjà épuisé lors de l’envoi, la requête renvoie immédiatement
quota_exceeded. S’il s’épuise pendant le traitement, les URL restantes échouent avecquota_exceeded. - Chaque compte peut avoir au maximum 5 lots en cours. Au-delà, la requête renvoie
too_many_batches. - La durée de validité des fichiers est définie par
options.cache_ttl(1 jour par défaut). Téléchargez-les et enregistrez-les avant leur expiration. - Dans les résultats de consultation et les notifications,
webhook.urln’affiche que le chemin, sans la chaîne de requête.
Notification de fin
Si vous renseignez webhook_url, nous y envoyons une requête POST une fois le lot entièrement traité. Le corps JSON est { "event": "batch.completed", "batch": { … } }, où batch est strictement identique à la réponse de l’endpoint de suivi. En-têtes :
X-Snapi-Signature: t=1760779812,v1=5d3f…:test l’heure d’envoi (horodatage Unix en secondes) etv1la signatureX-Snapi-Event: batch.completed,X-Snapi-Delivery-Attempt: 1(numéro de la tentative d’envoi)
v1 = hex(HMAC-SHA256(secret, t + "." + Corps brut de la requête))
secret est le secret de signature associé à l’API Key utilisée pour soumettre la tâche (disponible sur la page API Key de la console).- Renvoyez une réponse 2xx sous 10 secondes, sinon l’envoi est considéré comme ayant échoué. En cas d’échec, trois nouvelles tentatives sont effectuées après 10 secondes, 1 minute et 5 minutes (4 tentatives au maximum au total).
- Dans de très rares cas, vous pouvez recevoir plusieurs notifications pour une même tâche. Dédupliquez-les à l’aide de
batch.id. - Pour vérifier la signature, utilisez les octets bruts reçus et assurez-vous que
tne diffère pas de l’heure actuelle de plus de quelques minutes afin d’éviter les attaques par rejeu. - L’URL de callback doit être une adresse http(s) accessible publiquement et ne doit pas pointer vers un réseau privé.
import crypto from "node:crypto";
// rawBody doit contenir les octets bruts reçus, sans passer par JSON.parse puis une nouvelle sérialisation.
function verifySnapiWebhook(rawBody, header, secret, toleranceSec = 300) {
const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
const t = Number(parts.t);
if (!t || Math.abs(Date.now() / 1000 - t) > toleranceSec) return false; // Protection contre le rejeu
const expected = crypto.createHmac("sha256", secret).update(`${t}.${rawBody}`).digest("hex");
const got = Buffer.from(parts.v1 ?? "");
return got.length === expected.length && crypto.timingSafeEqual(Buffer.from(expected), got);
}
// verifySnapiWebhook(req.rawBody, req.headers["x-snapi-signature"], SNAPI_SECRET)Suivi de l’utilisation
/v1/usagecurl "https://yhfdghkh6544.com/v1/usage" -H "X-Access-Key: $SNAPI_KEY"
{ "month": "202610", "used": 37, "limit": 100, "remaining": 63, "overage": 0, "resets_at": "2026-11-01T00:00:00Z", ... }État du service
/v1/healthSans authentification. Renvoie l’état du nœud et du pool de navigateurs (nombre total, occupés et disponibles).