API di Web Scraping per Markdown, Screenshot e Dati Strutturati#
POST /v2/perceive è l'API di web scraping di EnConvert: renderizza un URL
una sola volta in un browser headless reale (JavaScript eseguito, contenuti
lazy caricati) e restituisce ogni output richiesto da quel singolo render:
Markdown pulito (di default solo il contenuto principale, senza il chrome
del sito), HTML pulito o raw, uno screenshot, un PDF, l'inventario di link
e immagini, e dati strutturati (metadata della pagina, JSON-LD,
intestazioni, tabelle). Gli output file tornano come URL di download
pre-firmati a breve durata, il blocco strutturato inline, e i batch oltre
10 URL vengono eseguiti in modo asincrono dietro un job_id da interrogare
con il polling. Un'unica richiesta sostituisce un'intera serie di chiamate
separate: url-to-markdown, url-to-screenshot, url-to-pdf, più il tuo
scraping personalizzato.
Ecco la chiamata utile più semplice. Invia un URL e ricevi Markdown pulito e i metadata strutturati della pagina:
curl -X POST https://api.enconvert.com/v2/perceive \
-H "X-API-Key: sk_your_private_key" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com/pricing",
"outputs": ["markdown", "structured"]
}'
La risposta include un URL di download pre-firmato per il file Markdown e il blocco strutturato inline:
{
"operation_id": "per_3f9a2c1b8e7d4a6f90b1c2d3e4f5a6b7",
"status": "completed",
"url": "https://example.com/pricing",
"url_final": "https://example.com/pricing",
"content_hash": "9f2b8c1a...d4e5",
"render_quality": 0.93,
"cache_hit": false,
"outputs": {
"markdown": {
"url": "https://spaces.example.com/...signed...",
"object_key": "env/files/4127/v2-perceive/per_3f9a..._markdown.md",
"size_bytes": 8421,
"content_type": "text/markdown; charset=utf-8",
"expires_in": 900
}
},
"structured": {
"metadata": {
"title": "Pricing",
"description": "Simple, usage-based pricing."
},
"structured_data": [
{"@type": "Product", "name": "Studio", "offers": {"price": "99"}}
]
},
"extraction_tier": "heuristic",
"tokens": {"input": 0, "output": 0},
"cost_cents": 0.0,
"duration_ms": 6230,
"warnings": []
}
Endpoint#
| Metodo | Percorso | Scopo |
|---|---|---|
POST |
/v2/perceive |
Esegue il perceive di un singolo URL e restituisce gli output richiesti. |
GET |
/v2/perceive/{operation_id} |
Recupera di nuovo un'operazione passata con URL di download firmati di fresco. |
POST |
/v2/perceive/batch |
Esegue il perceive di un massimo di 1.000 URL che condividono un unico set di opzioni. |
GET |
/v2/perceive/batch/{job_id} |
Effettua il polling dello stato e dei risultati per-URL di un batch. |
DELETE |
/v2/perceive/batch/{job_id} |
Annulla un batch in esecuzione. |
Content-Type: application/json su ogni POST.
Autenticazione#
Autenticati con una chiave privata nell'header X-API-Key per le chiamate
server-to-server. Questo è il percorso usato dagli esempi seguenti.
X-API-Key: sk_your_private_key
Funzionano anche le chiavi pubbliche con un token bearer JWT, usando lo
stesso flusso di ogni altro endpoint: genera un token con la tua chiave
pk_, poi invialo come Authorization: Bearer <token>. Il flusso completo,
incluso il domain locking e il refresh del token, è nella
guida all'autenticazione.
Ogni chiave API porta con sé un'allowlist di endpoint consentiti. Se
/v2/perceive non è nella lista della chiave, la richiesta viene rifiutata
con 403.
Come funziona perceive#
Una richiesta attiva un singolo render del browser tramite un singleton Chrome headless condiviso, poi materializza ogni output da quel render. Non paghi mai la stessa pagina due volte in un'unica chiamata.
- Render. La pagina viene recuperata tramite un fallback multi-engine automatico: prima una veloce impronta TLS da browser reale, con escalation a Chrome headless quando la pagina è bloccata o richiede JavaScript, e ancora una volta a un render con hardening stealth quando una pagina sembra ancora bloccata da protezioni anti-bot, così più pagine del mondo reale tornano con contenuti utilizzabili. Nel browser, i banner dei cookie vengono chiusi, la pagina viene scrollata per attivare i contenuti lazy, gli header sticky vengono gestiti, e alle immagini viene dato il tempo di caricarsi. È la stessa pipeline di cattura che alimenta l'endpoint url-to-pdf.
- Materializza. Dal DOM renderizzato, perceive costruisce tutto ciò
che hai elencato in
outputs: Markdown, HTML pulito/raw, link, immagini, uno screenshot, un PDF. Il DOM viene prima normalizzato affinché il Markdown rifletta ciò che vede un lettore: i fence di codice mantengono il loro linguaggio, i link delle card la loro struttura, e gli elementi di interfaccia vengono rimossi cononly_main_content. Vedi Qualità del Markdown. - Estrai. Se hai richiesto l'output
structured, perceive esegue un passaggio euristico per metadata della pagina, JSON-LD, intestazioni e tabelle. Se invii anche unoschemae il tuo piano include il livello LLM, un passaggio assistito da LLM completa lo schema quando il passaggio euristico non basta. - Valuta. Un punteggio di render-quality (0.0–1.0) distingue un
render reale da uno fallito. Punteggi sotto 0.40 indicano un render
fallito: una pagina anti-bot, un login wall, una pagina di errore
HTTP, un soft 404 o un guscio vuoto. Consulta
deductionsper il motivo estatus_codeper lo stato del server di origine.
Gli output binari e testuali (Markdown, HTML, screenshot, PDF, il JSON di
link e immagini) vengono caricati sullo storage e restituiti come URL
pre-firmati che scadono dopo 15 minuti. Il blocco structured viene
restituito inline nel JSON. Recupera di nuovo un'operazione con
GET /v2/perceive/{operation_id} per ottenere un nuovo set di URL firmati.
Parametri della richiesta#
La validazione è rigorosa: una chiave della richiesta sconosciuta allo
schema viene rifiutata con 422 indicando il campo interessato. Le chiavi
sconosciute non vengono mai ignorate silenziosamente. Ogni body
422 include inoltre un array errors di primo livello con messaggi
leggibili, accanto alla lista detail leggibile dalle macchine.
Principali#
| Parametro | Tipo | Predefinito | Descrizione |
|---|---|---|---|
url |
string |
-- | La pagina su cui eseguire il perceive. Deve iniziare con http:// o https://. Massimo 2,048 caratteri. Obbligatorio. |
outputs |
string[] |
["markdown", "structured"] |
Quali output produrre. Vedi Output. |
extract |
string[] |
[] |
Quali campi strutturati estrarre quando structured è presente in outputs. Vedi Estrazione strutturata. |
schema |
object |
null |
Uno schema JSON che descrive i campi che vuoi estrarre. Attiva il livello di estrazione LLM sui piani che lo includono. |
only_main_content |
boolean |
true |
Rimuove il chrome del sito (navigazione, header, footer, sidebar, banner dei cookie, nodi nascosti) e gli elementi di interfaccia (pulsanti, barre di tab, widget «Questa pagina ti è stata utile?», etichette destinate ai soli screen reader, breadcrumb) dall'output markdown e dall'extract main_content, protetto da una guardia di fedeltà: se la rimozione eliminasse troppo contenuto reale, viene restituita la pagina completa e viene aggiunto un warning. Gli URL delle immagini vengono resi come il loro testo alt (la lista completa delle immagini resta disponibile via outputs: ["images"]). Imposta false per la pagina completa, senza rimuovere nulla. Vedi Qualità del Markdown. |
truncate_data_arrays |
boolean |
non impostato | Comprime lunghe sequenze di letterali numerici (vettori di embedding grezzi, dump di tensori stampati nelle celle di output dei notebook) in un campione iniziale più un conteggio, ad es. ... [truncated 1520 of 1536 values]. Se non impostato segue only_main_content: attivo quando la pagina viene ripulita, inattivo quando hai chiesto la pagina così com'è. Imposta true o false per controllarlo esplicitamente. |
allow_degraded |
boolean |
false |
Restituisce il render anche quando è una challenge anti-bot o una pagina di blocco priva di contenuto. Per impostazione predefinita un render simile fallisce con 502 invece di consegnare il testo dell'interstiziale come se fosse la pagina. |
direct_download |
boolean |
false |
Restituisce i byte dell'artefatto direttamente come body della risposta HTTP invece di una busta JSON. Richiede esattamente un output che produca un artefatto. Solo per richieste a URL singolo, perché l'endpoint batch lo rifiuta con 422. Vedi Download diretto. |
cache_mode |
string |
"enabled" |
enabled, bypass, o refresh. Vedi Caching. |
Output#
outputs accetta qualsiasi combinazione di questi nomi:
| Output | Restituito come | Cosa ottieni |
|---|---|---|
markdown |
URL firmato | Markdown pulito della pagina. Con only_main_content (default true) il chrome del sito (navigazione, header, footer, sidebar, banner dei cookie, nodi nascosti) viene rimosso dietro una guardia di fedeltà, e gli URL delle immagini vengono resi come il loro testo alt. I blocchi di codice mantengono il loro linguaggio sul fence (```python) in entrambe le modalità. Imposta only_main_content: false per la pagina completa. Vedi Qualità del Markdown. |
html_cleaned |
URL firmato | L'HTML renderizzato con script, stili e boilerplate rimossi. |
html_raw |
URL firmato | L'HTML renderizzato completo, esattamente come prodotto dal browser. |
screenshot |
URL firmato | Un PNG del viewport, nella dimensione richiesta (o predefinita). |
screenshot_full_page |
URL firmato | Un PNG a pagina intera che cattura l'intera altezza di scroll. |
pdf |
URL firmato | Un PDF della pagina. Accetta l'intera superficie di pdf_options (vedi sotto). |
links |
URL firmato | Un array JSON di ogni link trovato, con URL assoluti e testo dell'ancora. |
images |
URL firmato | Un array JSON di ogni immagine, con src assoluto e testo alt. |
structured |
JSON inline | Dati strutturati estratti dalla pagina (il campo di risposta structured). |
Qualità del Markdown#
Prima che la pagina venga convertita, il DOM renderizzato viene normalizzato affinché il Markdown rifletta ciò che vede un lettore e non come la pagina è stata costruita. Questo avviene a ogni render, quindi il risultato non dipende da quale strategia di estrazione vince per una determinata pagina.
Sempre applicato, in entrambe le modalità di only_main_content:
- I fence di codice mantengono il loro linguaggio. Il linguaggio
viene letto dalla convenzione usata dal sito
(
class="language-python",data-lang, un attributolanguagenudo, o un wrapper dell'evidenziatore) e normalizzato, così arriva```pythoninvece di un fence spoglio. - I link delle card restano leggibili. Un link che avvolge un
titolo e una descrizione diventa un titolo collegato seguito dalla
sua descrizione, invece di un unico link appiccicato come
[DatabaseSupabase provides a full Postgres database...]. L'URL di destinazione viene preservato. - I titoli restano su una sola riga. Un titolo il cui testo si
trova dentro un elemento annidato non emette più un
##spoglio con il testo abbandonato sotto. - Gli elementi adiacenti non si concatenano più. I layout che
spaziano i loro elementi con il CSS anziché con spazi producevano
YesNoedEvaluationDeploymentProduction; ora si leggono come parole separate. - I caratteri invisibili vengono rimossi: spazi a larghezza zero usati come etichette di ancoraggio, trattini morbidi e glifi della Private Use Area dei font di icone, che arrivano come token non stampabili.
- Gli elementi vuoti vengono scartati: elementi
<i>con la sola icona che venivano resi come__vaganti, e link con etichetta vuota.
Inoltre, con only_main_content: true:
- I controlli di interfaccia vengono rimossi: pulsanti, barre di tab, suggerimenti di scorciatoie da tastiera, azioni «Copy page» / «On this page» e widget di valutazione «Questa pagina ti è stata utile? Sì/No». Un controllo che porta contenuto reale (una domanda di FAQ, il corpo di una card cliccabile) viene mantenuto.
- Il testo destinato ai soli screen reader viene rimosso: skip link e le etichette «Section titled ...» che molti temi di documentazione attaccano a ogni titolo.
- Il non-contenuto dichiarato dal sito viene rispettato: blocchi
marcati
data-nosnippet,data-pagefind-ignoreodata-noindex, a meno che non contengano titoli o codice. - I blocchi duplicati vengono uniti: i design responsive che spediscono una copia desktop e una mobile della stessa barra, e i caroselli che pre-renderizzano ogni fotogramma, compaiono una volta sola.
- Breadcrumb ed etichette di occhiello sopra il titolo della pagina vengono scartati.
Il contenuto differito viene deliberatamente mantenuto: un pannello di tab inattivo all'interno della regione di contenuto contiene un vero esempio di codice (l'esempio Python in un tab, quello JavaScript in un altro), così entrambi arrivano nel Markdown e non solo il tab che al momento del render risultava selezionato.
Rendering e attesa#
| Parametro | Tipo | Predefinito | Descrizione |
|---|---|---|---|
viewport |
object |
1920 x 1080 |
{"width": <int>, "height": <int>}. Larghezza 320–3840, altezza 240–2160. |
mobile |
boolean |
false |
Esegue il render con un viewport mobile (390 x 844) a meno che viewport non sia impostato esplicitamente. |
wait_for |
string |
null |
Attende dopo la navigazione un selettore CSS (".price" o "css:.price") o un'espressione JS ("js:window.dataReady === true"). |
wait_timeout_ms |
integer |
30000 |
Per quanto tempo wait_for può attendere, in millisecondi. 0–60,000. Un timeout degrada a un warning; la pagina viene catturata così com'è. |
js_code |
string |
null |
JavaScript da eseguire sulla pagina dopo la navigazione. Massimo 20,000 caratteri. Un errore diventa un warning, non un fallimento. |
block_resources |
string[] |
[] |
Tipi di risorsa da interrompere prima che si carichino. Uno tra image, media, font, stylesheet, script, xhr, fetch, websocket, manifest, other. Utile per render più veloci, solo testo. |
respect_robots |
boolean |
false |
Quando è true, un URL non consentito dal robots.txt del sito viene rifiutato con 403. |
pdf_options |
object |
null |
Formato pagina, margini, header, footer, scala e orientamento per l'output pdf. Stesso oggetto di url-to-pdf. Senza pdf_options, perceive produce una singola pagina continua, byte-identica a V1 url-to-pdf. |
Richieste autenticate e personalizzate#
| Parametro | Tipo | Predefinito | Descrizione |
|---|---|---|---|
auth |
object |
null |
HTTP Basic Auth per la pagina di destinazione: {"username": "...", "password": "..."}. |
cookies |
array |
null |
Cookie da iniettare prima della navigazione. Massimo 50. Ognuno richiede name, value, e uno tra domain o url. |
headers |
object |
null |
Header di richiesta personalizzati. Massimo 20. Nomi bloccati: host, content-length, transfer-encoding, connection, upgrade, te, trailer. |
proxy_url (Production+),
geolocation e action_chain sono accettati dallo
schema della richiesta ma oggi restituiscono 422. Arriveranno in
una release futura; inviarli ora ti dice esattamente quale funzione non è
pronta invece di ignorarla silenziosamente.
Estrazione strutturata#
Quando structured è presente in outputs, la lista extract controlla
quali campi perceive estrae. Se non richiedi nulla, il default è
metadata e structured_data.
Valore di extract |
Campo in structured |
Stato |
|---|---|---|
metadata |
metadata |
Attivo |
structured_data |
structured_data (JSON-LD) |
Attivo |
headings |
headings |
Attivo |
tables |
tables |
Attivo |
main_content |
main_content (testo, limitato a 50,000 caratteri) |
Attivo |
all |
si espande in tutti i campi attivi sopra elencati | Attivo |
prices |
-- | Non ancora attivo: restituisce un warning, omesso |
contacts |
-- | Non ancora attivo: restituisce un warning, omesso |
technologies |
-- | Non ancora attivo: restituisce un warning, omesso |
Per essere diretti: prices, contacts e technologies sono nomi
riservati. Richiederne uno oggi non genera un errore. Il nome finisce
nell'array warnings e viene rimosso da structured.
Estrazione guidata da schema#
Invia uno schema per estrarre campi specifici in structured.extracted:
{
"url": "https://example.com/product/widget",
"outputs": ["markdown", "structured"],
"schema": {
"type": "object",
"properties": {
"name": {"type": "string"},
"price": {"type": "number"},
"in_stock": {"type": "boolean"}
}
}
}
Il livello di estrazione assistita da LLM completa lo schema e
si attiva solo quando valgono tutte queste condizioni: hai inviato uno
schema, il tuo piano include il livello LLM (Indie e superiori), la
pagina non è stata valutata come bloccata, e il passaggio euristico ha
lasciato vuoti i campi dello schema. Quando viene eseguito,
extraction_tier vale "llm", e tokens e cost_cents riportano il
costo di quell'estrazione; altrimenti extraction_tier vale "heuristic"
ed entrambi sono zero.
Nota. L'estrazione tramite schema ha un tetto rigido per proteggere la tua spesa: una singola estrazione è limitata per richiesta, e la spesa del progetto attinge dal tuo saldo mensile di crediti AI ($5 / $15 / $40 al mese su Indie / Studio / Production; i crediti non usati si accumulano). L'estrazione LLM consuma crediti, non ops. Se viene raggiunto un tetto o il saldo è esaurito, perceive restituisce il risultato euristico con una nota in
warningsinvece di spendere oltre il limite. Su un piano senza livello LLM, ottieni solo datistructuredeuristici.
Risposta#
Sia POST /v2/perceive che GET /v2/perceive/{operation_id} restituiscono
lo stesso oggetto.
| Campo | Tipo | Descrizione |
|---|---|---|
operation_id |
string |
ID opaco (per_...). Usalo con l'endpoint GET e citalo quando contatti il supporto. |
status |
string |
queued, processing, completed, o failed. |
url |
string |
L'URL che hai inviato. |
url_final |
string |
L'URL dopo i redirect. |
content_hash |
string |
SHA-256 della pagina renderizzata. Determina la cache di 1 ora. |
render_quality |
number |
0.0–1.0. Punteggi sotto 0.40 indicano un render fallito: una pagina anti-bot, un login wall, una pagina di errore HTTP, un soft 404 o un guscio vuoto. Consulta deductions per il motivo e status_code per lo stato del server di origine. |
status_code |
integer |
Stato HTTP della risposta finale del documento principale (es. 200, 404). null quando è sconosciuto. |
deductions |
object |
Detrazioni di render-quality con nome che sono scattate, es. {"http_error": 0.7}, {"soft_404": 0.65}, {"login_wall": 0.65}. Vuoto per un render pulito. |
options_echo |
object |
Eco delle opzioni della richiesta che il server ha applicato. I segreti sono ridotti a booleani (auth_provided, cookies_provided, headers_provided, js_code_provided, schema_provided, pdf_options_provided). Le opzioni semplici (outputs, only_main_content, truncate_data_arrays, allow_degraded, extract, cache_mode, mobile, respect_robots, direct_download, wait_for, wait_timeout_ms, viewport, block_resources) vengono restituite così come applicate. truncate_data_arrays viene restituito come booleano risolto, così anche lasciandolo non impostato sai in che modo è stato deciso. |
cache_hit |
boolean |
true quando il risultato proviene dalla cache invece che da un render fresco. |
outputs |
object |
Mappa da nome dell'output a {url, object_key, size_bytes, content_type, expires_in}. Gli URL firmati scadono in 900 secondi. |
structured |
object |
Dati strutturati inline, presenti quando è stato richiesto structured. |
extraction_tier |
string |
heuristic, css, o llm. |
tokens |
object |
Token LLM {input, output} utilizzati. Zero a meno che il livello LLM non sia stato eseguito. |
cost_cents |
number |
Costo LLM in centesimi per questa operazione. Zero a meno che il livello LLM non sia stato eseguito. |
duration_ms |
integer |
Tempo di render end-to-end. |
error |
string |
Impostato solo quando status è failed. |
warnings |
string[] |
Note non fatali: un timeout di wait_for, un extract saltato, un flag di pagina bloccata, un fallback di only_main_content alla pagina completa, una nota che lunghi array di dati numerici sono stati troncati. |
Recuperare un'operazione#
Gli URL firmati scadono dopo 15 minuti. Per scaricare un output più tardi, recupera di nuovo l'operazione e perceive firma di nuovo ogni URL a partire dagli object key salvati. Non avviene alcun nuovo render, quindi questo non consuma ops.
curl https://api.enconvert.com/v2/perceive/per_3f9a2c1b8e7d4a6f90b1c2d3e4f5a6b7 \
-H "X-API-Key: sk_your_private_key"
Un operation ID sconosciuto, o uno che appartiene a un progetto diverso,
restituisce 404. L'esistenza non viene mai rivelata tra progetti.
Download diretto#
Di default ogni output file torna come URL pre-firmato da recuperare con
una seconda richiesta. Imposta direct_download: true sul POST per
saltare la busta: il body della risposta HTTP è i byte
dell'artefatto, senza JSON, senza URL firmato e senza una seconda
richiesta. La richiesta deve produrre esattamente un output che generi un
artefatto (outputs: ["markdown"], outputs: ["pdf"], …), altrimenti
viene rifiutata con 400. I metadata che sarebbero finiti nel JSON
viaggiano invece negli header della risposta: Content-Disposition,
X-Operation-Id, X-Object-Key, X-Cache-Hit, X-Render-Quality,
X-Source-Status-Code, X-Content-Hash e X-Warnings-Count.
curl -X POST https://api.enconvert.com/v2/perceive \
-H "X-API-Key: sk_your_private_key" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com/blog/post",
"outputs": ["markdown"],
"direct_download": true
}' \
-o post.md
Gli endpoint GET trasmettono gli artefatti salvati allo stesso modo:
GET /v2/perceive/{operation_id}?direct_download=true&output=markdowntrasmette un artefatto di un'operazione passata.outputè obbligatorio quando l'operazione ha prodotto più di un artefatto. Un artefatto oltre la finestra di retention del tuo piano risponde410.GET /v2/perceive/batch/{job_id}?direct_download=truetrasmette lo ZIP del batch per i batch conoutput_mode: "zip"il cui archivio è pronto, e negli altri casi risponde400.
direct_download vale solo per URL singoli: POST /v2/perceive/batch
lo rifiuta con 422. Imposta output_mode su "zip" e scarica
l'archivio. Vedi Perceive in batch.
Perceive in batch#
POST /v2/perceive/batch esegue il perceive di un elenco di URL che
condividono un unico blocco options. Ogni URL viene renderizzato tramite
la stessa pipeline di una singola chiamata e produce la propria riga di
operazione.
curl -X POST https://api.enconvert.com/v2/perceive/batch \
-H "X-API-Key: sk_your_private_key" \
-H "Content-Type: application/json" \
-d '{
"urls": [
"https://example.com/a",
"https://example.com/b",
"https://example.com/c"
],
"options": {"outputs": ["markdown"]},
"output_mode": "manifest"
}'
I batch di 10 URL o meno vengono eseguiti inline e rispondono 200 con
ogni risultato popolato. I batch più grandi rispondono 202 con un
job_id; gli URL vengono processati uno alla volta e puoi effettuare il
polling dei risultati:
curl https://api.enconvert.com/v2/perceive/batch/{job_id} \
-H "X-API-Key: sk_your_private_key"
La risposta del batch riporta il progresso aggregato e include un risultato perceive completo per URL una volta renderizzato:
{
"job_id": "bat_8c1a...",
"status": "partial",
"output_mode": "manifest",
"total": 3,
"completed": 2,
"failed": 1,
"pending": 0,
"items": [
{"operation_id": "per_...", "status": "completed", "url": "https://example.com/a"},
{"operation_id": "per_...", "status": "completed", "url": "https://example.com/b"},
{"operation_id": "per_...", "status": "failed", "url": "https://example.com/c"}
]
}
status è queued, processing, completed, failed, partial
(alcuni URL riusciti, altri falliti), o canceled. Imposta output_mode
su zip per raggruppare ogni artefatto in un unico ZIP, restituito nel
campo zip una volta terminato il batch.
Durevole e ripristinabile#
I batch sono a prova di riavvio. Se il servizio si riavvia mentre un batch è in corso, il batch riprende automaticamente e renderizza di nuovo solo gli URL che non erano stati completati, così gli URL già completati mantengono i loro artefatti. Non devi mai reinviare un batch a causa di un riavvio.
Annullare un batch#
DELETE /v2/perceive/batch/{job_id} annulla un batch in esecuzione. Il
worker si ferma tra un URL e l'altro, quindi gli URL già renderizzati
mantengono i loro risultati e i rimanenti restano non avviati. La chiamata
è idempotente, quindi annullare un batch già terminato restituisce
semplicemente il suo stato attuale, e lo status del batch diventa
canceled.
curl -X DELETE https://api.enconvert.com/v2/perceive/batch/{job_id} \
-H "X-API-Key: sk_your_private_key"
Caching#
cache_mode controlla come perceive tratta la sua cache dei risultati di
1 ora, la cui chiave è composta dal tuo progetto, dall'URL e dalle opzioni
della richiesta che influenzano il render.
cache_mode |
Comportamento |
|---|---|
enabled (predefinito) |
Restituisce un risultato in cache quando una richiesta identica è stata renderizzata nell'ultima ora. cache_hit è true, cost_cents è 0. |
bypass |
Salta la cache ed esegue un render fresco. |
refresh |
Esegue un render fresco e sostituisce la voce in cache. |
Da segnalare: un cache hit conta comunque come un'operazione perceive rispetto alla tua quota mensile di ops. La quota misura le operazioni anziché i render del browser, quindi la cache ti fa risparmiare tempo di render, non quota.
Esempi di codice#
curl: solo Markdown#
curl -X POST https://api.enconvert.com/v2/perceive \
-H "X-API-Key: sk_your_private_key" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com/blog/post",
"outputs": ["markdown"]
}'
curl: Markdown più dati strutturati#
curl -X POST https://api.enconvert.com/v2/perceive \
-H "X-API-Key: sk_your_private_key" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com/pricing",
"outputs": ["markdown", "structured"],
"extract": ["metadata", "structured_data", "tables"]
}'
curl: output completi più PDF#
curl -X POST https://api.enconvert.com/v2/perceive \
-H "X-API-Key: sk_your_private_key" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com/report",
"outputs": ["markdown", "html_cleaned", "screenshot_full_page", "pdf"],
"pdf_options": {"format": "A4", "print_background": true}
}'
Python#
import requests
response = requests.post(
"https://api.enconvert.com/v2/perceive",
headers={"X-API-Key": "sk_your_private_key"},
json={
"url": "https://example.com/pricing",
"outputs": ["markdown", "structured"],
"extract": ["metadata", "tables"],
},
)
response.raise_for_status()
data = response.json()
# Download the Markdown artifact from its signed URL
markdown_url = data["outputs"]["markdown"]["url"]
markdown_text = requests.get(markdown_url).text
print(data["structured"])
print(markdown_text)
Node.js#
const res = await fetch("https://api.enconvert.com/v2/perceive", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-Key": "sk_your_private_key"
},
body: JSON.stringify({
url: "https://example.com/pricing",
outputs: ["markdown", "structured"],
extract: ["metadata", "tables"]
})
});
const data = await res.json();
// Download the Markdown artifact from its signed URL
const markdownText = await fetch(data.outputs.markdown.url).then(r => r.text());
console.log(data.structured);
console.log(markdownText);
Se chiami EnConvert da Claude, Cursor o un altro client MCP, la stessa
funzionalità è esposta come tool perceive_url. Vedi
la pagina del server MCP.
Risposte di errore#
| Stato | Condizione |
|---|---|
400 Bad Request |
L'URL non è http(s), contiene credenziali incorporate, o si risolve in un indirizzo privato, loopback o link-local (protezione SSRF). |
400 Bad Request |
auth non valido (manca username/password), cookies non valido (non è un array, oltre 50 voci, campi mancanti), o headers non valido (non è un oggetto, oltre 20 voci, nome bloccato). |
401 Unauthorized |
Chiave API / token JWT mancante o non valido. |
402 Payment Required |
Perceive non è incluso nel tuo piano attuale, oppure la tua quota mensile di ops è esaurita. |
403 Forbidden |
/v2/perceive non è tra gli endpoint consentiti della chiave API. |
403 Forbidden |
Il batch non è disponibile sul tuo piano, oppure la dimensione del batch supera il limite del tuo piano. |
403 Forbidden |
respect_robots=true e il robots.txt del sito non consente l'URL. |
404 Not Found |
operation_id o job_id sconosciuto, oppure di proprietà di un altro progetto. |
422 Unprocessable Entity |
Validazione della richiesta fallita (enum non valido in outputs/extract, wait_timeout_ms fuori range, viewport fuori dai limiti, una chiave della richiesta sconosciuta). |
422 Unprocessable Entity |
È stato inviato proxy_url, geolocation, o action_chain. Tutti e tre sono riservati per una release futura. |
500 Internal Server Error |
Il render è fallito. Il messaggio include l'operation_id da citare al supporto. |
502 Bad Gateway |
Tutti i motori sono stati bloccati e l'origine ha restituito una challenge anti-bot senza contenuto di pagina dietro di essa. Riprova più tardi, oppure invia allow_degraded: true per ricevere la pagina della challenge così com'è. |
Le chiavi della richiesta sconosciute vengono rifiutate con un 422 che
indica il campo, su /v2/perceive, /v2/perceive/batch, /v2/discover
e /v2/lookup allo stesso modo. Non vengono mai ignorate silenziosamente.
Ogni body 422 include un array errors di primo livello con messaggi
leggibili, accanto alla lista detail grezza.
Il riferimento completo dei codici di stato è nella guida ai codici di errore.
Limiti#
| Limite | Valore |
|---|---|
| Lunghezza URL | 2,048 caratteri |
wait_timeout_ms |
0–60,000 ms |
Lunghezza js_code |
20,000 caratteri |
| Larghezza viewport | 320–3,840 px |
| Altezza viewport | 240–2,160 px |
| Cookie per richiesta | 50 |
| Header personalizzati per richiesta | 20 |
Extract main_content |
50,000 caratteri |
| URL batch per richiesta | 1,000 (tetto dello schema) |
| Soglia batch inline | 10 URL (i batch più grandi vengono eseguiti in modo asincrono) |
| TTL della cache dei risultati | 1 ora |
| Scadenza URL firmato | 15 minuti |
| Ops mensili (condivise tra tutti gli endpoint) | 500 / 3.000 / 15.000 / 50.000 per livello; vedi i prezzi |
Domande frequenti#
Come converto una pagina web in Markdown con una API REST?#
Invia POST /v2/perceive con {"url": "...", "outputs": ["markdown"]}. La pagina viene renderizzata in Chrome headless e la risposta include un URL di download pre-firmato per il file Markdown. Di default only_main_content rimuove il chrome del sito, così ricevi l'articolo, non la navigazione; imposta "only_main_content": false per la pagina completa, oppure aggiungi "direct_download": true per ricevere i byte del Markdown direttamente nel body della risposta.
Posso ottenere uno screenshot e il Markdown dallo stesso render?#
Sì. outputs accetta qualsiasi combinazione, quindi ["markdown", "screenshot"] (o screenshot_full_page per l'intera altezza di scroll) produce entrambi da un singolo render del browser. Non paghi mai la stessa pagina due volte in un'unica chiamata.
/v2/perceive renderizza pagine JavaScript?#
Sì. Ogni richiesta esegue un render reale in Chrome headless: i banner dei cookie vengono chiusi, la pagina viene scrollata per attivare i contenuti lazy, e puoi controllare la pagina prima della cattura con wait_for (un selettore CSS o un'espressione JS), js_code, e block_resources.
Perché il mio URL di download firmato ha smesso di funzionare?#
Gli URL firmati scadono dopo 15 minuti (expires_in: 900). Recupera di nuovo l'operazione con GET /v2/perceive/{operation_id} per ottenere URL firmati di fresco. Non avviene alcun nuovo render e non vengono consumate ops.
Un risultato in cache conta comunque nella mia quota?#
Sì. Un cache hit addebita una op, perché la quota mensile misura le operazioni anziché i render del browser. Imposta cache_mode su bypass per saltare la cache di 1 ora, oppure su refresh per eseguire un render fresco e sostituire la voce in cache.