---
seo_title: Discover (fase 2): API sitemap crawler | EnConvert
meta_desc: Beta privata, fase 2: elenca tutti gli URL di un sito in una chiamata. Parsing della sitemap più scansione HTTP, senza rendering per pagina, in un elenco piatto.
keywords: api per elencare tutti gli url di un sito, crawler sitemap api, estrarre url da sitemap xml api, scansionare un sito web via http api, trovare tutte le pagine di un sito api, mappare gli url di un dominio api, sitemap parser api, api mappatura sito web
---

# API Sitemap Crawler

<div class="alert alert-warning">
<strong>Beta privata.</strong> Discover è già chiamabile oggi con la tua normale chiave API, su qualsiasi piano compreso Founding, e scala dalla tua quota mensile di ops come ogni altra chiamata. Non è annunciato né disponibile in generale: le forme di richiesta e risposta possono cambiare senza preavviso e non c'è alcun impegno di stabilità o di supporto, quindi non costruirci ancora sopra nulla di critico. La roadmap è su <a href="/it/docs/coming-soon">In arrivo</a>, e ogni rilascio viene annunciato nel <a href="/it/changelog">changelog</a>.
</div>

`POST /v2/discover` è un'API di sitemap crawling che elenca gli URL di un
sito in modo economico: una scansione HTTP-first più il parsing della
sitemap, con un fallback opzionale di rendering JavaScript per le
single-page app. Non c'è alcuno screenshot, alcun PDF né alcun artefatto
salvato. Restituisce un elenco piatto e deduplicato
di URL con un conteggio di provenienza per ciascuno, in modo sincrono in
un'unica chiamata. Sarà la primitiva leggera per "mappare il sito":
puntala a un dominio, ottieni le pagine che vale la pena elaborare, poi
passa quell'elenco all'[endpoint perceive](/it/docs/endpoints/perceive.md) oppure a
un job di ingest in modalità crawl.

Ecco la chiamata minima utile. Invia un URL, ricevi la sua mappa:

```bash
curl -X POST https://api.enconvert.com/v2/discover \
  -H "X-API-Key: sk_your_private_key" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com"
  }'
```

La risposta è un semplice elenco di URL con contatori di provenienza.
Non ci sono URL firmati, né ID operazione, né polling:

```json
{
    "url": "https://example.com",
    "mode": "hybrid",
    "total": 47,
    "urls": [
        "https://example.com/",
        "https://example.com/pricing",
        "https://example.com/docs",
        "https://example.com/blog/launch"
    ],
    "pages_crawled": 12,
    "truncated": false,
    "robots_respected": false,
    "sources": {"sitemap": 42, "crawl": 30},
    "warnings": []
}
```

---

## Endpoints

| Metodo | Percorso | Scopo |
|--------|------|---------|
| `POST` | `/v2/discover` | Mappa gli URL di un sito solo via HTTP e restituisce un elenco piatto con i conteggi per fonte. |

`/v2/discover` espone un solo percorso. È stateless: non c'è una riga di
operazione da recuperare né un job da interrogare, quindi non esiste un
endpoint `GET` di supporto come [avviene per perceive](/it/docs/endpoints/perceive.md).

**Content-Type:** `application/json` sulla `POST`.

---

## Autenticazione

Autenticati con una chiave privata nell'header `X-API-Key` per le
chiamate server-to-server. È il percorso usato negli esempi qui sotto.

```http
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, si trova
nella [guida all'autenticazione](/it/docs/authentication.md).

Ogni chiave API porta con sé un'allowlist di endpoint consentiti. Se
`/v2/discover` non è nell'elenco della chiave, la richiesta viene
rifiutata con `403` e un messaggio che indica il percorso bloccato.

---

## Come funziona discover

Una richiesta viene eseguita interamente via HTTP. Il singleton Chrome
headless che alimenta [perceive](/it/docs/endpoints/perceive.md) non viene mai
toccato. Il percorso di crawl usa la strategia di crawler HTTP di
Crawl4AI (una GET `httpx` più un parsing dei link con `lxml` per pagina),
e il percorso sitemap riutilizza gli stessi helper puramente HTTP per
sitemap e feed usati dal resto della piattaforma.

1. **Verifica del seed.** L'URL che invii viene controllato per SSRF
   prima di qualsiasi fetch: schema, credenziali incorporate, hostname
   bloccati e IP risolto vengono tutti validati. Un URL che si risolve in
   un indirizzo privato, loopback, link-local o di metadata cloud viene
   rifiutato con `400`. Il seed è sempre incluso come prima voce nella
   propria mappa.
2. **Raccolta dalle sitemap.** In modalità `sitemap` o `hybrid`, discover
   legge `robots.txt` e recupera gli URL `Sitemap:` che vi sono dichiarati,
   sonda `sitemap.xml` (ricorrendo nei figli `<sitemapindex>`), ed estrae
   le pagine dei feed RSS/Atom. Esegue questi controlli sia sull'host che
   hai inviato sia sul dominio registrabile (apex) del sito, così una
   sitemap pubblicata solo sull'apex viene comunque trovata a partire da un
   seed su un sottodominio o su un percorso profondo. Una sitemap mancante
   o non funzionante diventa un warning, non un errore.
3. **Crawl via HTTP.** In modalità `crawl` o `hybrid`, discover esegue
   una scansione breadth-first a partire dal seed fino a `max_depth`,
   raccogliendo i link `<a href>` dall'HTML grezzo di ogni pagina
   recuperata. Ogni link seguito viene sottoposto a screening SSRF prima
   di essere recuperato.
4. **Normalizzazione e filtro.** L'elenco grezzo combinato viene
   canonicalizzato (frammenti e parametri di tracking rimossi, porte
   predefinite eliminate, chiavi di query ordinate), poi passa attraverso
   il controllo dello stesso dominio, i tuoi pattern regex di inclusione
   ed esclusione, il filtro `robots.txt`, la deduplicazione e infine il
   tetto di `max_urls`.

Sul percorso solo HTTP, una single-page app renderizzata lato client
restituisce solo il proprio HTML shell in modalità `crawl`, tipicamente
il seed più zero o un URL. Il fallback opzionale `render_js` (vedi
[Rendering JavaScript](#javascript-rendering)) colma questa lacuna: nella
sua impostazione predefinita `"auto"`, discover renderizza la pagina una
volta nel browser e raccoglie i link che essa inietta a runtime ogni volta
che la scansione HTTP restituisce solo uno shell. La modalità `sitemap`
resta un percorso veloce e senza browser per le SPA attente alla SEO, che
di norma pubblicano una sitemap.

---

## Parametri della richiesta

### Principali

| Parametro | Tipo | Predefinito | Descrizione |
|-----------|------|---------|-------------|
| `url` | `string` | nessuno | Il sito da mappare. Deve iniziare con `http://` o `https://`. Massimo 2,048 caratteri. Obbligatorio. |
| `mode` | `string` | `"hybrid"` | `sitemap`, `crawl` o `hybrid`. Vedi [Modalità](#modes). |
| `max_urls` | `integer` | `100` | Numero massimo di URL restituiti. 1–1,000. L'elenco viene limitato qui e `truncated` segnala se ne esistevano altri. |
| `max_depth` | `integer` | `2` | Profondità di scansione dal seed, in modalità `crawl`/`hybrid`. 1–5. |
| `same_domain_only` | `boolean` | `true` | Mantiene solo gli URL sull'host del seed. Quando è `false`, vengono mantenuti anche i link fuori host scoperti durante la scansione. |
| `render_js` | `string` | `"auto"` | Scoperta renderizzata nel browser per siti JavaScript/SPA (solo `crawl`/`hybrid`). `auto` renderizza solo quando la scansione HTTP restituisce uno shell; `always` la forza; `never` resta solo HTTP. Vedi [Rendering JavaScript](#javascript-rendering). |

### Filtri

| Parametro | Tipo | Predefinito | Descrizione |
|-----------|------|---------|-------------|
| `include_patterns` | `string[]` | `[]` | Allowlist di regex Python (semantica `re.search`, non glob). Un URL deve corrispondere ad almeno una per essere mantenuto. Vuoto significa consenti tutto. Massimo 50 pattern. |
| `exclude_patterns` | `string[]` | `[]` | Denylist di regex Python. Un URL che corrisponde a qualsiasi pattern viene scartato. Applicata dopo `include_patterns`. Massimo 50 pattern. |
| `respect_robots` | `boolean` | `false` | Quando è `true`, gli URL non consentiti dal `robots.txt` del sito vengono rimossi dall'elenco restituito. |

I pattern sono vere espressioni regolari Python, compilate al momento
della validazione. Un pattern malformato produce un `422` al bordo, non
un `500` a metà scansione. Poiché la semantica è `re.search`, una
semplice sottostringa come `"/blog/"` corrisponde ovunque nell'URL,
quindi ancorala con `^`/`$` se ti serve una corrispondenza posizionale.

> **Da segnalare subito.** `respect_robots` viene applicato al momento
> del filtro in output, non al momento del fetch. Crawl4AI 0.8.9 non ha
> un gate robots nativo, quindi in modalità `crawl` o `hybrid` una pagina
> non consentita può comunque essere recuperata via HTTP e poi scartata
> prima di raggiungere la risposta. A differenza di
> [perceive](/it/docs/endpoints/perceive.md), dove `respect_robots=true` rifiuta un
> URL non consentito con `403`, discover non restituisce mai `403` per
> una regola robots. Scarta silenziosamente l'URL e non riporta nulla.

### Modalità {: #modes }

| `mode` | Cosa fa |
|--------|--------------|
| `sitemap` | Voci sitemap da `robots.txt`, `sitemap.xml` sondato (con ricorsione sull'indice) e pagine dei feed RSS/Atom. Istantaneo, nessuna scansione. |
| `crawl` | Scansione breadth-first solo HTTP a partire dal seed, che raccoglie i link `<a href>` dal markup grezzo. |
| `hybrid` (predefinita) | L'unione deduplicata di `sitemap` e `crawl`. |

La modalità `crawl` recupera fino al tuo `max_urls` completo (fino a
1,000), così un sito grande viene enumerato in un solo passaggio. La
modalità crawl è solo HTTP (nessun browser), quindi resta veloce. Gli
operatori possono abbassare il tetto con la variabile d'ambiente
`DISCOVER_CRAWL_MAX_PAGES` se una scansione molto grande dovesse mai
richiedere un limite. `pages_crawled` nella risposta ti dice esattamente
quante GET sono state eseguite.

Il fetch delle sitemap gestisce le **sitemap compresse con gzip**
(`sitemap.xml.gz` e qualsiasi sitemap servita come `application/gzip`),
e invia header da browser realistici, così i siti dietro un WAF hanno
molte più probabilità di restituire la loro sitemap invece di un
`403`/`503`.

### Rendering JavaScript {: #javascript-rendering }

Le modalità `crawl` e `hybrid` leggono l'HTML grezzo che un server
restituisce, quindi una single-page app renderizzata lato client che
costruisce i propri link nel browser è invisibile per loro. `render_js`
attiva un fallback browser limitato che colma questa lacuna:

| `render_js` | Cosa fa |
|-------------|---------|
| `auto` (predefinito) | Esegue prima la scansione HTTP; renderizza nel browser solo quando questa restituisce uno shell nudo (un URL o nessuno), poi raccoglie i link iniettati lato client. |
| `always` | Esegue sempre la scansione renderizzata nel browser. |
| `never` | Resta rigorosamente solo HTTP, il comportamento preesistente. |

Il fallback usa lo stesso Chrome headless condiviso di
[perceive](/it/docs/endpoints/perceive.md) ed è limitato a poche pagine così che la
chiamata resti entro il timeout della richiesta. I link che trova vengono
conteggiati sotto la chiave `crawl_js` in `sources`. Si applica solo alle
modalità `crawl` e `hybrid`, perché la modalità `sitemap` è già senza
browser e non ne è influenzata.

---

## Risposta

`POST /v2/discover` restituisce direttamente questo oggetto, senza alcun
job asincrono né una seconda chiamata.

| Campo | Tipo | Descrizione |
|-------|------|-------------|
| `url` | `string` | L'URL seed che hai inviato. |
| `mode` | `string` | La modalità eseguita: `sitemap`, `crawl` o `hybrid`. |
| `total` | `integer` | Numero di URL in `urls` (dopo dedup, filtro e limite). |
| `urls` | `string[]` | L'elenco di URL deduplicato, normalizzato e limitato. Il seed è sempre il primo candidato. |
| `pages_crawled` | `integer` | GET HTTP emesse dalla scansione. `0` in modalità `sitemap` pura. |
| `truncated` | `boolean` | `true` quando esistevano più URL unici di quanti ne permettesse `max_urls`. |
| `robots_respected` | `boolean` | Rispecchia il valore di `respect_robots` che hai inviato. |
| `sources` | `object` | Conteggio grezzo di URL per fonte prima di dedup/filtro, ad es. `{"sitemap": 42, "crawl": 30}`. Le chiavi includono `sitemap`, `crawl`, `sitemap_apex` (sitemap trovate sul dominio apex) e `crawl_js` (link dal fallback di rendering JavaScript). I conteggi si sovrappongono e sommano oltre `total`. |
| `warnings` | `string[]` | Note non fatali: una sitemap mancante, una scansione fallita, un `robots.txt` irraggiungibile. |

I conteggi di `sources` sono grezzi: rappresentano il numero di URL
prodotti da ciascun percorso prima che venissero eseguiti la
normalizzazione, il controllo dello stesso dominio, i tuoi filtri e la
deduplicazione. Di norma sommano più di `total`, perché la modalità
`hybrid` trova le stesse pagine sia dalla sitemap sia dalla scansione.
Usali per capire quale percorso sta portando la mappa, non come conteggio
post-filtro.

---

## Esempi di codice

### curl: mappa hybrid predefinita

```bash
curl -X POST https://api.enconvert.com/v2/discover \
  -H "X-API-Key: sk_your_private_key" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com"
  }'
```

### curl: solo sitemap, pagine del blog, limite a 500

```bash
curl -X POST https://api.enconvert.com/v2/discover \
  -H "X-API-Key: sk_your_private_key" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com",
    "mode": "sitemap",
    "max_urls": 500,
    "include_patterns": ["/blog/"],
    "exclude_patterns": ["/tag/", "/author/"],
    "respect_robots": true
  }'
```

### Python

```python
import requests

response = requests.post(
    "https://api.enconvert.com/v2/discover",
    headers={"X-API-Key": "sk_your_private_key"},
    json={
        "url": "https://example.com",
        "mode": "hybrid",
        "max_urls": 200,
        "include_patterns": [r"/docs/"],
    },
)
response.raise_for_status()
data = response.json()

print(f"found {data['total']} URLs from {data['sources']}")
for page_url in data["urls"]:
    print(page_url)
```

### Node.js

```javascript
const res = await fetch("https://api.enconvert.com/v2/discover", {
    method: "POST",
    headers: {
        "Content-Type": "application/json",
        "X-API-Key": "sk_your_private_key"
    },
    body: JSON.stringify({
        url: "https://example.com",
        mode: "hybrid",
        max_urls: 200,
        include_patterns: ["/docs/"]
    })
});

const data = await res.json();

console.log(`found ${data.total} URLs from`, data.sources);
data.urls.forEach((pageUrl) => console.log(pageUrl));
```

Uno schema comune è scoprire prima e poi renderizzare: prendi `data.urls`
e passalo all'[endpoint perceive](/it/docs/endpoints/perceive.md), con chiamate
singole per una manciata di pagine oppure il suo percorso batch per
l'intero elenco.

---

## Risposte di errore

| Stato | Condizione |
|--------|-----------|
| `400 Bad Request` | L'URL non è `http(s)`, contiene credenziali incorporate, non ha hostname, oppure si risolve in un indirizzo privato, loopback, link-local o di metadata cloud (protezione SSRF). |
| `401 Unauthorized` | Chiave API / token JWT mancante o non valido. |
| `402 Payment Required` | Discover non è abilitato sul tuo piano attuale, oppure la tua quota mensile di ops è esaurita. Ogni chiamata a `/v2/discover` addebita una op. |
| `403 Forbidden` | `/v2/discover` non è tra gli endpoint consentiti della chiave API. |
| `422 Unprocessable Entity` | Validazione della richiesta fallita: enum `mode` non valido, `max_urls` fuori da 1–1,000, `max_depth` fuori da 1–5, oltre 50 pattern include/exclude, una regex malformata, oppure un `url` oltre 2,048 caratteri. |
| `500 Internal Server Error` | La scoperta degli URL è fallita inaspettatamente. Il client riceve un messaggio generico; il dettaglio completo va solo nei log del server. |

Nota che un fetch della sitemap fallito o un errore di scansione non
producono uno stato di errore. Questi degradano a voci nell'array
`warnings`, e la richiesta restituisce comunque `200` con tutto ciò che è
stato trovato. Il riferimento completo dei codici di stato è nella
[guida ai codici di errore](/it/docs/reference/errors.md).

---

## Limiti

| Limite | Valore |
|-------|-------|
| Lunghezza URL | 2,048 caratteri |
| `max_urls` | 1–1,000 (predefinito 100) |
| `max_depth` | 1–5 (predefinito 2) |
| `include_patterns` | Massimo 50 pattern |
| `exclude_patterns` | Massimo 50 pattern |
| Pagine recuperate in modalità crawl | fino a `max_urls` (max 1,000; abbassabile con `DISCOVER_CRAWL_MAX_PAGES`) |
| Timeout richiesta | 300 secondi |
| Ops per chiamata | 1, addebitata sulla quota mensile unificata |

---

## Domande frequenti

### Come elenco tutti gli URL di un sito web con un'API?

Invia `POST /v2/discover` con l'URL del sito. La modalità predefinita
`hybrid` combina il parsing della sitemap (voci `robots.txt`,
`sitemap.xml` con ricorsione sull'indice, feed RSS/Atom) con una
scansione HTTP breadth-first, e restituisce fino a `max_urls` (1–1,000,
predefinito 100) URL deduplicati in un'unica risposta sincrona.

### L'endpoint discover usa un browser headless o renderizza JavaScript?

Per impostazione predefinita viene eseguito via HTTP e renderizza solo
quando è necessario. L'opzione `render_js` controlla questo comportamento:
nella modalità predefinita `"auto"`, discover resta solo HTTP e renderizza
una pagina nel browser solo quando la scansione HTTP restituisce uno shell
JavaScript nudo, raccogliendo i link iniettati lato client; `"always"`
forza la scansione nel browser e `"never"` la mantiene rigorosamente solo
HTTP. La modalità `sitemap` resta un'opzione veloce e senza browser per le
SPA attente alla SEO.

### Quante pagine recupera effettivamente la modalità crawl?

La modalità crawl recupera fino al tuo `max_urls` completo (fino a
1,000). Resta solo HTTP, quindi rimane veloce; gli operatori possono
abbassare il tetto con la variabile d'ambiente
`DISCOVER_CRAWL_MAX_PAGES`. Ogni pagina recuperata contribuisce inoltre
con molti link. Il campo `pages_crawled` della risposta riporta
esattamente quante GET HTTP sono state eseguite.

### Posso filtrare quali URL vengono restituiti?

Sì. `include_patterns` e `exclude_patterns` accettano fino a 50 regex
Python ciascuno (semantica `re.search`, non glob), e
`respect_robots: true` rimuove dall'elenco restituito gli URL non
consentiti dal `robots.txt` del sito.
