Discover

POST /v2/discover enumera gli URL di un sito senza renderizzare una singola pagina. Niente browser, niente screenshot, niente PDF, nessun artefatto archiviato — solo una scansione HTTP-only più il parsing della sitemap che restituisce un elenco piatto di URL e un conteggio di dove ciascuno è stato trovato. È la primitiva economica per "mappare il sito": puntala su un dominio, ottieni le pagine che vale la pena elaborare, poi passa quell'elenco all'endpoint perceive o a un job di ingest in crawl-mode.

Ecco la chiamata utile più piccola. Invia un URL, ottieni la sua mappa:

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

La risposta è un semplice elenco di URL con contatori di provenienza — nessun URL firmato, nessun ID operazione, nessun polling:

{
    "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": []
}

Da segnalare subito. /v2/discover è un endpoint V2, vincolato dal flag di piano discover_enabled. A differenza della maggior parte degli endpoint V2 non ha un contatore di quota per operazione — è economico (niente browser, niente storage), quindi non c'è alcun contatore mensile da esaurire. Se il tuo piano non include V2 discover, la chiamata restituisce 402. Consulta la pagina dei prezzi per sapere quali piani lo includono.


Endpoints

Method Path Purpose
POST /v2/discover Mappa gli URL di un sito solo via HTTP e restituisce un elenco piatto con i conteggi per sorgente.

/v2/discover espone un singolo path. È stateless — non c'è alcuna riga di operazione da rileggere e nessun job da interrogare, quindi non esiste un endpoint GET corrispondente come perceive ne ha uno.

Content-Type: application/json sul POST.


Authentication

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

X-API-Key: sk_live_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, è descritto ne la guida all'autenticazione.

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 path bloccato.


How discover works

Una richiesta gira interamente su HTTP. Il singleton di Chrome headless che alimenta perceive non viene mai toccato — il percorso di crawl usa la strategia HTTP crawler di Crawl4AI (un GET httpx più un parse dei link lxml per pagina), e il percorso della sitemap riutilizza gli stessi helper puramente HTTP per sitemap e feed del resto della piattaforma.

  1. Verifica il seed. L'URL che invii viene controllato per SSRF prima di qualsiasi fetch: schema, credenziali incorporate, hostname bloccati e l'IP risolto vengono tutti validati. Un URL che si risolve in un indirizzo privato, loopback, link-local o di cloud-metadata viene rifiutato con 400. Il seed è sempre incluso come prima voce nella sua stessa mappa.
  2. Raccogli dalle sitemap. In modalità sitemap o hybrid, discover legge robots.txt, sonda sitemap.xml (ricorrendo nei figli <sitemapindex>) e recupera le pagine dei feed RSS/Atom. Una sitemap mancante o danneggiata diventa un warning, non un errore.
  3. Scansiona via HTTP. In modalità crawl o hybrid, discover esegue una scansione breadth-first dal seed fino a max_depth, raccogliendo i link <a href> dall'HTML grezzo di ogni pagina recuperata. Ogni link seguito viene verificato per SSRF prima di essere recuperato.
  4. Normalizza e filtra. L'elenco grezzo combinato viene canonicalizzato (frammenti e parametri di tracking rimossi, porte di default rimosse, chiavi di query ordinate), poi passato attraverso il controllo stesso-dominio, i tuoi pattern regex di include ed exclude, il filtro robots.txt, la de-duplicazione e infine il limite max_urls.

Poiché non c'è alcun render, una single-page app renderizzata lato client restituisce solo il suo guscio HTML in modalità crawl — tipicamente il seed più zero o un URL. È il comportamento corretto per un endpoint senza browser, non un fallimento. Per le SPA, usa la modalità sitemap (la maggior parte delle SPA attente alla SEO pubblica una sitemap) oppure ripiega su chiamate perceive per singolo URL.


Request parameters

Core

Parameter Type Default Description
url string Il sito da mappare. Deve iniziare con http:// o https://. Massimo 2.048 caratteri. Obbligatorio.
mode string "hybrid" sitemap, crawl o hybrid. Vedi 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.

Filtering

Parameter Type Default Description
include_patterns string[] [] Allowlist di regex Python (semantica re.search, non glob). Un URL deve corrispondere ad almeno uno 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. Applicato dopo include_patterns. Massimo 50 pattern.
respect_robots boolean false Quando è true, gli URL non consentiti dal robots.txt del sito vengono scartati dall'elenco restituito.

I pattern sono vere espressioni regolari Python, compilate al momento della validazione. Un pattern malformato è un 422 al confine, non un 500 a metà scansione. Poiché la semantica è re.search, una semplice sottostringa come "/blog/" corrisponde in qualsiasi punto dell'URL — ancora con ^/$ se ti serve una corrispondenza posizionale.

Da segnalare subito. respect_robots viene applicato al momento del filtro di 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 che raggiunga la risposta. A differenza di perceive, 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 segnala nulla.

Modes

mode What it does
sitemap Voci sitemap del robots.txt, sitemap.xml sondato (con ricorsione sull'indice) e pagine dei feed RSS/Atom. Istantaneo, nessuna scansione.
crawl Scansione breadth-first solo via HTTP dal seed, raccogliendo i link <a href> dal markup grezzo.
hybrid (default) L'unione de-duplicata di sitemap e crawl.

Per essere chiari: la modalità crawl limita il numero di pagine che recupera effettivamente a min(max_urls, 50). Un max_urls di 1.000 non emette mille GET HTTP — discover smette di recuperare a 50 pagine, ma ogni pagina contribuisce con molti link <a href>, quindi l'elenco restituito può comunque raggiungere max_urls. Il tetto di 50 pagine recuperate mantiene la richiesta sincrona ben al di sotto del timeout di richiesta di 300 secondi. pages_crawled nella risposta ti dice esattamente quanti GET sono stati eseguiti.


Response

POST /v2/discover restituisce questo oggetto direttamente — non c'è alcun job asincrono né una seconda chiamata.

Field Type Description
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, filtraggio e limite).
urls string[] L'elenco di URL de-duplicato, normalizzato e limitato. Il seed è sempre il primo candidato.
pages_crawled integer GET HTTP emessi dalla scansione. 0 in modalità sitemap pura.
truncated boolean true quando esistevano più URL univoci di quanti max_urls ne consentisse.
robots_respected boolean Restituisce il valore respect_robots che hai inviato.
sources object Conteggio grezzo di URL per sorgente prima di dedup/filtro, es. {"sitemap": 42, "crawl": 30}. 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 sources sono grezzi — sono il numero di URL prodotti da ciascun percorso prima che venissero eseguiti normalizzazione, controllo stesso-dominio, i tuoi filtri e la de-duplicazione. Sommeranno abitualmente a un valore maggiore di total, perché la modalità hybrid trova le stesse pagine sia dalla sitemap sia dalla scansione. Usali per vedere quale percorso sta sostenendo la mappa, non come conteggio post-filtro.


Code examples

curl — default hybrid map

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

curl — sitemap only, blog pages, capped at 500

curl -X POST https://api.enconvert.com/v2/discover \
  -H "X-API-Key: sk_live_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

import requests

response = requests.post(
    "https://api.enconvert.com/v2/discover",
    headers={"X-API-Key": "sk_live_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

const res = await fetch("https://api.enconvert.com/v2/discover", {
    method: "POST",
    headers: {
        "Content-Type": "application/json",
        "X-API-Key": "sk_live_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));

Un pattern comune è prima fare discover, poi renderizzare: prendi data.urls e passali all'endpoint perceive — chiamate singole per una manciata di pagine, oppure il suo percorso batch per l'intero elenco.


Plan gating

/v2/discover è vincolato dal flag di piano discover_enabled e si comporta diversamente dagli endpoint V2 misurati. Quelli misurati — perceive tra essi — consumano ciascuno un contatore mensile (perceive_operations, lookup_queries, ingest_pages, watch_checks, distill_operations). Discover non ha un contatore del genere: è solo HTTP, non archivia nulla, quindi non c'è alcun contatore per operazione da esaurire.

Cosa significa in pratica:

  • Se il tuo piano include discover_enabled, puoi chiamarlo. Non c'è alcuna quota mensile di discover che possa esaurirsi.
  • Se non lo include, la chiamata restituisce 402 con un invito all'upgrade.
  • I piani Admin bypassano del tutto il gate.

Quali piani includono discover_enabled è impostato per tier; le quote correnti sono su la pagina dei prezzi. I piani di conversione V1 non includono gli endpoint V2 — una chiamata discover non consuma mai la quota di conversione V1.


Error responses

Status Condition
400 Bad Request L'URL non è http(s), porta credenziali incorporate, non ha hostname o si risolve in un indirizzo privato, loopback, link-local o di cloud-metadata (protezione SSRF).
401 Unauthorized Chiave API / token JWT mancante o non valido.
402 Payment Required Discover non è abilitato sul tuo piano attuale.
403 Forbidden /v2/discover non è tra gli endpoint consentiti della chiave API.
422 Unprocessable Entity Validazione della richiesta fallita: enum mode errato, max_urls fuori da 1–1.000, max_depth fuori da 1–5, più di 50 pattern di include/exclude, una regex malformata o un url oltre i 2.048 caratteri.
500 Internal Server Error La discovery degli URL è fallita inaspettatamente. Il client riceve un messaggio generico; il dettaglio completo va solo nei log del server.

Nota che un fetch di sitemap fallito o un guasto nella scansione non producono uno status di errore — questi degradano in voci nell'array warnings, e la richiesta restituisce comunque 200 con tutto ciò che è stato trovato. Il riferimento completo dei codici di stato è ne la guida ai codici di errore.


Limits

Limit Value
URL length 2.048 caratteri
max_urls 1–1.000 (default 100)
max_depth 1–5 (default 2)
include_patterns 50 pattern max
exclude_patterns 50 pattern max
Pages fetched in crawl mode 50 (min(max_urls, 50))
Request timeout 300 secondi
Per-operation quota Nessuna — nessun contatore mensile di discover