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.

  1. 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.
  2. 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 con only_main_content. Vedi Qualità del Markdown.
  3. Estrai. Se hai richiesto l'output structured, perceive esegue un passaggio euristico per metadata della pagina, JSON-LD, intestazioni e tabelle. Se invii anche uno schema e il tuo piano include il livello LLM, un passaggio assistito da LLM completa lo schema quando il passaggio euristico non basta.
  4. 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 deductions per il motivo e status_code per 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 attributo language nudo, o un wrapper dell'evidenziatore) e normalizzato, così arriva ```python invece 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 YesNo ed EvaluationDeploymentProduction; 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-ignore o data-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.
Riservati, non ancora attivi. 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 warnings invece di spendere oltre il limite. Su un piano senza livello LLM, ottieni solo dati structured euristici.


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=markdown trasmette 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 risponde 410.
  • GET /v2/perceive/batch/{job_id}?direct_download=true trasmette lo ZIP del batch per i batch con output_mode: "zip" il cui archivio è pronto, e negli altri casi risponde 400.

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.