---
seo_title: SDK Python conversione file: pip install enconvert | EnConvert
meta_desc: SDK Python ufficiale di EnConvert. Installalo con pip, converti oltre 40 formati e usa il namespace V2 per perceive, discover, distill, ingest e watch.
keywords: sdk conversione file python, convertire file con python, url in pdf python, api web scraping python, docx in pdf python, enconvert python sdk, pip install enconvert, heic in webp python, html in markdown python, pipeline rag ingestion python, monitoraggio modifiche pagine web python
---

# SDK Python per la conversione file

`enconvert` è il client Python ufficiale per l'API EnConvert: un solo `pip install`, una sola chiave API e metodi tipizzati per convertire file e per leggere il web live. Gira su Python 3.9 o successivo con un'unica dipendenza runtime, `requests`, e include type hint inline più un marcatore `py.typed`, così mypy e Pyright vedono tutto. Il client ha due superfici. Nove metodi di conversione coprono 43 coppie `{input}-to-{output}` più URL in PDF, screenshot e Markdown, supportati da tre helper di polling dello stato. Il namespace `client.v2` aggiunge la web intelligence: perceive, discover, lookup, distill, ingest e watch.

<div class="alert alert-info">
<strong>PyPI:</strong> <code>enconvert</code> &middot; <strong>Sorgente:</strong> <a href="https://github.com/conversionapi/python-sdk">conversionapi/python-sdk</a> &middot; <strong>Python:</strong> 3.9+ &middot; <strong>Dipendenza runtime:</strong> <code>requests&gt;=2.28</code> &middot; <strong>Licenza:</strong> MIT
</div>

---

## Installazione

```bash
pip install enconvert
```

`uv add enconvert` e `poetry add enconvert` funzionano allo stesso modo. `requests` è l'unica dipendenza runtime, e gli stub di tipo viaggiano dentro la wheel, quindi non c'è nessun pacchetto `types-` da inseguire.

---

## Guida rapida

Leggi una pagina come dovrebbe farlo il tuo agente, con un punteggio di qualità allegato, poi converti un file locale con lo stesso client.

```python
import os

from enconvert import Enconvert

client = Enconvert(api_key=os.environ["ENCONVERT_API_KEY"])

op = client.v2.perceive("https://example.com", outputs=["markdown", "structured"])
print(op.outputs["markdown"].url, op.render_quality)

print(client.convert_to_pdf("report.docx", save_to="report.pdf").presigned_url)
```

Ogni metodo è sincrono e bloccante. L'SDK è **solo lato server**: si autentica con una chiave API privata, quindi non distribuirlo mai dentro un client desktop, mobile o browser. Prendi una chiave dalla tua [dashboard](/it/dashboard) e consulta [Autenticazione](/it/docs/authentication) per capire come sono definiti gli ambiti delle chiavi.

---

## Cosa espone il client

`Enconvert` è tutta la superficie pubblica. I metodi di conversione pendono direttamente dal client; tutto ciò che riguarda il web vive sotto `client.v2`.

| Metodo di conversione | Endpoint | Restituisce |
|--------|----------|---------|
| `convert_url_to_pdf(url, ...)` | `POST /v1/convert/url-to-pdf` | `ConversionResult` |
| `convert_url_to_screenshot(url, ...)` | `POST /v1/convert/url-to-screenshot` | `ConversionResult` |
| `convert_url_to_markdown(url, ...)` | `POST /v1/convert/url-to-markdown` | `ConversionResult` |
| `convert_image(file, output_format=...)` | `POST /v1/convert/{input}-to-{output}` | `ConversionResult` |
| `convert_document(file, ...)` | `POST /v1/convert/{input}-to-{output}` | `ConversionResult` |
| `convert_to_markdown(file, ...)` | `POST /v1/convert/anything-to-markdown` | `ConversionResult` |
| `convert_to_pdf(file, ...)` | `POST /v1/convert/anything-to-pdf` | `ConversionResult` |
| `convert_website_to_pdf(url, ...)` | `POST /v1/convert/website-to-pdf` | `BatchSubmission` |
| `convert_website_to_screenshot(url, ...)` | `POST /v1/convert/website-to-screenshot` | `BatchSubmission` |
| `get_job_status(job_id)` | `GET /v1/convert/status/{job_id}` | `JobStatus` |
| `get_batch_status(batch_id)` | `GET /v1/convert/batch/{batch_id}` | `BatchStatus` |
| `wait_for_batch(batch_id, ...)` | `GET /v1/convert/batch/{batch_id}` (con polling) | `BatchStatus` |

| Capacità di `client.v2` | Metodi | Restituisce |
|------------|---------|---------|
| Perceive | `perceive`, `perceive_direct`, `get_perceive_operation`, `download_perceive_artifact`, `perceive_batch`, `get_perceive_batch` | `PerceiveResult`, `PerceiveDirectResult`, `PerceiveBatchResult` |
| Discover, Lookup, Distill | `discover`, `lookup`, `distill` | `DiscoverResult`, `LookupResult`, `DistillResult` |
| Ingest | `ingest`, `ingest_files`, `list_ingest_jobs`, `get_ingest_job`, `cancel_ingest_job`, `retry_ingest_webhook`, `get_webhook_secret`, `rotate_webhook_secret` | `IngestJob`, `IngestJobList`, `WebhookSecret`, `WebhookRetryResult` |
| Watch | `create_watcher`, `list_watchers`, `get_watcher`, `get_watcher_snapshots`, `update_watcher`, `delete_watcher` | `Watcher`, `WatcherList`, `WatcherSnapshotList` |

Ogni argomento dopo il primo posizionale è solo per parola chiave, in snake_case e facoltativo, se non indicato diversamente in una tabella. I risultati sono dataclass congelate, quindi costruisci una nuova istanza invece di mutarne una. Le forme REST sono nella [panoramica degli endpoint](/it/docs/endpoints-overview).

---

## Conversione file

Ogni metodo di conversione accetta `save_to` (uno `str` o un `os.PathLike`; il risultato viene scritto lì in streaming e le directory superiori vengono create) e `output_filename` (per sovrascrivere il nome generato). Entrambi sono omessi dalle tabelle qui sotto.

### convert_url_to_pdf

Esegue il rendering in PDF di qualsiasi URL raggiungibile.

```python
result = client.convert_url_to_pdf(
    "https://example.com", single_page=False, viewport_width=1440, save_to="report.pdf"
)
print(result.presigned_url, result.file_size)
```

| Opzione | Tipo | Default | Descrizione |
|--------|------|---------|-------------|
| `single_page` | `bool` | `True` | `True` produce una sola pagina continua. `False` impagina usando `pdf_options.page_size`. |
| `pdf_options` | `PdfOptions` | -- | Dimensione pagina, orientamento, margini, scala, scala di grigi, intestazione, piè di pagina. Vedi [Opzioni PDF](#opzioni-pdf). |
| `viewport_width` / `viewport_height` | `int` | `1920` / `1080` | Dimensione del viewport del browser in pixel. |

`load_media` ed `enable_scroll` valgono entrambi `True` per impostazione predefinita: il primo attende immagini e video, il secondo scorre dall'alto in basso così i lazy loader scattano. Altre tre opzioni raggiungono pagine dietro un accesso protetto: `auth` (`HttpBasicAuth`), `cookies` (`list[BrowserCookie]`) e `headers` (`dict[str, str]`).

<div class="alert alert-warning">
<strong>Non combinare <code>auth</code> con un header <code>Authorization</code>.</strong> L'API rifiuta il conflitto invece di indovinare quale credenziale debba prevalere. Scegline una.
</div>

---

### convert_url_to_screenshot

Cattura un PNG di qualsiasi URL.

```python
client.convert_url_to_screenshot("https://example.com", viewport_width=1440, save_to="shot.png")
```

Accetta le stesse opzioni di viewport, media, scroll, nome file e accesso al browser di `convert_url_to_pdf`, esclusi `single_page` e `pdf_options`.

---

### convert_url_to_markdown

Estrae Markdown GitHub-Flavored pulito da un URL. Navigazione, piè di pagina, pubblicità e script vengono rimossi, il corpo principale dell'articolo viene mantenuto e viene anteposto un frontmatter YAML con titolo, descrizione, url, link e immagini.

```python
client.convert_url_to_markdown("https://example.com/article", save_to="article.md")
```

Stesso insieme di opzioni di `convert_url_to_screenshot`. Quando sulla stessa lettura vuoi anche un punteggio di qualità, i metadati della pagina o l'estrazione strutturata, usa invece [`v2.perceive`](#perceive).

---

### convert_image

Converte tra `jpeg`, `png`, `svg`, `heic` e `webp`, oppure rasterizza un PDF in JPEG. Il formato di input viene ricavato dall'estensione del nome file.

```python
client.convert_image("photo.heic", output_format="webp", save_to="photo.webp")
client.convert_image("scan.pdf", output_format="jpeg", save_to="scan.jpeg")
```

`output_format` è obbligatorio e deve essere uno tra `jpeg`, `png`, `svg`, `heic` o `webp`; gli alias `jpg`, `yml`, `htm` e `md` vengono normalizzati per te. `file` accetta una stringa di percorso, un `os.PathLike`, `bytes` grezzi o un wrapper `FileData(data, filename)`. I `bytes` grezzi non portano alcun nome file e vengono caricati come `upload.bin`, quindi preferisci `FileData` ogni volta che l'estensione conta.

```python
from pathlib import Path

from enconvert import FileData

blob = FileData(data=Path("photo.heic").read_bytes(), filename="photo.heic")
client.convert_image(blob, output_format="webp", save_to="photo.webp")
```

---

### convert_document

Converte documenti e formati dati. `output_format` vale `"pdf"` per impostazione predefinita, e `pdf_options` viene rispettato quando l'output è un PDF.

```python
from enconvert import PdfMargins, PdfOptions

client.convert_document("report.docx", save_to="report.pdf")
client.convert_document("data.json", output_format="yaml", save_to="data.yaml")
client.convert_document(
    "README.md", pdf_options=PdfOptions(page_size="A4", margins=PdfMargins(top=20)), save_to="r.pdf"
)
```

**Input supportati:** `.doc`, `.docx`, `.xls`, `.xlsx`, `.ppt`, `.pptx`, `.html`, `.htm`, `.odt`, `.ods`, `.odp`, `.ots`, `.pages`, `.numbers`, `.md`, `.markdown`, `.csv`, `.json`, `.xml`, `.yaml`, `.yml`, `.toml`.

EPUB non ha una coppia documenti dedicata. Passa i `.epub` attraverso `convert_to_pdf` o `convert_to_markdown`.

---

### convert_to_markdown

Rileva automaticamente lato server un documento caricato e restituisce Markdown pulito. È il mattone di base per l'ingestione RAG di un singolo file.

```python
client.convert_to_markdown("handbook.docx", save_to="handbook.md")
```

**Input accettati:** PDF, DOCX, PPTX, XLSX, CSV, HTML, EPUB, TXT e MD, più i formati office legacy e ODF. Le immagini non sono supportate qui, e l'endpoint non accetta opzioni PDF. Per un sito intero invece che per un singolo file, usa [`v2.ingest`](#ingest).

---

### convert_to_pdf

Rileva automaticamente lato server un file caricato e restituisce un PDF.

```python
from enconvert import PdfOptions

client.convert_to_pdf("slides.pptx", save_to="slides.pdf")
client.convert_to_pdf("scan.pdf", pdf_options=PdfOptions(grayscale=True), save_to="gray.pdf")
```

**Input accettati:** office, ODF, Pages, Numbers, RTF, CSV, HTML, Markdown, testo semplice, immagini raster, SVG, EPUB e un PDF esistente in passthrough. Dato che un input `.pdf` viene restituito così com'è, questo metodo funziona anche come normalizzatore in scala di grigi.

<div class="alert alert-warning">
<strong>Su questo endpoint viene rispettato solo <code>pdf_options.grayscale</code>.</strong> Gli altri campi di geometria della pagina vengono ignorati qui. Quando ti serve una vera impostazione di pagina, fai passare il file attraverso <code>convert_document</code> o <code>convert_url_to_pdf</code>.
</div>

---

### convert_website_to_pdf e convert_website_to_screenshot

Individua ogni pagina di un sito, converte ciascuna in background e raccoglie tutto in un unico ZIP. Entrambi sono asincroni per progetto e restituiscono subito un `BatchSubmission`.

```python
batch = client.convert_website_to_pdf(
    "https://example.com", crawl_mode="sitemap", exclude_patterns=["/blog/tag/"]
)
print(batch.batch_id, batch.url_count, batch.discovery_method)

status = client.wait_for_batch(batch.batch_id, save_to="site.zip")
print(status.completed, "of", status.total, "pages converted")

for item in client.get_batch_status(batch.batch_id).items:
    print(item.source_url, item.status, item.download_url)
```

| Opzione | Tipo | Default | Descrizione |
|--------|------|---------|-------------|
| `crawl_mode` | `"auto" \| "sitemap" \| "full"` | default del server | `sitemap` legge solo `sitemap.xml`. `full` aggiunge un crawl in ampiezza. `auto` sceglie la modalità più alta disponibile per la chiave. |
| `include_patterns` / `exclude_patterns` | `list[str]` | -- | Mantiene o scarta URL in base a un frammento di percorso. Le esclusioni valgono solo in modalità full crawl. |
| `notification_email` / `callback_url` | `str` | -- | Indirizzo da avvisare via email, e webhook da chiamare, quando il batch finisce. |
| `single_page` | `bool` | default del server | Solo PDF. |
| `pdf_options` | `PdfOptions` | -- | Solo PDF. Vedi [Opzioni PDF](#opzioni-pdf). |

Anche le opzioni di rendering per singola pagina si applicano a ogni URL individuato: `viewport_width`, `viewport_height`, `load_media`, `enable_scroll`, `auth`, `cookies` e `headers`. Tutto ciò che lasci non impostato mantiene il valore predefinito del gateway.

`wait_for_batch` accetta `interval_seconds` (default `5.0`), `timeout_seconds` (default `1800.0`) e `save_to`. Solleva `APIError(504, ...)` se il batch è ancora in elaborazione quando la scadenza viene superata.

---

### Coppie di conversione supportate

L'SDK porta con sé una copia della mappa di conversione del gateway e rifiuta in locale una coppia non implementata, prima di qualsiasi round-trip di rete, indicando nel messaggio gli output validi per quell'input.

| Input | Output |
|-------|---------|
| `json` | `csv`, `toml`, `xml`, `yaml` |
| `xml` | `csv`, `json` |
| `yaml`, `toml` | `json` |
| `csv` | `json`, `xml` |
| `markdown` | `html`, `pdf` |
| `html` | `pdf` |
| `doc`, `excel`, `ppt`, `odt`, `ods`, `odp`, `ots`, `pages`, `numbers` | `pdf` |
| `jpeg`, `png`, `svg`, `heic`, `webp` | l'uno verso l'altro, tutte e 20 le coppie |
| `pdf` | `jpeg` |

In totale fanno 43 coppie implementate. Controllale a livello di codice:

```python
from enconvert import IMPLEMENTED_CONVERSIONS, valid_outputs_for

valid_outputs_for("json")                  # ['csv', 'toml', 'xml', 'yaml']
"heic-to-webp" in IMPLEMENTED_CONVERSIONS  # True
```

Il riferimento completo dei parametri vive in [Parametri e opzioni](/it/docs/parameters-options).

---

## Web intelligence (V2)

Ogni lettura V2 porta con sé `render_quality`, un float da 0.0 a 1.0 che dice quanto onestamente si è renderizzata la pagina. Una schermata di sfida, un muro dei cookie, un accesso protetto da login, una pagina di errore HTTP o uno shell SPA vuoto tornano con un punteggio basso, una mappa `deductions` che nomina cosa è scattato e un elenco `warnings`. Il contenuto viene comunque restituito, solo segnalato, così una lettura sbagliata non entra mai di nascosto nel contesto del tuo agente. Tratta il punteggio come un filtro e controllalo prima di usare il testo. `PerceiveResult`, `PerceiveDirectResult`, `DistillItem`, `LookupItem.perceive` e `WatcherSnapshot` lo espongono tutti. I concetti sono nella [panoramica V2](/it/docs/v2-overview).

### Perceive

Esegue il rendering di un URL in artefatti pronti per gli agenti. È sincrono: la chiamata restituisce l'operazione completata con gli URL firmati degli artefatti.

```python
op = client.v2.perceive(
    "https://example.com",
    outputs=["markdown", "screenshot", "structured"],
    extract=["tables", "metadata"],
    only_main_content=True,
)

print(op.render_quality)            # da 0.0 a 1.0
print(op.status_code)               # stato HTTP della pagina stessa
print(op.deductions)                # ad es. {"login_wall": 0.65}
print(op.outputs["markdown"].url)   # URL firmato, 15 minuti
print(op.structured)

if (op.render_quality or 0) < 0.6:
    print("Low-confidence read:", op.warnings)
```

| Opzione | Tipo | Default | Descrizione |
|--------|------|---------|-------------|
| `outputs` | `list[str]` | `["markdown", "structured"]` | Uno o più tra `markdown`, `html_cleaned`, `html_raw`, `screenshot`, `screenshot_full_page`, `pdf`, `links`, `images`, `structured`. |
| `extract` | `list[str]` | -- | Uno o più tra `tables`, `prices`, `contacts`, `metadata`, `main_content`, `headings`, `structured_data`, `technologies`, `all`. |
| `wait_for` | `str` | -- | Selettore CSS da attendere prima della cattura. |
| `viewport` | `PerceiveViewport` | 1920 x 1080 | `width` da 320 a 3840, `height` da 240 a 2160. |
| `cache_mode` | `"enabled" \| "bypass" \| "refresh"` | default del server | Riusa, salta o riscrive il render in cache. |
| `block_resources` | `list[str]` | -- | Uno o più tra `image`, `media`, `font`, `stylesheet`, `script`, `xhr`, `fetch`, `websocket`, `manifest`, `other`. |
| `only_main_content` | `bool` | default del server | Rimuove navigazione, header, footer e altri elementi di contorno della pagina dal contenuto estratto. |
| `direct_download` | `bool` | default del server | Restituisce i byte grezzi invece di un URL firmato. Non impostata per impostazione predefinita, quindi la chiave viene omessa dalla richiesta e vale il default del server (false). Accettata solo da `perceive`; l'endpoint batch la rifiuta. |

Sono accettati anche: `schema` (`dict`, uno schema di estrazione libero passato invariato), `js_code` (`str`, eseguito nella pagina prima della cattura), `wait_timeout_ms` (`int`), `headers` (`dict[str, str]`), `cookies` (`list[BrowserCookie]`), `auth` (`HttpBasicAuth`), `proxy_url` (`str`), `geolocation` (`dict`), `action_chain` (`list[dict]` di passaggi scriptati di click, digitazione e scroll), `pdf_options` (`PdfOptions`, applicato all'output `pdf`), `mobile` (`bool`) e `respect_robots` (`bool`). `perceive_batch` accetta esattamente lo stesso insieme a parte `direct_download`.

Gli URL degli artefatti vengono rifirmati a ogni lettura, quindi chiama `client.v2.get_perceive_operation(op.operation_id)` per averne uno fresco invece di mettere in cache la stringa.

**Byte grezzi, senza giro di andata e ritorno sull'URL firmato.** `perceive_direct` forza `direct_download` a on e ti consegna l'artefatto stesso, con i metadati letti dagli header della risposta.

```python
direct = client.v2.perceive_direct("https://example.com", outputs=["markdown"])
print(direct.filename, direct.content_type, len(direct.content), direct.render_quality)

raw = client.v2.download_perceive_artifact(op.operation_id, output="markdown")
```

`perceive_direct` richiede esattamente un output che produca un artefatto tra `markdown`, `html_cleaned`, `html_raw`, `screenshot`, `screenshot_full_page`, `pdf`, `links` e `images`. `structured` può viaggiare insieme ma resta inline lato server; passa qualsiasi altra cosa e l'SDK solleva `EnconvertError` prima di inviare la richiesta. Su `download_perceive_artifact`, `output` si può omettere quando l'operazione ha prodotto esattamente un artefatto, e un artefatto oltre la sua finestra di conservazione risponde `410`.

**Batch.** Fino a 1000 URL condividono un unico blocco di opzioni. I batch piccoli si completano inline; quelli più grandi tornano con stato `"queued"`, quindi interroga l'id del job.

```python
batch = client.v2.perceive_batch(
    ["https://a.example.com", "https://b.example.com"], outputs=["markdown"], output_mode="zip"
)

done = client.v2.get_perceive_batch(batch.job_id)
print(done.status, done.completed, done.failed, done.pending)
for item in done.items:
    print(item.url, item.render_quality)
```

`output_mode` è `"manifest"` (predefinito) o `"zip"`; quando è `"zip"`, l'archivio finito si trova su `done.zip.url`. I dettagli sono in [Perceive](/it/docs/v2-perceive).

### Discover

Elenca gli URL di un sito senza alcun rendering del browser, il che ne fa il primo passo economico prima di percepire qualsiasi cosa.

```python
found = client.v2.discover(
    "https://example.com", mode="hybrid", max_urls=200, exclude_patterns=["/tag/"]
)
print(found.total, found.truncated, found.sources)
```

| Opzione | Tipo | Default | Descrizione |
|--------|------|---------|-------------|
| `mode` | `"sitemap" \| "crawl" \| "hybrid"` | default del server | Analisi della sitemap, un crawl HTTP, o entrambi uniti e deduplicati. |
| `max_urls` / `max_depth` | `int` | default del server | Limite sugli URL restituiti (`truncated` è `True` quando ce n'erano di più) e profondità del crawl dall'URL di partenza. |
| `same_domain_only` | `bool` | default del server | Resta sull'host di partenza. |
| `respect_robots` | `bool` | default del server | Rispetta `robots.txt`. |

`include_patterns` ed `exclude_patterns` (entrambi `list[str]`) filtrano l'insieme dei risultati. `DiscoverResult.sources` contiene i conteggi grezzi per sorgente prima della deduplicazione, ad esempio `{"sitemap": 42, "crawl": 30}`. Altro in [Discover](/it/docs/v2-discover).

### Lookup

Esegue una ricerca web categorizzata, con la possibilità di renderizzare i primi risultati nella stessa chiamata.

```python
search = client.v2.lookup(
    "best static site generators", category="web", num_results=10, perceive_top=3
)

for hit in search.results:
    quality = hit.perceive.render_quality if hit.perceive else None
    print(hit.position, hit.title, hit.url, quality)
```

| Opzione | Tipo | Default | Descrizione |
|--------|------|---------|-------------|
| `category` | `"web" \| "news" \| "images" \| "scholar" \| "patents" \| "maps"` | default del server | Verticale di ricerca. |
| `time_filter` | `"hour" \| "day" \| "week" \| "month" \| "year"` | -- | Finestra temporale di recency. |
| `num_results` / `page` | `int` | default del server | Risultati per pagina, e numero di pagina a base 1. |
| `perceive_top` | `int` | `0` | Esegue il rendering automatico dei primi N URL dei risultati. Ognuno arriva con il suo `PerceiveResult` completo su `hit.perceive`. |

Sono accettati anche: `country` (`str`), `locale` (`str`), `location` (`str`) per query sensibili alla geolocalizzazione e `autocorrect` (`bool`). `LookupResult` porta con sé `answer_box`, `knowledge_graph`, `perceive_operation_ids` e `perceive_top`, dove quest'ultimo riporta quanti risultati sono stati effettivamente renderizzati, che possono essere meno di quanti ne avevi chiesti. Riferimento in [Lookup](/it/docs/v2-lookup).

### Distill

Estrazione strutturata guidata da uno schema su una o più pagine. Fornisci esattamente uno tra `urls` e `discover_from`; `schema` è sempre obbligatorio. Entrambe le regole vengono applicate lato client e sollevano `EnconvertError` prima che parta una richiesta.

```python
from enconvert import CssField, CssSchema

extraction = client.v2.distill(
    urls=["https://example.com/pricing"],
    schema={"plans": "list of plan names with monthly prices"},
    css_schema=CssSchema(
        base_selector=".plan-card",
        fields=[
            CssField(name="name", type="text", selector="h3"),
            CssField(name="price", type="text", selector=".price"),
        ],
        target_field="plans",
    ),
)

for item in extraction.results:
    print(item.url, item.extraction_tier, item.fields_from_css, item.fields_from_llm, item.data)
```

Il `css_schema` facoltativo gira per primo e risponde a tutto ciò che i selettori riescono a raggiungere. Solo i campi che mancano vengono escalati al tier del modello linguistico, ed `extraction_tier` su ciascun elemento riporta quale percorso è stato usato: `css`, `llm`, `mixed` o `none`.

Scoprire e distillare in un'unica chiamata:

```python
from enconvert import DistillDiscoverFrom

client.v2.distill(
    discover_from=DistillDiscoverFrom(url="https://example.com", mode="sitemap", max_pages=10),
    schema={"title": "page title"},
)
```

| Opzione | Tipo | Default | Descrizione |
|--------|------|---------|-------------|
| `schema` | `dict` | -- (obbligatorio) | La forma di output che vuoi indietro, passata invariata. |
| `urls` | `list[str]` | -- | Elenco esplicito di pagine. Mutuamente esclusivo con `discover_from`. |
| `discover_from` | `DistillDiscoverFrom` | -- | `url`, `mode` facoltativo, `max_pages` facoltativo (da 1 a 50, default 10). |
| `css_schema` | `CssSchema` | -- | Passaggio sui selettori eseguito prima di qualsiasi chiamata al modello. |

Sono accettati anche: `wait_for` (`str`), `wait_timeout_ms` (`int`), `headers` (`dict[str, str]`), `cookies` (`list[BrowserCookie]`) e `respect_robots` (`bool`). `CssField` supporta i tipi `text`, `attribute`, `html`, `regex`, `nested`, `list` e `nested_list`, con `attribute`, `pattern`, `default`, `transform` (`lowercase`, `uppercase`, `strip`) facoltativi e `fields` annidati fino a cinque livelli di profondità. Vedi [Distill](/it/docs/v2-distill).

### Ingest

Trasforma un sito intero, un elenco esplicito di URL o una pila di documenti caricati in JSONL suddiviso in chunk e pronto per RAG. Ingest è sempre asincrono.

```python
import time

from enconvert import IngestChunkOptions

job = client.v2.ingest(
    mode="sitemap",
    url="https://docs.example.com",
    max_pages=100,
    chunk=IngestChunkOptions(max_words=512, sentence_overlap=1),
    webhook_url="https://my.app/hooks/enconvert",
)

status = client.v2.get_ingest_job(job.job_id)
while status.status in ("queued", "discovering", "processing"):
    time.sleep(10)
    status = client.v2.get_ingest_job(job.job_id)

print(status.status, status.pages_processed, status.total_chunks, status.output_url)
```

I file caricati percorrono lo stesso ciclo di vita del job sotto la modalità `files`. Sono accettati PDF, DOCX, PPTX, XLSX, CSV, HTML, EPUB, TXT e MD, più i formati office legacy e ODF, e ogni voce può essere un percorso, un `os.PathLike`, `bytes` grezzi o un `FileData`.

```python
file_job = client.v2.ingest_files(["handbook.pdf", "notes.docx"])
```

| Opzione | Tipo | Default | Descrizione |
|--------|------|---------|-------------|
| `mode` | `"urls" \| "sitemap" \| "crawl" \| "files"` | `"urls"` | `urls` richiede un elenco `urls` non vuoto e rifiuta `url`. Ogni altra modalità richiede un `url` di partenza e rifiuta `urls`. |
| `url` / `urls` | `str` / `list[str]` | -- | URL di partenza per `sitemap` e `crawl`, oppure l'elenco esplicito di pagine per la modalità `urls`. |
| `max_pages` | `int` | default del server | Limite alle pagine ingerite. |
| `chunk` | `IngestChunkOptions` | -- | `max_words` da 32 a 4000, default 512. `sentence_overlap` da 0 a 10, default 1. Abbinalo a `webhook_url` (`str`), chiamato quando il job raggiunge uno stato terminale. |

Sono accettate anche le opzioni di modellazione del crawl e di rendering: `max_depth`, `same_domain_only`, `include_patterns`, `exclude_patterns`, `respect_robots`, `wait_for` e `wait_timeout_ms`. `ingest_files` accetta solo `chunk` e `webhook_url`.

```python
for summary in client.v2.list_ingest_jobs(limit=20).jobs:
    print(summary.job_id, summary.status, summary.total_chunks)

client.v2.cancel_ingest_job(job.job_id)
client.v2.retry_ingest_webhook(job.job_id)

secret = client.v2.get_webhook_secret()
print(secret.signature_header, secret.signature_scheme, secret.replay_tolerance_seconds)
client.v2.rotate_webhook_secret()
```

`cancel_ingest_job` è idempotente; annullare un job già in stato terminale lo restituisce invariato. `retry_ingest_webhook` risponde `409` quando il job non è completato e `400` quando non era configurato alcun webhook. `rotate_webhook_secret` invalida immediatamente il segreto precedente. Approfondimento in [Ingest](/it/docs/v2-ingest).

### Watch

Riesegue il rendering di un URL a cadenza fissa e ti avvisa quando cambia.

```python
watcher = client.v2.create_watcher(
    "https://example.com/pricing",
    frequency_minutes=60,
    diff_mode="auto",
    webhook_url="https://my.app/hooks/changes",
    notify_email=True,
)

for snap in client.v2.get_watcher_snapshots(watcher.watcher_id, limit=10).snapshots:
    print(snap.checked_at, snap.has_changes, snap.similarity, snap.change_count)

client.v2.list_watchers(limit=20)
client.v2.update_watcher(watcher.watcher_id, status="paused")
client.v2.update_watcher(watcher.watcher_id, webhook_url="")
client.v2.delete_watcher(watcher.watcher_id)
```

| Opzione | Tipo | Default | Descrizione |
|--------|------|---------|-------------|
| `frequency_minutes` | `int` | default del server | Intervallo tra i controlli. Il limite minimo è di un'ora. |
| `diff_mode` | `"auto" \| "text" \| "structured" \| "tables" \| "metadata"` | default del server | Quale livello della pagina confronta il motore di diff. |
| `track_fields` | `dict` | -- | Campi con nome da tracciare, passati invariati. |
| `webhook_url` | `str` | -- | Chiamato a ogni cambiamento rilevato. |
| `notify_email` | `bool` | default del server | Invia le notifiche di cambiamento via email. |

`update_watcher` richiede almeno un campo e altrimenti solleva `EnconvertError`; `status` accetta `"active"` o `"paused"`, e un `webhook_url` vuoto azzera il webhook. `delete_watcher` è una eliminazione soft e idempotente che restituisce il watcher marcato come eliminato con stato `"deleted"`, dopodiché il watcher risulta `404`.

<div class="alert alert-warning">
<strong>I diff degli snapshot portano contenuto di pagina non attendibile.</strong> I dict in <code>WatcherSnapshot.changes</code> arrivano direttamente dal sito sorvegliato. Applica l'escape prima di renderizzarli in HTML, nel corpo di un'email o in un messaggio di chat.
</div>

Il motore di diff e i payload di notifica sono documentati in [Watch](/it/docs/v2-watch).

---

## Opzioni PDF

`PdfOptions` è una dataclass congelata condivisa da `convert_url_to_pdf`, `convert_website_to_pdf`, `convert_document`, `convert_to_pdf` e dalla famiglia `v2.perceive`.

```python
from enconvert import BrowserCookie, HttpBasicAuth, PdfHeaderFooter, PdfMargins, PdfOptions

client.convert_url_to_pdf(
    "https://internal.example.com/report",
    pdf_options=PdfOptions(
        page_size="A4",
        orientation="landscape",
        margins=PdfMargins(top=10, bottom=10, left=15, right=15),
        scale=0.9,
        header=PdfHeaderFooter(content="Quarterly Report", height=15),
        footer=PdfHeaderFooter(content="Confidential", height=12),
    ),
    auth=HttpBasicAuth(username="user", password="pass"),
    cookies=[BrowserCookie(name="session", value="abc123", domain="internal.example.com")],
    save_to="report.pdf",
)
```

| Campo | Tipo | Descrizione |
|-------|------|-------------|
| `page_size` | `str` | `"A4"`, `"A3"`, `"Letter"`, `"Legal"` e simili. |
| `page_width` / `page_height` | `float` | Geometria di pagina personalizzata. Insieme prevalgono su `page_size`. |
| `orientation` | `"portrait" \| "landscape"` | Il valore predefinito è verticale. |
| `margins` | `PdfMargins` | `top`, `bottom`, `left`, `right`, tutti facoltativi. |
| `scale` | `float` | Scala di rendering, per esempio `0.9` per il 90 percento. |
| `grayscale` | `bool` | Post-elabora il PDF convertendolo in scala di grigi. |
| `header` | `PdfHeaderFooter` | `content` (fino a 2000 caratteri) e `height`. |
| `footer` | `PdfHeaderFooter` | `content` (fino a 2000 caratteri) e `height`. |

`BrowserCookie` accetta `name`, `value` e o `domain` o `url`, più `path`, `expires`, `http_only`, `secure` e `same_site` (`"Strict"`, `"Lax"`, `"None"`) facoltativi. L'SDK mappa `http_only` e `same_site` sulle rispettive chiavi wire in camelCase al posto tuo.

---

## Gestione degli errori

Gli errori sono classi di eccezione, quindi intercettali con `except`. Ordina i gestori dal più specifico al più generale: `AuthenticationError`, `QuotaError` e `RateLimitError` sono tutte sottoclassi di `APIError`, che a sua volta è sottoclasse di `EnconvertError`.

```python
from enconvert import APIError, AuthenticationError, EnconvertError, QuotaError, RateLimitError

try:
    op = client.v2.perceive("https://example.com")
except AuthenticationError:
    print("Invalid or missing API key. Check ENCONVERT_API_KEY.")
except QuotaError as e:
    print(f"402 from the API: {e.message}")
except RateLimitError:
    print("Too many requests. Back off and retry.")
except APIError as e:
    print(f"API error [{e.status_code}]: {e.message}")
except EnconvertError as e:
    print(f"Rejected before the request was sent: {e}")
```

| Classe | Sollevata su | Codice di stato |
|-------|-----------|-------------|
| `AuthenticationError` | Chiave non valida, mancante o revocata | `401`, `403` |
| `QuotaError` | HTTP 402 | `402` |
| `RateLimitError` | Troppe richieste | `429` |
| `APIError` | Qualsiasi altro 4xx o 5xx | il codice effettivo |
| `EnconvertError` | Classe base, e validazione lato client che non raggiunge mai la rete | -- |

`APIError` porta con sé `status_code` e `message`, e il suo `str()` si legge come `[404] Not found`. Errori lato client in cui puoi incappare: una `api_key` vuota, un'estensione di file non supportata, una coppia di conversione non implementata, una chiamata `distill` con entrambi o nessuno tra `urls` e `discover_from`, una mancata corrispondenza tra modalità e argomenti di `ingest`, un payload `update_watcher` vuoto e un elenco di output di `perceive_direct` che non si risolve in esattamente un artefatto. La mappa completa dei messaggi è in [Codici di errore](/it/docs/error-codes).

---

## Recupero dei timeout

Le conversioni lunghe di URL e documenti possono sopravvivere più a lungo del timeout di un reverse-proxy anche quando il server porta a termine il job. L'SDK Python recupera in modo trasparente:

1. Prima di ogni conversione di un singolo file o di un singolo URL, l'SDK genera una stringa esadecimale UUID4 e la invia come `job_id`.
2. Se quella richiesta torna con un 5xx, l'SDK passa al polling di `GET /v1/convert/status/{job_id}` ogni 3 secondi. Un `404` lì significa che la riga del job non è ancora stata scritta, quindi il polling continua.
3. Su `success` l'SDK restituisce il risultato come se nulla fosse; su `failed` solleva `APIError(500, ...)` con il messaggio di errore del server; e oltre la scadenza di polling di 300 secondi solleva `APIError(504, "Conversion timed out")`.

Per tutto questo non scrivi codice. Quando una risposta arriva attraverso il percorso di recupero, `ConversionResult.job_id` viene valorizzato, così puoi correlarla nei tuoi log.

Due eccezioni. `convert_website_to_pdf` e `convert_website_to_screenshot` non partecipano, perché l'invio di un intero sito non ha una riga di job per singola pagina, quindi un 5xx lì significa che è fallito l'invio stesso e l'errore emerge direttamente. Nemmeno gli endpoint V2 vengono interrogati con polling: usa i loro id di job con `get_perceive_batch` o `get_ingest_job`.

---

## Configurazione

```python
client = Enconvert(
    api_key=os.environ["ENCONVERT_API_KEY"], timeout=300.0, base_url="https://api.enconvert.com"
)
```

| Opzione | Tipo | Default | Descrizione |
|--------|------|---------|-------------|
| `api_key` | `str` | -- (obbligatoria) | Chiave API privata. Un valore vuoto solleva `EnconvertError` alla costruzione. |
| `timeout` | `float` | `300.0` | Timeout per richiesta in secondi, applicato a ogni chiamata inclusi gli upload. |
| `base_url` | `str` | `https://api.enconvert.com` | URL base dell'API. Le barre finali vengono rimosse. |

Il client mantiene una `requests.Session`, quindi le connessioni vengono riusate tra le chiamate; riusa un solo client invece di crearne uno per richiesta. La tua chiave viaggia come header `X-API-Key`, e i download con `save_to` colpiscono l'URL firmato dello storage come una semplice GET non autenticata scritta su disco in streaming a blocchi da 64 KB, così la chiave non lascia mai l'host dell'API.

<div class="alert alert-warning">
<strong>Non scrivere mai la chiave API hardcoded.</strong> Leggila da una variabile d'ambiente o dal tuo secret manager, e tienila lato server. Chiunque possieda la tua chiave privata può eseguire lavoro sul tuo progetto.
</div>

---

## Forma del risultato

I metodi di conversione restituiscono un `ConversionResult` congelato:

```python
@dataclass(frozen=True)
class ConversionResult:
    presigned_url: str
    object_key: str
    filename: str
    file_size: int | None = None
    conversion_time_seconds: float | None = None
    job_id: str | None = None
```

L'URL pre-firmato è di breve durata. Passa `save_to`, oppure recupera tu stesso l'URL, e archivia i byte nel tuo bucket quando ti serve un accesso duraturo.

| Tipo | Restituito da | Campi principali |
|------|-------------|------------|
| `JobStatus` | `get_job_status` | `status` (`processing`, `success`, `failed`), `presigned_url`, `object_key`, `error` |
| `BatchSubmission`, `BatchStatus` | `convert_website_to_*`, `get_batch_status`, `wait_for_batch` | `batch_id`, `status`, `url_count`, `total`, `completed`, `failed`, `discovery_method`, `zip_download_url`, `items` |
| `PerceiveResult` | `v2.perceive`, `v2.get_perceive_operation` | `operation_id`, `render_quality`, `status_code`, `deductions`, `outputs`, `structured`, `warnings`, `cache_hit` |
| `PerceiveDirectResult` | `v2.perceive_direct`, `v2.download_perceive_artifact` | `content` (byte grezzi), `content_type`, `filename`, `render_quality`, `source_status_code` |
| `IngestJob` | `v2.ingest`, `v2.ingest_files`, `v2.get_ingest_job` | `job_id`, `status`, `mode`, `pages_processed`, `total_chunks`, `output_url`, `error_message` |
| `Watcher` | i metodi watch di `v2` | `watcher_id`, `status`, `frequency_minutes`, `diff_mode`, `checks_count`, `next_check_at`, `last_change_at` |

Gli URL degli artefatti V2 sono firmati per 15 minuti e rifirmati ogni volta che leggi l'operazione, quindi chiama `get_perceive_operation` invece di mettere in cache una stringa di URL.

---

## Sorgente e segnalazioni

- **PyPI:** [enconvert](https://pypi.org/project/enconvert/)
- **GitHub:** [conversionapi/python-sdk](https://github.com/conversionapi/python-sdk)
- **Python:** 3.9, 3.10, 3.11, 3.12, 3.13
- **Licenza:** MIT
- **Altri client:** [Tutti gli SDK](/it/docs/sdks)

---

## Domande frequenti

### Come converto file in Python?

Esegui `pip install enconvert`, costruisci un client con `Enconvert(api_key=os.environ["ENCONVERT_API_KEY"])` e chiama un metodo tipizzato come `convert_document`, `convert_image`, `convert_to_pdf` o `convert_to_markdown`. Passa `save_to="out.pdf"` e l'SDK scrive il file finito direttamente su disco in streaming, invece di consegnarti un URL da recuperare da solo.

### Come converto un URL in PDF con Python?

Chiama `client.convert_url_to_pdf("https://example.com", save_to="page.pdf")`. Imposta `single_page=False` più `pdf_options=PdfOptions(page_size="A4")` per un output impaginato, e regola `viewport_width`, `load_media` o `enable_scroll` quando una pagina ha bisogno di un canvas più largo o di immagini caricate in lazy loading.

### Come converto DOCX in PDF con Python?

O `client.convert_document("report.docx", save_to="report.pdf")`, dato che `output_format` vale già `"pdf"` per impostazione predefinita, oppure `client.convert_to_pdf("report.docx", save_to="report.pdf")` quando vuoi il rilevamento automatico del formato lato server. La stessa chiamata gestisce XLSX, PPTX, ODT, ODS, ODP, OTS, Pages e Numbers.

### Come converto HEIC in WebP con Python?

`client.convert_image("photo.heic", output_format="webp", save_to="photo.webp")`. Il formato di input viene ricavato dall'estensione del nome file, e `jpeg`, `png`, `svg`, `heic` e `webp` si convertono tutti l'uno nell'altro. Stai lavorando in memoria invece che su disco? Avvolgi i byte in `FileData(data=blob, filename="photo.heic")` così l'estensione sopravvive.

### Come estraggo una pagina web in Markdown pulito con Python?

Usa `client.v2.perceive(url, outputs=["markdown"])` e leggi `op.outputs["markdown"].url`, oppure `client.v2.perceive_direct(url, outputs=["markdown"])` per ottenere i byte nel corpo della risposta. Aggiungi `only_main_content=True` per eliminare navigazione e piè di pagina. Per una conversione semplice senza punteggio né estrazione, `convert_url_to_markdown` è la chiamata più leggera.

### Cosa significa render_quality e quando conviene riprovare una pagina?

È un float da 0.0 a 1.0 su ogni lettura V2 che dice quanto onestamente si è renderizzata la pagina. Una schermata di sfida, un muro dei cookie, un accesso protetto da login, una pagina di errore HTTP o uno shell SPA vuoto ottengono un punteggio basso e indicano cosa è scattato in `deductions`, con il dettaglio in `warnings` e lo stato HTTP della pagina stessa in `status_code`. Usalo come filtro: tratta un punteggio basso come il segnale per riprovare con `cache_mode="refresh"`, con un selettore `wait_for` che solo il contenuto reale soddisfa, oppure con `cookies` diversi, invece di dare quel testo in pasto al tuo modello.

### Come trasformo un intero sito di documentazione in chunk RAG con Python?

Chiama `client.v2.ingest(mode="sitemap", url="https://docs.example.com", max_pages=100, chunk=IngestChunkOptions(max_words=512, sentence_overlap=1))`. Il job è asincrono, quindi o interroghi `get_ingest_job(job_id)` finché lo stato non esce da `queued`, `discovering` e `processing`, oppure passi `webhook_url` e aspetti di essere chiamato. Il JSONL finito si trova su `output_url`. Per documenti locali invece che per un sito, `ingest_files` esegue la stessa pipeline.

### L'SDK Python supporta asyncio?

Non in modo nativo. Ogni metodo è sincrono e costruito su `requests`. Dentro un'applicazione asincrona, avvolgi le chiamate in `await asyncio.to_thread(client.convert_to_pdf, "report.docx")` oppure passale a un `concurrent.futures.ThreadPoolExecutor` così l'event loop continua a girare. Il client si può condividere tra thread in sicurezza perché mantiene una `requests.Session` con pool di connessioni.

### Come sopravvive l'SDK a un timeout del proxy su una conversione lunga?

Ogni conversione di un singolo file o di un singolo URL invia un `job_id` generato. Se la richiesta restituisce 5xx, l'SDK interroga `GET /v1/convert/status/{job_id}` ogni 3 secondi per un massimo di 300 secondi, restituisce normalmente una volta che il job riporta `success`, solleva `APIError(500, ...)` se riporta `failed` e solleva `APIError(504, "Conversion timed out")` se la scadenza viene superata. Gli invii di batch di interi siti saltano questo percorso, dato che un errore lì significa che l'invio stesso non è andato a buon fine.
