Snapi
Entwicklerdokumentation

API Dokumentation

Basis-URL: https://yhfdghkh6544.com/v1. Alle Endpunkte verwenden Standard-HTTP. Parameter können im Query-String (GET) oder im JSON-Body (POST) übergeben werden.

Schnellstart

  1. Registrieren Sie sich kostenlos und erstellen Sie in der Konsole unter „API Key“ einen API Key.
  2. Übergeben Sie den API Key im Request-Header X-Access-Key und rufen Sie den Screenshot-Endpunkt auf:
curl "https://yhfdghkh6544.com/v1/screenshot?url=https://example.com&full_page=true" \
  -H "X-Access-Key: $SNAPI_KEY" \
  -o screenshot.png

Authentifizierung

Jede Anfrage benötigt einen API Key. Sie können ihn auf eine der folgenden Arten übergeben:

  • Request-Header X-Access-Key: snp_… (empfohlen für serverseitige Aufrufe)
  • Query-Parameter access_key=snp_…
  • Signierte URL (zum Einbetten der URL in Webseiten, ohne das Secret offenzulegen)

Der API Key wird nur bei der Erstellung einmal im Klartext angezeigt. Bewahren Sie ihn sicher auf. Bei einer Offenlegung können Sie ihn in der Konsole deaktivieren.

Screenshot-Endpunkt

GET/v1/screenshot
POST/v1/screenshot

Screenshot-spezifische Parameter:

ParameterTypStandardBeschreibung
formatenumpngpng, jpeg oder webp.
device_scale_factornumber1Skalierungsfaktor 1–3. Mit 2 erhalten Sie Retina-Schärfe bei etwa 4-facher Dateigröße.
qualityinteger80JPEG / WebP-Qualität, 1–100.
full_pagebooleanfalseNimmt die gesamte Seite auf. Scrollt Bildschirm für Bildschirm, um Lazy Loading auszulösen. Maximale Höhe: 20,000 Pixel.
selectorstring—Nimmt nur das erste Element auf, das diesem CSS-Selektor entspricht. Nicht mit full_page kombinierbar.

Gemeinsame Parameter für Screenshots und PDFs:

ParameterTypStandardBeschreibung
urlstring—URL der aufzunehmenden Webseite, http oder https. Geben Sie entweder diesen Parameter oder html an. Das #-Fragment der URL bleibt erhalten. So können Sie Seiten von Single-Page-Anwendungen mit Hash-Routing (z. B. https://app.com/#/report/1) direkt aufnehmen.
htmlstring—HTML direkt übergeben (maximal 2 MB), nur per POST.
viewport_widthinteger1280Viewport-Breite, 320–3840. Bei Werten unter 768 wird die Seite wie auf einem Mobilgerät gerendert.
viewport_heightinteger800Viewport-Höhe, 200–3840.
wait_untilenumnetworkidleWartestrategie: domcontentloaded (am schnellsten, für statische Seiten), load, networkidle (verfolgt alle Anfragen ab dem Öffnen der Seite; nach dem Laden wird der Screenshot erstellt, sobald 500 Millisekunden lang keine Anfrage mehr läuft; wartet höchstens weitere 8 Sekunden, geeignet für Single-Page-Anwendungen).
wait_for_selectorstring—Wartet nach dem Laden der Seite bis zu 10 Sekunden, bis ein Element erscheint und sichtbar ist, das diesem CSS-Selektor entspricht. Andernfalls wird selector_not_found zurückgegeben (ohne Berechnung). Geeignet für Single-Page-Anwendungen, die Inhalte per Timer oder Animation verzögert rendern, ohne Netzwerkanfragen auszulösen, z. B. .chart-ready.
delayinteger0Zusätzliche Wartezeit in Millisekunden nach Abschluss der Wartestrategie, 0–10000. Für Animationen oder Lazy-Loading-Komponenten.
block_adsbooleantrueBlockiert Anfragen an Werbe- und Tracking-Domains.
block_cookie_bannersbooleantrueBlendet Cookie-Einwilligungsdialoge aus.
dark_modebooleanfalseSimuliert prefers-color-scheme: dark.
hide_selectorsstring—Liste von CSS-Selektoren für auszublendende Elemente, z. B. .ad,#popup.
cachebooleantrueLegt fest, ob der Cache geprüft wird. Mit false wird die Seite neu gerendert; das Ergebnis wird weiterhin im Cache gespeichert.
cache_ttlinteger86400Cache-Gültigkeitsdauer in Sekunden, 60–2592000.
response_typeenumbinarybinary gibt die Datei direkt zurück; json gibt die Datei-URL und Metadaten zurück.

Single-Page-Anwendungen aufnehmen (React, Vue usw.):Die Seite führt JavaScript in einem echten Browser aus, bevor der Screenshot erstellt wird. Die Standardstrategie networkidle wartet, bis die Seite ihre Daten geladen hat. Werden Inhalte erst danach per Timer oder Animation gerendert, geben Sie mit wait_for_selector ein Element an, das signalisiert, dass der Inhalt bereit ist. Gibt es kein geeignetes Element, können Sie mit delay zusätzliche Wartezeit einplanen.

PDF-Endpunkt

GET/v1/pdf
POST/v1/pdf

Jedes PDF verbraucht 2 Kontingenteinheiten. Vor dem Drucken wird die Seite einmal durchgescrollt, um Lazy-Loading-Inhalte zu laden. Zusätzlich zu den gemeinsamen Parametern werden unterstützt:

ParameterTypStandardBeschreibung
paperenuma4Papierformat: a4 oder letter. Enthält die Seite eine eigene @page-Regel, hat diese Vorrang.
print_backgroundbooleantrueDruckt Hintergrundfarben und -bilder.
pdf_mediaenumprintprint verwendet Druckstile; screen verwendet Bildschirmstile (wie im Browser angezeigt).
HTML in PDF umwandeln
curl -X POST "https://yhfdghkh6544.com/v1/pdf" \
  -H "X-Access-Key: $SNAPI_KEY" \
  -H "Content-Type: application/json" \
  -d '{"html": "<h1>Quartalsbericht</h1><p>Inhalt …</p>", "paper": "a4"}' \
  -o report.pdf

Antworten

Standardmäßig (response_type=binary) wird die Datei direkt zurückgegeben, mit dem passenden Format in Content-Type. Response-Header:

  • X-Request-Id: ID dieser Anfrage; bitte bei der Fehleranalyse angeben
  • X-Snapi-Cache: HIT oder MISS
  • X-Snapi-Truncated: true: Die vollständige Seite wurde bei 20,000 Pixeln Höhe abgeschnitten
  • X-Snapi-Format-Changed: webp: PNG über 5 MB wurde automatisch in WebP umgewandelt (keine Umwandlung bei expliziter Angabe von format=png)

Mit response_type=json werden die Datei-URL und Metadaten zurückgegeben:

JSON-Antwort
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"}'

# Rückgabe
{
  "url": "https://yhfdghkh6544.com/i/3f/3f9c….png",
  "cache_hit": false,
  "duration_ms": 1284,
  "format": "png",
  "width": 1280,
  "height": 800,
  "request_id": "req_01j…"
}

Signierte URLs

Sie möchten die Screenshot-URL direkt in <img src> einbinden, ohne Ihren API Key offenzulegen? Berechnen Sie die Signatur auf Ihrem eigenen Server mit dem zum API Key gehörenden Secret. Sie finden es in der Konsole unter „API Key“:

Signaturregeln
canonical = Alle Parameter außer signature lexikografisch nach Parameternamen sortieren,
            jeden Eintrag als k=urlencode(v) kodieren (RFC 3986, Leerzeichen als %20) und mit & verbinden
signature = hex(HMAC-SHA256(secret, canonical))

Eine gültige Signatur authentifiziert die Anfrage. Wird ein Parameter geändert (z. B. url), wird die Signatur ungültig. Der optionale Parameter expires (Unix-Zeit in Sekunden) wird in die Signatur einbezogen; nach Ablauf wird signature_expired zurückgegeben. Um den Cache zu aktualisieren, ändern Sie einen Parameter ohne funktionale Bedeutung (z. B. v=2) und signieren Sie die URL erneut. Signierte URLs unterstützen nur GET.

import crypto from "node:crypto";

// RFC-3986-Kodierung (Leerzeichen als %20), muss den serverseitigen Regeln entsprechen
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="Webseitenvorschau" />

Cache

Anfragen mit identischen Parametern werden aus dem Cache bedient. Weggelassene Parameter gelten als identisch mit explizit angegebenen Standardwerten; Authentifizierungs- und Cache-Parameter werden nicht verglichen. Bei einem Cache-Treffer liefert der Binärmodus eine 302-Weiterleitung zur Datei-URL mit Cache-Control. Der Browser folgt der Weiterleitung automatisch und speichert die Datei im Cache. Im JSON-Modus wird cache_hit: true zurückgegeben.

Cache-Treffer werden nicht berechnet.Gleichzeitige Anfragen mit identischen Parametern lösen nur einen Rendervorgang aus.

Limits und Abrechnung

  • Nur erfolgreiche Anfragen (2xx) ohne Cache-Treffer werden berechnet: 1 Kontingenteinheit pro Screenshot, 2 pro PDF.
  • Zeitüberschreitungen, Renderfehler, Fehler der Zielwebsite, ungültige Parameter und fehlgeschlagene Authentifizierungen verbrauchen kein Kontingent.
  • Das Kontingent wird zu Beginn jedes Kalendermonats (UTC) zurückgesetzt. Der kostenlose Tarif enthält 100 Kontingenteinheiten pro Monat. Nach Verbrauch wird quota_exceeded zurückgegeben.
  • Bei Überschreitung der Anfragen pro Sekunde oder der gleichzeitigen Anfragen wird 429 zurückgegeben. Der Response-Header Retry-After enthält die empfohlene Wartezeit in Sekunden.
  • Maximale Renderdauer: 30 Sekunden; maximales Downloadvolumen pro Seite: 50 MB; maximale Ausgabedateigröße: 50 MB.

Die genauen Limits der einzelnen Tarife finden Sie unter Preise.

Fehlercodes

Fehlerantworten werden immer als JSON zurückgegeben:

Fehlerformat
{
  "error": {
    "code": "render_failed",
    "message": "target responded with an error status",
    "upstream_status": 404,
    "request_id": "req_01j…"
  }
}
HTTPcodeBedeutungKostenpflichtig
400invalid_paramsParameter fehlt oder liegt außerhalb des zulässigen Bereichs; message nennt den betroffenen ParameterNein
400html_too_largehtml überschreitet 2 MBNein
401unauthorizedAPI Key fehlt, ist ungültig oder wurde deaktiviertNein
401signature_invalidSignatur stimmt nicht überein (Parameter geändert oder falsches Secret)Nein
401signature_expiredDie signierte URL ist seit dem in expires angegebenen Zeitpunkt abgelaufenNein
403url_blockedZugriff auf die Zieladresse nicht erlaubt (z. B. interne Netzwerkadresse oder localhost)Nein
408render_timeoutRenderdauer überschreitet 30 SekundenNein
422render_failedZielwebsite gibt 4xx/5xx zurück, DNS-Auflösung fehlgeschlagen oder Zertifikatsfehler; enthält upstream_statusNein
422selector_not_foundKein Element für selector gefunden oder Zeitüberschreitung bei wait_for_selectorNein
422too_many_redirectsMehr als 5 WeiterleitungenNein
429rate_limitedLimit für Anfragen pro Sekunde überschritten; gemäß Retry-After erneut versuchenNein
429concurrency_exceededLimit für gleichzeitige Anfragen überschrittenNein
429quota_exceededMonatskontingent aufgebraucht (kostenloser Tarif)Nein
429too_many_batchesMehr als 5 noch nicht abgeschlossene Stapelaufträge gleichzeitigNein
500internalServerfehler; kontaktieren Sie uns und geben Sie request_id anNein

Asynchrone Stapelverarbeitung

POST/v1/batch
GET/v1/batch/{id}

Übermitteln Sie 1–100 URLs auf einmal. Der Endpunkt gibt sofort eine Auftrags-ID zurück (HTTP 202); die Screenshots werden im Hintergrund nacheinander erstellt. Ideal für mehrere Screenshots, ohne die Verbindung offen halten zu müssen. Sie können den Status regelmäßig abfragen oder webhook_url angeben, um nach Abschluss aller Aufgaben benachrichtigt zu werden. Die Authentifizierung entspricht der synchronen API (nur per Request-Header oder Parameter access_key, keine signierten URLs).

ParameterTypStandardBeschreibung
typeenumscreenshotscreenshot (Screenshot) oder pdf.
urlsstring[]—Zu verarbeitende URLs, 1–100. Ist eine davon ungültig, wird die gesamte Anfrage mit 400 abgelehnt. message gibt die Position der ungültigen URL an.
optionsobject{}Gemeinsame Parameter für alle URLs, identisch mit denen der synchronen Endpunkte (z. B. full_page, format, viewport_width, cache_ttl). Darf url, html und response_type nicht enthalten.
webhook_urlstring—Optional. URL für die Benachrichtigung nach Abschluss aller Aufgaben, siehe „Webhook bei Abschluss“.
Stapelauftrag erstellen
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"
  }'

# Rückgabe: 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 }
  ]
}

Fortschritt abfragen: Der Auftragsstatus status durchläuft queued (in der Warteschlange), running (in Bearbeitung) und completed (alles abgeschlossen, unabhängig von Erfolg oder Fehler). Jede URL hat den Status pending, running, succeeded oder failed. Bei Erfolg enthält result die Datei-URL, bei einem Fehler enthält error den Fehlercode (identisch mit den synchronen Endpunkten).

Stapelauftrag abfragen
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" }
    }
  ]
}
  • Es gelten dieselben Abrechnungsregeln wie bei den synchronen Endpunkten: pro erfolgreich verarbeiteter URL 1 Kontingenteinheit für einen Screenshot oder 2 für ein PDF. Cache-Treffer und Fehler verbrauchen kein Kontingent.
  • Ist das Kontingent beim Einreichen bereits aufgebraucht, wird sofort quota_exceeded zurückgegeben. Wird es während der Verarbeitung aufgebraucht, schlagen die verbleibenden URLs mit quota_exceeded fehl.
  • Pro Konto sind höchstens 5 noch nicht abgeschlossene Stapelaufträge gleichzeitig erlaubt. Bei Überschreitung wird too_many_batches zurückgegeben.
  • Die Gültigkeitsdauer der Ergebnisdateien wird durch options.cache_ttl bestimmt (standardmäßig 1 Tag). Laden Sie die Dateien rechtzeitig herunter und speichern Sie sie.
  • In Abfrageergebnissen und Webhooks wird webhook.url nur bis zum Pfad angezeigt, ohne Query-String.

Webhook bei Abschluss

Wenn Sie webhook_url angeben, senden wir nach Abschluss des gesamten Auftrags eine POST-Anfrage an diese URL. Der Request-Body ist JSON: { "event": "batch.completed", "batch": { … } }. Dabei entspricht batch genau der Antwort des Abfrage-Endpunkts. Request-Header:

  • X-Snapi-Signature: t=1760779812,v1=5d3f…: t ist der Sendezeitpunkt (Unix-Zeit in Sekunden), v1 die Signatur
  • X-Snapi-Event: batch.completed, X-Snapi-Delivery-Attempt: 1 (Nummer des Zustellversuchs)
Signaturregeln
v1 = hex(HMAC-SHA256(secret, t + "." + Unveränderter Request-Body))
secret ist das Signatur-Secret des API Key, mit dem der Auftrag eingereicht wurde (in der Konsole auf der Seite „API Key“ einsehbar).
  • Antworten Sie innerhalb von 10 Sekunden mit 2xx, andernfalls gilt die Zustellung als fehlgeschlagen. Bei einem Fehler wird die Zustellung nach 10 Sekunden, 1 Minute und 5 Minuten jeweils einmal wiederholt (insgesamt maximal 4 Versuche).
  • In seltenen Fällen können Sie für denselben Auftrag mehrere Benachrichtigungen erhalten. Erkennen Sie Duplikate anhand von batch.id.
  • Berechnen Sie die Signatur anhand der empfangenen, unveränderten Bytes. Prüfen Sie außerdem, dass t höchstens wenige Minuten von der aktuellen Zeit abweicht, um Replay-Angriffe zu verhindern.
  • Die Callback-URL muss eine öffentlich erreichbare http(s)-Adresse sein und darf nicht auf ein internes Netzwerk verweisen.
import crypto from "node:crypto";

// rawBody muss die empfangenen Originalbytes enthalten. Nicht zuerst mit JSON.parse parsen und anschließend erneut serialisieren.
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; // Schutz vor Replay-Angriffen
  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)

Nutzung abfragen

GET/v1/usage
shell
curl "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", ... }

Statusprüfung

GET/v1/health

Keine Authentifizierung erforderlich. Gibt den Knotenstatus und den Status des Browser-Pools zurück (gesamt, belegt, frei).