---
seo_title: API da URL a Markdown | Converti pagine web per LLM | EnConvert
meta_desc: Converti URL in Markdown GitHub-Flavored pulito con POST /v1/convert/url-to-markdown. Estrazione con Readability e frontmatter YAML per pipeline di ingestione LLM.
keywords: api per convertire url in markdown, convertire pagina web in markdown per llm, api html to markdown, convertitore webpage to markdown api, url to markdown per pipeline rag, estrarre contenuto articolo in markdown api, conversione batch url markdown, api estrazione articolo con readability
---

# API da URL a Markdown

L'endpoint `POST /v1/convert/url-to-markdown` converte qualsiasi pagina web pubblicamente accessibile in Markdown GitHub-Flavored pulito, con un blocco di metadati frontmatter YAML. Ogni pagina viene renderizzata in un browser reale, passata attraverso un estrattore di leggibilità che rimuove il boilerplate (navigazione, footer, aside, script, form, pulsanti), quindi serializzata in Markdown con link normalizzati, blocchi di codice delimitati e URL relativi risolti in assoluti. È esattamente ciò che serve alle pipeline di ingestione LLM e RAG al posto dell'HTML grezzo. Le conversioni vengono eseguite in modo sincrono o asincrono in batch, e i risultati vengono restituiti come byte Markdown grezzi o come URL di download prefirmato.

---

## Endpoint

```
POST /v1/convert/url-to-markdown
```

**Content-Type:** `application/json`

**Formato di output:** Markdown (`.md`, UTF-8) con un blocco frontmatter YAML in cima al file contenente i metadati della pagina. Il formato di output non è configurabile. Viene sempre prodotto Markdown con frontmatter YAML.

---

## Autenticazione

Questo endpoint supporta l'autenticazione sia con chiave privata che con chiave pubblica.

### Chiave privata

Includi la tua chiave segreta nell'header `X-API-Key`. Usala per chiamate server-to-server in cui la chiave non è mai esposta al client.

```
X-API-Key: sk_your_private_key
```

### Chiave pubblica con JWT

Per l'uso lato client, genera prima un token JWT usando la tua chiave pubblica, poi passalo come token Bearer.

**Passo 1 -- Ottieni un token:**

```
POST /v1/auth/token
X-API-Key: pk_your_public_key
```

**Passo 2 -- Usa il token:**

```
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
```

<div class="alert alert-info">
<strong>Nota:</strong> Le richieste con chiave pubblica sono limitate a un singolo URL, alla modalità sincrona e al download diretto. La modalità asincrona, l'elaborazione batch, i webhook e le email di notifica non sono disponibili con le chiavi pubbliche.
</div>

---

## Parametri della richiesta

### Parametri di primo livello

| Parametro | Tipo | Obbligatorio | Predefinito | Descrizione | Limitazione per piano |
|-----------|------|----------|---------|-------------|-------------|
| `url` | `string` o `string[]` | Sì | -- | Una singola stringa URL o un array di URL da convertire. Più URL richiedono la modalità asincrona. | -- |
| `async_mode` | `boolean` | No | `false` | Esegue la conversione in modo asincrono. Restituisce immediatamente un `batch_id` per il polling. Obbligatorio per il batch (più URL). | Richiede accesso asincrono |
| `direct_download` | `boolean` | No | `false` | Restituisce i byte Markdown grezzi nel corpo della risposta invece di una risposta JSON con un URL prefirmato. Forzato a `true` per le chiavi pubbliche. Incompatibile con `async_mode` e con più URL. | -- |
| `output_format` | `boolean` | No | `false` | Quando è `true` con più URL, raggruppa tutti i file Markdown di output in un unico archivio ZIP. Richiede più URL. | Richiede accesso all'output ZIP |
| `output_filename` | `string` | No | Generato automaticamente | Nome file personalizzato per il file di output. L'estensione `.md` viene aggiunta automaticamente. Formato predefinito: `{domain}_{timestamp}.md`. | -- |
| `job_id` | `string` | No | -- | ID del job fornito dal client per il recupero in caso di timeout. **Solo chiavi pubbliche.** Quando una conversione sincrona supera i limiti di timeout del reverse proxy, il client può interrogare `GET /v1/convert/status/{job_id}` per recuperare il risultato. Ignorato per le chiavi private. | -- |
| `notification_email` | `string` | No | Email del proprietario del progetto | Indirizzo email da notificare al completamento di un job asincrono. Solo chiavi private. | -- |
| `callback_url` | `string` | No | -- | URL webhook che riceve una richiesta POST al completamento della conversione. Solo chiavi private. | Richiede accesso ai webhook |

### Parametri del browser e del rendering

| Parametro | Tipo | Obbligatorio | Predefinito | Descrizione | Limitazione per piano |
|-----------|------|----------|---------|-------------|-------------|
| `viewport_width` | `integer` | No | `1920` | Larghezza del viewport del browser in pixel. Influisce sui contenuti responsive e su quale variante di layout viene catturata prima dell'estrazione. | -- |
| `viewport_height` | `integer` | No | `1080` | Altezza del viewport del browser in pixel. Usata come riferimento per il rendering e per il calcolo delle unità viewport. | -- |
| `load_media` | `boolean` | No | `true` | Attende il caricamento completo di tutte le immagini e i video prima dell'estrazione. Quando è `false`, l'estrazione è più veloce ma le immagini con lazy-loading possono avere valori `src` segnaposto nell'output Markdown. | -- |
| `enable_scroll` | `boolean` | No | `true` | Scorre la pagina dall'alto verso il basso per attivare il contenuto con lazy-loading (loader basati su IntersectionObserver). | -- |
| `handle_sticky_header` | `boolean` | No | `true` | Rileva header sticky/fissi e riporta lo scroll in cima prima dell'estrazione, così l'ordine dei contenuti viene preservato correttamente. | -- |
| `handle_cookies` | `boolean` | No | `true` | Chiude automaticamente i banner di consenso cookie (OneTrust, Cookiebot, Didomi, Usercentrics e banner generici) prima dell'estrazione. | -- |
| `wait_for_images` | `boolean` | No | `true` | Attende che tutti gli elementi `<img>` finiscano di caricarsi (timeout di 5 secondi per immagine) in modo che il testo `alt` e i valori finali di `src` vengano catturati correttamente. | -- |
| `wait_for_selector` | `string` | No | `null` | Selettore CSS da attendere prima dell'estrazione. Restituisce `422` se non compare mai entro `wait_for_selector_timeout`. Utile per le SPA che idratano il contenuto dopo il caricamento. | -- |
| `wait_for_selector_timeout` | `integer` | No | `10000` | Millisecondi di attesa per `wait_for_selector` (massimo `60000`). | -- |
| `block_ads` | `boolean` | No | `false` | Interrompe le richieste ai domini noti di pubblicità/tracker in modo che non vengano mai caricati o rallentino l'estrazione. | -- |
| `block_media` | `boolean` | No | `false` | Interrompe completamente le richieste di immagini e audio/video per un rendering più veloce e leggero. A differenza di `load_media` (che controlla solo l'attesa), questo impedisce del tutto il download dei media. | -- |

### Autenticazione e richieste personalizzate

| Parametro | Tipo | Obbligatorio | Predefinito | Descrizione | Limitazione per piano |
|-----------|------|----------|---------|-------------|-------------|
| `auth` | `object` | No | `null` | Credenziali HTTP Basic Auth per l'URL di destinazione. Formato: `{"username": "...", "password": "..."}`. Non può essere usato insieme a un header personalizzato `Authorization`. | Richiede accesso basic auth |
| `cookies` | `array` | No | `null` | Array di oggetti cookie da iniettare prima della navigazione. Massimo 50 cookie. Ogni cookie deve avere `name`, `value` e almeno uno tra `domain` o `url`. | Richiede accesso basic auth |
| `headers` | `object` | No | `null` | Dizionario di header HTTP personalizzati inviati con ogni richiesta all'URL di destinazione. Massimo 20 header. Header bloccati: `host`, `content-length`, `transfer-encoding`, `connection`, `upgrade`, `te`, `trailer`. | Richiede accesso basic auth |

<div class="alert alert-warning">
<strong>Non supportato:</strong> I parametri <code>single_page</code> e <code>pdf_options</code> dell'endpoint <a href="/it/docs/endpoints/convert/web-pages/url-to-pdf">url-to-pdf</a> sono accettati per mantenere la stessa forma della richiesta, ma non hanno alcun effetto sull'output Markdown. Markdown non ha alcun concetto di pagine, margini o orientamento.
</div>

---

## Schema dell'oggetto cookie

Ogni elemento dell'array `cookies` deve seguire questa struttura:

| Campo | Tipo | Obbligatorio | Predefinito | Descrizione |
|-------|------|----------|---------|-------------|
| `name` | `string` | Sì | -- | Nome del cookie. |
| `value` | `string` | Sì | -- | Valore del cookie. |
| `domain` | `string` | Condizionale | -- | Dominio del cookie. Deve essere fornito almeno uno tra `domain` o `url`. |
| `url` | `string` | Condizionale | -- | URL da associare al cookie. Deve essere fornito almeno uno tra `domain` o `url`. |
| `path` | `string` | No | `"/"` | Percorso del cookie. Il valore predefinito è `"/"` quando `domain` è impostato. |

---

## Risposta

### Sincrona con download diretto (`direct_download=true`)

**Chiave privata** -- restituisce byte Markdown grezzi:

```
HTTP 200 OK
Content-Type: text/markdown; charset=utf-8
Content-Disposition: inline; filename="example_20260421_123456789.md"
X-Object-Key: env/files/{project_id}/url-to-markdown/example_20260421_123456789.md
X-File-Size: 8421
X-Conversion-Time: 6.3
X-Filename: example_20260421_123456789.md

(UTF-8 Markdown with YAML frontmatter)
```

**Chiave pubblica** -- restituisce JSON con un URL prefirmato:

```json
{
    "presigned_url": "https://spaces.example.com/...",
    "object_key": "env/files/{project_id}/url-to-markdown/example_20260421_123456789.md",
    "filename": "example_20260421_123456789.md",
    "file_size": 8421,
    "conversion_time_seconds": 6.3,
    "job_id": "client-provided-id"
}
```

### Sincrona senza download diretto (`direct_download=false`)

Disponibile solo con chiavi private.

```json
{
    "presigned_url": "https://spaces.example.com/...",
    "object_key": "env/files/{project_id}/url-to-markdown/example_20260421_123456789.md",
    "filename": "example_20260421_123456789.md",
    "file_size": 8421,
    "conversion_time_seconds": 6.3
}
```

### Modalità asincrona

Restituisce immediatamente un `batch_id` per il polling.

```
HTTP 202 Accepted
```

```json
{
    "status": "processing",
    "batch_id": "550e8400-e29b-41d4-a716-446655440000",
    "url_count": 5,
    "output_format": "individual"
}
```

Quando `output_format=true` (raggruppamento ZIP):

```json
{
    "status": "processing",
    "batch_id": "550e8400-e29b-41d4-a716-446655440000",
    "url_count": 5,
    "output_format": "zip"
}
```

### Polling dello stato del job (solo chiavi pubbliche)

Per il recupero in caso di timeout con chiave pubblica:

```
GET /v1/convert/status/{job_id}
Authorization: Bearer <jwt_token>
```

| Stato | Risposta |
|--------|----------|
| In elaborazione | `{"status": "processing"}` |
| Successo | `{"status": "success", "presigned_url": "...", "object_key": "..."}` |
| Fallito | `{"status": "failed", "error": "..."}` |

### Polling dello stato del batch (solo chiavi private)

Per i job batch asincroni, esegui il polling con il `batch_id` ricevuto nella risposta 202:

```
GET /v1/convert/batch/{batch_id}
X-API-Key: sk_your_private_key
```

Restituisce lo stato aggregato, gli stati per singolo URL e gli URL di download prefirmati. Consulta [Polling dello stato del batch](/it/docs/endpoints/convert/web-pages/url-to-pdf.md#batch-status-polling-private-keys-only) per lo schema completo della risposta.

### Payload di callback del webhook

Quando viene fornito un `callback_url`, EnConvert invia una richiesta POST a quell'URL al completamento.

**Job con singolo URL:**

```json
{
    "job_id": "activity_id",
    "status": "success",
    "batch_id": "...",
    "gcs_uri": "object_key",
    "filename": "example_20260421_123456789.md",
    "file_size": 8421
}
```

**Job batch:**

```json
{
    "job_id": "activity_id",
    "status": "success",
    "batch_id": "...",
    "total_tasks": 10,
    "successful_tasks": 8,
    "failed_tasks": 2,
    "tasks": [
        {"url": "https://example.com/page1", "status": "success", "filename": "page1.md"},
        {"url": "https://example.com/page2", "status": "failed", "filename": null}
    ]
}
```

---

## Formato di output

Ogni file Markdown inizia con un blocco frontmatter YAML contenente i metadati della pagina, seguito dal corpo dell'articolo estratto.

```markdown
---
url: https://example.com/articles/my-post
title: My Post Title
description: A short summary from the meta description or og:description tag.
links:
  - url: https://example.com/related
    text: Related article
  - url: https://example.com/about
    text: About the author
images:
  - url: https://example.com/hero.jpg
    alt: Hero image alt text
  - url: https://example.com/diagram.png
    alt: Architecture diagram
---

# My Post Title

Opening paragraph of the article body, converted to GitHub-Flavored Markdown...

## A Section Heading

- List item one
- List item two

\`\`\`python
def example():
    return "code blocks are fenced with language hints"
\`\`\`

[A link in the body](https://example.com/linked-page)

![An image in the body](https://example.com/inline-image.png)
```

### Campi del frontmatter

| Campo | Tipo | Descrizione |
|-------|------|-------------|
| `url` | `string` | L'URL finale dopo i redirect (non sempre l'URL che hai inviato). |
| `title` | `string` | Il titolo della pagina preso da `<title>`, con fallback al titolo breve rilevato da Readability. |
| `description` | `string` | Il valore di `<meta name="description">`, con fallback a `<meta property="og:description">`. |
| `links` | `array` | Ogni `<a href>` trovato sulla pagina, con URL assoluti e testo di ancoraggio visibile. |
| `images` | `array` | Ogni `<img src>` trovato sulla pagina, con URL assoluti e testo `alt`. |

### Convenzioni Markdown

- **Stile delle intestazioni:** ATX (`#`, `##`, `###`)
- **Punti elenco:** `-`
- **Enfasi:** `*bold*`, `*italic*` con `*` e `_` escaped nel testo letterale
- **Interruzioni di riga leggere:** due spazi finali (preservati nell'output)
- **Blocchi di codice:** delimitati (` ``` `) con suggerimenti di linguaggio rilevati da `class="language-xxx"`, `class="lang-xxx"`, `class="highlight-source-xxx"`, `data-lang` e `data-language`
- **Link:** `[text](url)` quando è presente il testo di ancoraggio, forma autolink `<url>` quando l'ancora è vuota; i link solo-ancora (`#foo`) e i link `javascript:` vengono trasformati in testo semplice
- **Immagini:** `![alt](src)`, con `title` preservato quando presente, con fallback a `data-src` quando `src` è assente (immagini con lazy-loading)
- **Linee orizzontali:** `---`

---

## Funzionalità

### Estrazione pulita dei contenuti

EnConvert usa l'algoritmo Readability (la stessa libreria che alimenta Firefox Reader View) per isolare il contenuto principale dell'articolo dal resto della pagina, poi applica un secondo passaggio di post-elaborazione per produrre Markdown pulito.

**Rimosso prima della conversione:**

- Navigazione (`<nav>`), footer (`<footer>`), aside (`<aside>`)
- Script (`<script>`, `<noscript>`), stili (`<style>`), iframe, form, pulsanti
- SVG inline, canvas ed elementi template
- Gli attributi `style`, `class`, `id` e tutti gli attributi degli event handler `on*`

**Preservato:**

- Intestazioni, paragrafi, liste, tabelle, blockquote, blocchi di codice
- Link con il loro `href` e testo di ancoraggio (URL assoluti)
- Immagini con `alt`, `title` e `src` assoluto
- Figure e figcaption (le immagini inline vengono mantenute al loro interno)

### Modalità di cattura pulita

Prima dell'estrazione, la pagina viene renderizzata in un browser reale e ripulita nello stesso modo di [url-to-pdf](/it/docs/endpoints/convert/web-pages/url-to-pdf.md):

- **Banner di consenso cookie** -- Chiusi automaticamente sulla pagina principale e negli iframe (OneTrust, Cookiebot, Didomi, Usercentrics e banner generici).
- **Chiusura di modali e popup** -- Overlay chiusi tramite tasto Escape, pulsanti di chiusura ARIA, pulsanti di chiusura basati su classe e pulsanti di dialogo basati su role.
- **Rivelazione delle animazioni allo scroll** -- Forza la visibilità degli elementi nascosti da WOW.js, AOS, ScrollReveal, GSAP ScrollTrigger e classi di animazione generiche.
- **Gestione degli header sticky** -- Gli header sticky/fissi vengono rilevati e la pagina viene riportata in cima in modo da preservare l'ordine dei contenuti.

### Risoluzione degli URL assoluti

Ogni `href` e `src` relativo nell'articolo estratto viene risolto rispetto all'URL finale della pagina (dopo i redirect), quindi l'output Markdown contiene sempre link assoluti e cliccabili, il che è utile per le pipeline di ingestione LLM che altrimenti vedrebbero percorsi relativi non funzionanti.

I link solo-ancora (`#section`), i link `javascript:`, `mailto:` e `tel:` non vengono riscritti. I link solo-ancora e `javascript:` vengono trasformati in testo semplice perché non hanno significato al di fuori della pagina originale.

### Rilevamento del linguaggio dei blocchi di codice

I blocchi di codice vengono delimitati con un suggerimento di linguaggio rilevato, quando possibile:

```
<pre><code class="language-python">...</code></pre>    →   ```python
<pre data-lang="js">...</pre>                          →   ```js
<pre><code class="highlight-source-shell">...</code></pre> →   ```shell
```

Vengono riconosciute le classi corrispondenti a `language-*`, `lang-*`, `highlight-source-*` e `brush:*`, oltre agli attributi `data-lang` e `data-language` sia su `<pre>` che sul suo `<code>` annidato. Se non viene trovato alcun suggerimento, il blocco viene delimitato senza etichetta di linguaggio.

### HTTP Basic Auth

Passa `auth` con `username` e `password` per convertire pagine protette da HTTP Basic Authentication.

```json
{
    "url": "https://staging.example.com/docs/article",
    "auth": {
        "username": "admin",
        "password": "secret"
    }
}
```

### Iniezione di cookie

Inietta fino a 50 cookie prima del caricamento della pagina. Utile per convertire pagine di articoli riservate ai membri o specifiche per una determinata lingua/area geografica.

```json
{
    "url": "https://example.com/members/post",
    "cookies": [
        {"name": "session_id", "value": "abc123", "domain": "example.com"},
        {"name": "locale", "value": "en-US", "domain": "example.com"}
    ]
}
```

### Header personalizzati

Invia fino a 20 header HTTP personalizzati con ogni richiesta alla pagina di destinazione.

```json
{
    "url": "https://example.com/api-docs",
    "headers": {
        "X-Custom-Token": "my-token-value",
        "Accept-Language": "en-US"
    }
}
```

### Caricamento lazy delle immagini

Quando `load_media` e `enable_scroll` sono abilitati (entrambi hanno come valore predefinito `true`), il convertitore scorre la pagina lentamente per attivare i loader lazy, poi attende che tutte le immagini finiscano di caricarsi prima di catturare l'HTML finale. Questo garantisce che i valori `data-src` siano stati promossi a valori `src` reali e che l'elenco `images` del frontmatter sia completo.

Imposta `load_media=false` per un'estrazione più veloce quando ti serve solo il corpo testuale. Nell'output potrebbero però rimanere valori `src` segnaposto.

### Funzionalità di rendering aggiuntive

- **Normalizzazione delle unità viewport** -- Le unità viewport CSS (`vh`, `svh`, `lvh`, `dvh`) vengono convertite in valori fissi in pixel prima dell'estrazione.
- **Modalità stealth** -- Mascheramento del fingerprint del browser per evitare il rilevamento bot su pagine protette.
- **Intercettazione dei popup** -- Chiude automaticamente eventuali nuove schede del browser o popup attivati dalla pagina.
- **Bypass CSP** -- Gestisce le restrizioni di Content Security Policy e Trusted Types che altrimenti bloccherebbero la manipolazione della pagina.

---

## Limitazioni per piano di abbonamento

| Funzionalità | Founding | Indie | Studio | Enterprise |
|---------|------|---------|-----|------------|
| Conversione di base (singolo URL, sincrona) | Sì | Sì | Sì | Sì |
| Opzioni di viewport e rendering | Sì | Sì | Sì | Sì |
| Modalità asincrona | No | Sì | Sì | Sì |
| Elaborazione batch (più URL) | No | Sì | Sì | Sì |
| Raggruppamento output ZIP | No | No | Sì | Sì |
| Callback webhook | No | No | Sì | Sì |
| HTTP Basic Auth | No | Sì | Sì | Sì |
| Iniezione di cookie | No | Sì | Sì | Sì |
| Header personalizzati | No | Sì | Sì | Sì |
| Conversioni mensili | 100 | In base al piano | In base al piano | Illimitate |
| Limite dimensione batch | 0 | In base al piano | In base al piano | Illimitato |
| Conservazione dei file | 1 ora | In base al piano | In base al piano | In base al piano |

---

## Modalità asincrona

La modalità asincrona è utile per conversioni di lunga durata o quando si convertono più URL.

### Come funziona

1. Invia una richiesta con `async_mode=true` (oppure passa più URL, che abilita automaticamente la modalità asincrona).
2. L'API restituisce immediatamente HTTP 202 con un `batch_id` e un `url_count`.
3. Ogni URL viene convertito in background, caricato nello storage e monitorato singolarmente.
4. Monitora il completamento tramite **polling dello stato del batch**, **notifica email** o **callback webhook**.

### Notifica email

Per impostazione predefinita, viene inviata un'email di completamento all'indirizzo email del proprietario del progetto. Sovrascrivi con `notification_email`:

```json
{
    "url": ["https://example.com/page1", "https://example.com/page2"],
    "async_mode": true,
    "notification_email": "team@example.com"
}
```

### Callback webhook

Fornisci un `callback_url` per ricevere una notifica POST automatica al completamento:

```json
{
    "url": ["https://example.com/page1", "https://example.com/page2"],
    "async_mode": true,
    "callback_url": "https://your-server.com/webhook/enconvert"
}
```

Il webhook viene inviato con un timeout di 30 secondi e considera HTTP 200, 201, 202 e 204 come consegna riuscita.

---

## Elaborazione batch e in blocco

Converti più URL in un'unica richiesta. Richiede la modalità asincrona e una chiave privata.

### Output individuale (predefinito)

Ogni URL produce un file Markdown separato:

```json
{
    "url": [
        "https://example.com/post-1",
        "https://example.com/post-2",
        "https://example.com/post-3"
    ],
    "async_mode": true
}
```

### Output raggruppato in ZIP

Raggruppa tutti i file Markdown in un unico archivio ZIP:

```json
{
    "url": [
        "https://example.com/post-1",
        "https://example.com/post-2",
        "https://example.com/post-3"
    ],
    "async_mode": true,
    "output_format": true,
    "output_filename": "blog-archive"
}
```

Il file ZIP viene chiamato `{output_filename}_{timestamp}.zip` oppure `batch_{timestamp}.zip` se non viene fornito alcun nome personalizzato.

---

## Esempi di codice

### Python (chiave privata)

```python
import requests

response = requests.post(
    "https://api.enconvert.com/v1/convert/url-to-markdown",
    headers={"X-API-Key": "sk_your_private_key"},
    json={
        "url": "https://example.com/articles/my-post",
        "direct_download": True
    }
)

response.raise_for_status()
markdown_text = response.content.decode("utf-8")
print(markdown_text)
```

### PHP (chiave privata)

```php
$ch = curl_init("https://api.enconvert.com/v1/convert/url-to-markdown");
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        "Content-Type: application/json",
        "X-API-Key: sk_your_private_key"
    ],
    CURLOPT_POSTFIELDS => json_encode([
        "url" => "https://example.com/articles/my-post"
    ])
]);

$response = curl_exec($ch);
curl_close($ch);

$data = json_decode($response, true);
echo $data["presigned_url"];
```

### Node.js (chiave privata)

```javascript
const response = await fetch("https://api.enconvert.com/v1/convert/url-to-markdown", {
    method: "POST",
    headers: {
        "Content-Type": "application/json",
        "X-API-Key": "sk_your_private_key"
    },
    body: JSON.stringify({
        url: "https://example.com/articles/my-post"
    })
});

const data = await response.json();
console.log(data.presigned_url);
```

### Go (chiave privata)

```go
package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "io"
    "net/http"
)

func main() {
    body, _ := json.Marshal(map[string]interface{}{
        "url": "https://example.com/articles/my-post",
    })

    req, _ := http.NewRequest("POST", "https://api.enconvert.com/v1/convert/url-to-markdown", bytes.NewBuffer(body))
    req.Header.Set("Content-Type", "application/json")
    req.Header.Set("X-API-Key", "sk_your_private_key")

    resp, _ := http.DefaultClient.Do(req)
    defer resp.Body.Close()

    respBody, _ := io.ReadAll(resp.Body)
    fmt.Println(string(respBody))
}
```

### JavaScript -- Browser (chiave pubblica)

```javascript
// Step 1: Get a JWT token
const tokenRes = await fetch("https://api.enconvert.com/v1/auth/token", {
    method: "POST",
    headers: { "X-API-Key": "pk_your_public_key" }
});
const { token } = await tokenRes.json();

// Step 2: Convert URL to Markdown (public keys receive raw bytes)
const convertRes = await fetch("https://api.enconvert.com/v1/convert/url-to-markdown", {
    method: "POST",
    headers: {
        "Content-Type": "application/json",
        "Authorization": `Bearer ${token}`
    },
    body: JSON.stringify({
        url: "https://example.com/articles/my-post"
    })
});

const markdown = await convertRes.text();
console.log(markdown);
```

### React (chiave pubblica)

```jsx
import { useState } from "react";

function UrlToMarkdown() {
    const [loading, setLoading] = useState(false);
    const [markdown, setMarkdown] = useState("");

    async function convertUrl() {
        setLoading(true);
        try {
            // Get JWT token
            const tokenRes = await fetch("https://api.enconvert.com/v1/auth/token", {
                method: "POST",
                headers: { "X-API-Key": "pk_your_public_key" }
            });
            const { token } = await tokenRes.json();

            // Convert
            const convertRes = await fetch("https://api.enconvert.com/v1/convert/url-to-markdown", {
                method: "POST",
                headers: {
                    "Content-Type": "application/json",
                    "Authorization": `Bearer ${token}`
                },
                body: JSON.stringify({ url: "https://example.com/articles/my-post" })
            });

            setMarkdown(await convertRes.text());
        } finally {
            setLoading(false);
        }
    }

    return (
        <div>
            <button onClick={convertUrl} disabled={loading}>
                {loading ? "Converting..." : "Convert to Markdown"}
            </button>
            {markdown && <pre>{markdown}</pre>}
        </div>
    );
}

export default UrlToMarkdown;
```

---

## Risposte di errore

| Stato | Condizione |
|--------|-----------|
| `400 Bad Request` | Parametro `url` mancante o vuoto |
| `400 Bad Request` | `output_format=true` con un singolo URL (richiede più URL) |
| `400 Bad Request` | `direct_download=true` con più URL |
| `400 Bad Request` | `direct_download=true` con `async_mode=true` |
| `400 Bad Request` | Oggetto `auth` non valido (`username` o `password` mancanti) |
| `400 Bad Request` | `cookies` non valido (non è un array, supera le 50 voci, campi obbligatori mancanti) |
| `400 Bad Request` | `headers` non valido (non è un oggetto, supera le 20 voci, nomi di header bloccati, valori non stringa) |
| `400 Bad Request` | Conflitto tra `auth` e l'header personalizzato `Authorization` |
| `400 Bad Request` | Chiave pubblica che tenta di usare più URL |
| `401 Unauthorized` | Chiave API / token JWT mancante o non valido |
| `402 Payment Required` | Quota mensile di ops esaurita |
| `402 Payment Required` | Il batch supererebbe la quota mensile di ops residua |
| `402 Payment Required` | Limite di storage raggiunto |
| `403 Forbidden` | Endpoint non incluso tra quelli consentiti dalla chiave API |
| `403 Forbidden` | Funzionalità non disponibile nel piano attuale (asincrono, webhook, ZIP, basic auth) |
| `403 Forbidden` | La dimensione del batch supera il limite del piano |
| `404 Not Found` | ID del job non trovato (durante il polling dello stato) |
| `500 Internal Server Error` | Conversione fallita (crash del browser, errore di navigazione, errore di estrazione) |

---

## Limiti

| Limite | Valore |
|-------|-------|
| Timeout di navigazione pagina | 60 secondi |
| Timeout di caricamento per immagine | 5 secondi |
| Timeout di chiusura banner cookie | 3 secondi |
| Numero massimo di cookie per richiesta | 50 |
| Numero massimo di header personalizzati per richiesta | 20 |
| Operazioni mensili | Dipende dal piano (Founding: 500) |
| Dimensione batch | Dipende dal piano (Founding: disabilitato) |
| Conservazione dei file | Dipende dal piano (Founding: 1 ora) |
| Timeout di consegna webhook | 30 secondi |

---

## Domande frequenti

### Come converto una pagina web in Markdown con una REST API?

Invia una richiesta `POST` a `/v1/convert/url-to-markdown` con un `url` nel corpo JSON e la tua chiave nell'header `X-API-Key`. Ricevi come risposta un JSON con un `presigned_url` verso il file Markdown, oppure i byte Markdown UTF-8 grezzi se imposti `direct_download=true`.

### Posso convertire pagine web in Markdown per pipeline LLM e RAG?

Sì. L'output è pensato per l'ingestione LLM. L'algoritmo Readability (la stessa libreria dietro Firefox Reader View) isola l'articolo principale, il boilerplate come `<nav>`, `<footer>`, script e form viene rimosso, ogni link e URL immagine relativo viene risolto in un URL assoluto, e un blocco frontmatter YAML porta con sé `url`, `title`, `description`, `links` e `images` della pagina.

### Posso convertire più URL in Markdown in un'unica richiesta API?

Sì. Passa un array di URL in `url` con `async_mode=true` (richiede una chiave privata e un piano con accesso al batch); l'API restituisce HTTP `202` con un `batch_id` che interroghi tramite `GET /v1/convert/batch/{batch_id}`. Imposta `output_format=true` per raggruppare tutti i file Markdown in un unico archivio ZIP.

### Perché alcune immagini nel mio output Markdown hanno valori src segnaposto?

Succede quando `load_media=false`. L'estrazione è più veloce, ma le immagini con lazy-loading possono mantenere valori `src` segnaposto. Mantieni `load_media` e `enable_scroll` sul valore predefinito `true` in modo che la pagina venga scorsa per attivare i loader lazy e che ogni immagine finisca di caricarsi (timeout di 5 secondi per immagine) prima della cattura.

### L'API da URL a Markdown funziona su pagine protette da login?

Sì, sui piani con accesso basic auth: passa `auth` con `username` e `password` per l'HTTP Basic Auth, inietta fino a 50 `cookies` di sessione, oppure invia fino a 20 `headers` personalizzati, il che è utile per pagine di articoli riservate ai membri o in staging.
