Snapi
Documentazione per sviluppatori

API Documentazione

URL di base: https://yhfdghkh6544.com/v1. Tutti gli endpoint usano HTTP standard. I parametri possono essere passati nella query string (GET) o nel corpo JSON (POST).

Guida rapida

  1. Registrati gratis e crea un'API Key nella pagina «API Key» della console.
  2. Inserisci l'API Key nell'header X-Access-Key e chiama l'endpoint screenshot:
curl "https://yhfdghkh6544.com/v1/screenshot?url=https://example.com&full_page=true" \
  -H "X-Access-Key: $SNAPI_KEY" \
  -o screenshot.png

Autenticazione

Ogni richiesta richiede un'API Key, che puoi passare in uno dei seguenti modi:

  • Header X-Access-Key: snp_… (consigliato per le chiamate lato server)
  • Parametro di query access_key=snp_…
  • URL firmato (per inserire l'URL in una pagina web senza esporre il secret)

L'API Key viene mostrata in chiaro una sola volta, al momento della creazione. Conservala al sicuro. Se viene compromessa, puoi disattivarla dalla console.

Endpoint screenshot

GET/v1/screenshot
POST/v1/screenshot

Parametri specifici per gli screenshot:

ParametroTipoValore predefinitoDescrizione
formatenumpngpng, jpeg o webp.
device_scale_factornumber1Fattore di scala, 1–3. Impostando 2 si ottiene una risoluzione Retina, con dimensioni del file circa 4 volte maggiori.
qualityinteger80Qualità JPEG / WebP, 1–100.
full_pagebooleanfalseAcquisisce l'intera pagina. Scorre una schermata alla volta per attivare il caricamento differito, fino a un'altezza massima di 20,000 pixel.
selectorstring—Acquisisce solo il primo elemento corrispondente a questo selettore CSS. Non può essere usato insieme a full_page.

Parametri comuni a screenshot e PDF:

ParametroTipoValore predefinitoDescrizione
urlstring—URL della pagina da acquisire, http o https. Usa questo parametro oppure html. Il frammento # dell'URL viene mantenuto: puoi acquisire direttamente la pagina desiderata nelle applicazioni a pagina singola con routing hash, come https://app.com/#/report/1.
htmlstring—HTML da inviare direttamente (massimo 2 MB). Supportato solo con POST.
viewport_widthinteger1280Larghezza del viewport, 320–3840. Con valori inferiori a 768, la pagina viene renderizzata come su un dispositivo mobile.
viewport_heightinteger800Altezza del viewport, 200–3840.
wait_untilenumnetworkidleStrategia di attesa: domcontentloaded (la più rapida, ideale per pagine statiche), load, networkidle (monitora tutte le richieste dall'apertura della pagina e, dopo il caricamento, acquisisce lo screenshot quando non ci sono richieste in corso per 500 millisecondi consecutivi; attende al massimo altri 8 secondi, ideale per applicazioni a pagina singola).
wait_for_selectorstring—Dopo il caricamento della pagina, attende fino a 10 secondi che un elemento corrispondente a questo selettore CSS sia presente e visibile prima di acquisire lo screenshot. Se non compare, restituisce selector_not_found (senza addebito). Ideale per applicazioni a pagina singola che renderizzano contenuti in ritardo tramite timer o animazioni, anziché richieste di rete, come .chart-ready.
delayinteger0Ritardo aggiuntivo in millisecondi dopo l'attesa, 0–10000. Utile per animazioni o componenti con caricamento differito.
block_adsbooleantrueBlocca le richieste ai domini pubblicitari e di tracciamento.
block_cookie_bannersbooleantrueNasconde i banner di consenso ai cookie.
dark_modebooleanfalseSimula prefers-color-scheme: dark.
hide_selectorsstring—Elenco di selettori CSS degli elementi da nascondere, ad esempio .ad,#popup.
cachebooleantrueIndica se cercare nella cache. Con false forza un nuovo rendering; il risultato viene comunque salvato nella cache.
cache_ttlinteger86400Durata della cache in secondi, 60–2592000.
response_typeenumbinarybinary restituisce direttamente il file; json restituisce l'URL del file e i metadati.

Acquisire applicazioni a pagina singola (React, Vue, ecc.):La pagina viene eseguita in un browser reale e lo screenshot viene acquisito dopo l'esecuzione di JavaScript. La strategia predefinita networkidle attende il completamento del recupero dei dati. Se i contenuti vengono renderizzati con timer o animazioni dopo la fine delle richieste, aggiungi wait_for_selector per indicare un elemento che segnali che il contenuto è pronto. Se non c'è un elemento adatto, usa delay per attendere ancora.

Endpoint PDF

GET/v1/pdf
POST/v1/pdf

Ogni PDF consuma 2 crediti. Prima della stampa, la pagina viene scorsa per attivare i contenuti con caricamento differito. Oltre ai parametri comuni, sono supportati:

ParametroTipoValore predefinitoDescrizione
paperenuma4Formato carta: a4 o letter. Se la pagina definisce regole @page, queste hanno la precedenza.
print_backgroundbooleantrueStampa colori e immagini di sfondo.
pdf_mediaenumprintprint usa gli stili di stampa; screen usa gli stili a schermo (come visualizzati nel browser).
Da HTML a PDF
curl -X POST "https://yhfdghkh6544.com/v1/pdf" \
  -H "X-Access-Key: $SNAPI_KEY" \
  -H "Content-Type: application/json" \
  -d '{"html": "<h1>Report trimestrale</h1><p>Contenuto…</p>", "paper": "a4"}' \
  -o report.pdf

Risposte

Per impostazione predefinita (response_type=binary), viene restituito direttamente il file con il Content-Type del formato corrispondente. Header della risposta:

  • X-Request-Id: ID della richiesta, da fornire per la risoluzione dei problemi
  • X-Snapi-Cache: HIT o MISS
  • X-Snapi-Truncated: true: la pagina intera è stata troncata perché supera 20,000 pixel
  • X-Snapi-Format-Changed: webp: un PNG superiore a 5 MB è stato convertito automaticamente in WebP (nessuna conversione se format=png è specificato esplicitamente)

Impostando response_type=json, vengono restituiti l'URL del file e i metadati:

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

# Risposta
{
  "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 firmati

Vuoi inserire l'URL dello screenshot direttamente in <img src> senza esporre l'API Key? Calcola la firma sul tuo server usando il secret associato all'API Key (disponibile nella pagina API Key della console):

Regole di firma
canonical = Ordina tutti i parametri, tranne signature, in ordine lessicografico per nome.
            Codifica ogni coppia come k=urlencode(v) (RFC 3986, con gli spazi codificati come %20) e uniscile con &
signature = hex(HMAC-SHA256(secret, canonical))

Una firma valida autentica la richiesta. Qualsiasi modifica ai parametri (ad esempio a url) invalida la firma. Il parametro facoltativo expires (secondi Unix) è incluso nella firma; dopo la scadenza viene restituito signature_expired. Per aggiornare la cache, modifica un parametro privo di effetti (come v=2) e genera una nuova firma. Gli URL firmati supportano solo GET.

import crypto from "node:crypto";

// Codifica RFC 3986 (spazi come %20), conforme alle regole del server
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="Anteprima della pagina web" />

Cache

Le richieste con gli stessi parametri usano la cache (omettere un parametro equivale a specificarne il valore predefinito; i parametri di autenticazione e della cache sono esclusi dal confronto). In caso di riscontro nella cache, la modalità binaria restituisce un reindirizzamento 302 all'URL del file con Cache-Control: il browser segue automaticamente il reindirizzamento e memorizza il file nella cache. La modalità JSON restituisce cache_hit: true.

Le risposte dalla cache non vengono addebitate.Le richieste simultanee con gli stessi parametri generano un solo rendering.

Limiti e addebiti

  • Vengono addebitate solo le richieste riuscite (2xx) non servite dalla cache: 1 credito per screenshot, 2 crediti per PDF.
  • Timeout, rendering non riusciti, errori del sito di destinazione, parametri non validi e autenticazione non riuscita non consumano crediti.
  • I crediti si rinnovano ogni mese solare (UTC). Il piano gratuito include 100 crediti al mese; una volta esauriti, viene restituito quota_exceeded.
  • Se superi il limite di richieste al secondo o di richieste simultanee, viene restituito 429. L'header Retry-After indica quanti secondi attendere prima di riprovare.
  • Durata massima di un rendering: 30 secondi. Download massimo per pagina: 50 MB. Dimensione massima del file generato: 50 MB.

Consulta i limiti di ogni piano: Prezzi.

Codici di errore

Tutte le risposte di errore sono in JSON:

Formato degli errori
{
  "error": {
    "code": "render_failed",
    "message": "target responded with an error status",
    "upstream_status": 404,
    "request_id": "req_01j…"
  }
}
HTTPcodeSignificatoAddebitato
400invalid_paramsParametro mancante o fuori intervallo; message indica qualeNo
400html_too_largehtml supera 2 MBNo
401unauthorizedAPI Key mancante, non valida o disattivataNo
401signature_invalidFirma non corrispondente (parametri modificati o secret errato)No
401signature_expiredL'URL firmato ha superato la scadenza expiresNo
403url_blockedAccesso all'indirizzo di destinazione non consentito (ad esempio indirizzi di rete interna o localhost)No
408render_timeoutIl rendering ha superato 30 secondiNo
422render_failedIl sito di destinazione ha restituito 4xx/5xx, la risoluzione DNS è fallita o si è verificato un errore di certificato; include upstream_statusNo
422selector_not_foundselector non corrisponde ad alcun elemento oppure l'attesa di wait_for_selector è scadutaNo
422too_many_redirectsSuperato il limite di 5 reindirizzamentiNo
429rate_limitedSuperato il limite di richieste al secondo; riprova seguendo Retry-AfterNo
429concurrency_exceededSuperato il limite di richieste simultaneeNo
429quota_exceededCrediti mensili esauriti (piano gratuito)No
429too_many_batchesSuperato il limite di 5 batch in corsoNo
500internalErrore del server. Contattaci fornendo request_idNo

Batch asincroni

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

Invia da 1 a 100 URL in una sola richiesta: l'endpoint restituisce subito un ID attività (HTTP 202) e acquisisce gli screenshot uno alla volta in background. Ideale per acquisizioni in batch senza mantenere aperta la connessione. Puoi interrogare periodicamente l'endpoint di stato oppure specificare webhook_url per ricevere una notifica al completamento. L'autenticazione è la stessa degli endpoint sincroni (solo tramite header o parametro access_key; gli URL firmati non sono supportati).

ParametroTipoValore predefinitoDescrizione
typeenumscreenshotscreenshot (screenshot) o pdf.
urlsstring[]—URL da elaborare, da 1 a 100. Se anche uno solo non è valido, l'intera richiesta restituisce 400; message ne indica la posizione.
optionsobject{}Parametri comuni a tutti gli URL, gli stessi degli endpoint sincroni (ad esempio full_page, format, viewport_width, cache_ttl). Non possono includere url, html, response_type.
webhook_urlstring—Facoltativo. URL a cui inviare la notifica al termine di tutte le elaborazioni. Vedi «Webhook di completamento».
Inviare un batch
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"
  }'

# Risposta 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 }
  ]
}

Verifica l'avanzamento: lo stato status dell'attività passa da queued (in coda) a running (in elaborazione), fino a completed (tutte le elaborazioni terminate, riuscite o meno). Ogni URL ha uno stato tra pending, running, succeeded e failed. In caso di successo, result contiene l'URL del file; in caso di errore, error contiene il codice di errore (gli stessi degli endpoint sincroni).

Verificare lo stato di un batch
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" }
    }
  ]
}
  • Gli addebiti seguono le stesse regole degli endpoint sincroni: ogni URL elaborato con successo consuma 1 credito per screenshot o 2 per PDF. Le risposte dalla cache e le elaborazioni non riuscite non consumano crediti.
  • Se i crediti sono già esauriti al momento dell'invio, viene restituito subito quota_exceeded. Se si esauriscono durante l'elaborazione, gli URL rimanenti falliscono con quota_exceeded.
  • Ogni account può avere al massimo 5 batch in corso. Oltre questo limite viene restituito too_many_batches.
  • La validità dei file generati è determinata da options.cache_ttl (1 giorno per impostazione predefinita). Scaricali e salvali prima della scadenza.
  • Nelle risposte di stato e nei webhook, webhook.url viene mostrato solo fino al percorso, senza la query string.

Webhook di completamento

Se specifichi webhook_url, al completamento di tutte le elaborazioni inviamo a quell'URL una richiesta POST con corpo JSON: { "event": "batch.completed", "batch": { … } }. Il campo batch è identico alla risposta dell'endpoint di stato. Header della richiesta:

  • X-Snapi-Signature: t=1760779812,v1=5d3f…: t è l'ora di invio (secondi Unix), v1 è la firma
  • X-Snapi-Event: batch.completed, X-Snapi-Delivery-Attempt: 1 (numero del tentativo di invio)
Regole di firma
v1 = hex(HMAC-SHA256(secret, t + "." + Corpo originale della richiesta))
secret è il segreto di firma associato all’API Key usata per inviare il task (disponibile nella pagina API Key della console)
  • Restituisci una risposta 2xx entro 10 secondi, altrimenti l’invio sarà considerato fallito. In caso di errore, verranno effettuati nuovi tentativi dopo 10 secondi, 1 minuto e 5 minuti (fino a 4 tentativi in totale).
  • In casi molto rari potresti ricevere notifiche duplicate per lo stesso task. Usa batch.id per deduplicarle.
  • Per verificare la firma, calcolala sui byte originali ricevuti e controlla che t non differisca dall’ora corrente di più di qualche minuto, per prevenire attacchi replay.
  • L’indirizzo di callback deve essere un indirizzo http(s) accessibile pubblicamente e non può puntare a una rete privata.
import crypto from "node:crypto";

// rawBody deve contenere i byte originali ricevuti, senza eseguire prima JSON.parse e poi serializzare di nuovo
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; // Protezione dagli attacchi replay
  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)

Verifica dei consumi

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", ... }

Controllo di stato

GET/v1/health

Non richiede autenticazione. Restituisce lo stato del nodo e del pool di browser (totali, occupati, disponibili).