Discover
POST /v2/discover énumère les URL d'un site sans afficher la moindre
page. Aucun navigateur, aucune capture d'écran, aucun PDF, aucun artefact
stocké — uniquement un crawl HTTP plus l'analyse du sitemap, qui renvoie une
liste plate d'URL et un décompte de l'origine de chacune. C'est la primitive
économique « cartographier le site » : pointez-la vers un domaine, récupérez
les pages qui valent la peine d'être traitées, puis transmettez cette liste à
l'endpoint perceive ou à une tâche d'ingestion en mode crawl.
Voici l'appel utile le plus simple. Envoyez une URL, récupérez sa carte :
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 réponse est une simple liste d'URL accompagnée de compteurs de provenance — pas d'URL signées, pas d'identifiant d'opération, pas de 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": []
}
À signaler d'emblée.
/v2/discoverest un endpoint V2, conditionné par l'indicateur de plandiscover_enabled. Contrairement à la plupart des endpoints V2, il n'a aucun compteur de quota par opération — il est économique (pas de navigateur, pas de stockage), il n'y a donc aucun compteur mensuel à épuiser. Si votre plan n'inclut pas V2 discover, l'appel renvoie402. Consultez la page tarifs pour savoir quels plans le proposent.
Endpoints
| Method | Path | Purpose |
|---|---|---|
POST |
/v2/discover |
Cartographie les URL d'un site uniquement via HTTP et renvoie une liste plate avec le décompte des sources. |
/v2/discover expose un seul chemin. Il est sans état — il n'y a aucune ligne
d'opération à récupérer de nouveau ni aucune tâche à interroger, donc aucun
endpoint GET compagnon comme perceive en possède un.
Content-Type : application/json sur le POST.
Authentification
Authentifiez-vous avec une clé privée dans l'en-tête X-API-Key pour les
appels serveur à serveur. C'est la voie utilisée par les exemples ci-dessous.
X-API-Key: sk_live_your_private_key
Les clés publiques avec un jeton porteur JWT fonctionnent aussi, selon le même
flux que tout autre endpoint — générez un jeton avec votre clé pk_, puis
envoyez-le dans Authorization: Bearer <token>. Le flux complet, y compris le
verrouillage de domaine et le rafraîchissement du jeton, figure dans
le guide d'authentification.
Chaque clé API porte une liste blanche d'endpoints autorisés. Si /v2/discover
ne figure pas sur la liste de la clé, la requête est rejetée avec 403 et un
message nommant le chemin qui a été bloqué.
Fonctionnement de discover
Une requête s'exécute entièrement via HTTP. Le Chrome headless unique qui
alimente perceive n'est jamais sollicité — la voie de
crawl utilise la stratégie de crawler HTTP de Crawl4AI (un GET httpx plus une
analyse de liens lxml par page), et la voie du sitemap réutilise les mêmes
assistants purement HTTP de sitemap et de flux que le reste de la plateforme.
- Filtrer la graine. L'URL que vous envoyez est vérifiée contre le SSRF
avant toute récupération : le schéma, les identifiants embarqués, les noms
d'hôte bloqués et l'IP résolue sont tous validés. Une URL qui se résout vers
une adresse privée, de bouclage, link-local ou de métadonnées cloud est
rejetée avec
400. La graine est toujours incluse comme première entrée de sa propre carte. - Collecter depuis les sitemaps. En mode
sitemapouhybrid, discover litrobots.txt, sondesitemap.xml(en récursant dans les enfants<sitemapindex>) et récupère les pages de flux RSS/Atom. Un sitemap manquant ou cassé devient un avertissement, pas une erreur. - Crawler via HTTP. En mode
crawlouhybrid, discover exécute un crawl en largeur d'abord depuis la graine jusqu'àmax_depth, en récoltant les liens<a href>du HTML brut de chaque page récupérée. Chaque lien suivi est filtré contre le SSRF avant d'être récupéré. - Normaliser et filtrer. La liste brute combinée est canonicalisée
(fragments et paramètres de suivi supprimés, ports par défaut retirés, clés
de requête triées), puis passée à travers la vérification de même domaine, vos
motifs regex d'inclusion et d'exclusion, le filtre
robots.txt, la déduplication et, enfin, le plafondmax_urls.
Comme il n'y a aucun rendu, une application monopage rendue côté client renvoie
uniquement sa coquille HTML en mode crawl — généralement la graine plus zéro
ou une URL. C'est le comportement correct d'un endpoint sans navigateur, pas un
échec. Pour les SPA, utilisez le mode sitemap (la plupart des SPA soucieuses
du SEO publient un sitemap) ou repliez-vous sur des
appels perceive par URL.
Paramètres de requête
Cœur
| Parameter | Type | Default | Description |
|---|---|---|---|
url |
string |
— | Le site à cartographier. Doit commencer par http:// ou https://. 2 048 caractères max. Obligatoire. |
mode |
string |
"hybrid" |
sitemap, crawl ou hybrid. Voir Modes. |
max_urls |
integer |
100 |
Nombre maximal d'URL renvoyées. 1–1 000. La liste est plafonnée ici et truncated le signale si davantage existaient. |
max_depth |
integer |
2 |
Profondeur de crawl depuis la graine, en mode crawl/hybrid. 1–5. |
same_domain_only |
boolean |
true |
Ne conserve que les URL sur l'hôte de la graine. Quand false, les liens hors hôte découverts pendant le crawl sont conservés aussi. |
Filtrage
| Parameter | Type | Default | Description |
|---|---|---|---|
include_patterns |
string[] |
[] |
Liste blanche de regex Python (sémantique re.search, pas glob). Une URL doit en faire correspondre au moins un pour être conservée. Vide signifie tout autoriser. 50 motifs max. |
exclude_patterns |
string[] |
[] |
Liste noire de regex Python. Une URL correspondant à n'importe quel motif est écartée. Appliquée après include_patterns. 50 motifs max. |
respect_robots |
boolean |
false |
Quand true, les URL interdites par le robots.txt du site sont écartées de la liste renvoyée. |
Les motifs sont de véritables expressions régulières Python, compilées au moment
de la validation. Un motif mal formé est un 422 en bordure, pas un 500 en
plein crawl. Comme la sémantique est re.search, une simple sous-chaîne comme
"/blog/" correspond n'importe où dans l'URL — ancrez avec ^/$ si vous avez
besoin d'une correspondance positionnelle.
À signaler d'emblée.
respect_robotsest appliqué au moment du filtrage de sortie, pas de la récupération. Crawl4AI 0.8.9 n'a aucune barrière robots native, donc en modecrawlouhybridune page interdite peut quand même être récupérée via HTTP, puis écartée avant d'atteindre la réponse. Contrairement à perceive, oùrespect_robots=truerejette une URL interdite avec403, discover ne renvoie jamais403pour une règle robots — il écarte silencieusement l'URL et ne signale rien.
Modes
mode |
What it does |
|---|---|
sitemap |
Les entrées de sitemap du robots.txt, le sitemap.xml sondé (avec récursion d'index) et les pages de flux RSS/Atom. Instantané, sans crawl. |
crawl |
Crawl en largeur d'abord, uniquement via HTTP, depuis la graine, en récoltant les liens <a href> du balisage brut. |
hybrid (default) |
L'union dédupliquée de sitemap et crawl. |
Pour être clair : le mode crawl borne le nombre de pages qu'il récupère
réellement à min(max_urls, 50). Un max_urls de 1 000 n'émet pas mille GET
HTTP — discover cesse de récupérer à 50 pages, mais chaque page apporte de
nombreux liens <a href>, si bien que la liste renvoyée peut tout de même
atteindre max_urls. Ce plafond de 50 pages récupérées maintient la requête
synchrone bien en deçà du délai de requête de 300 secondes. pages_crawled dans
la réponse vous indique exactement combien de GET ont été exécutés.
Réponse
POST /v2/discover renvoie cet objet directement — il n'y a aucune tâche
asynchrone ni second appel.
| Field | Type | Description |
|---|---|---|
url |
string |
L'URL graine que vous avez envoyée. |
mode |
string |
Le mode qui s'est exécuté : sitemap, crawl ou hybrid. |
total |
integer |
Nombre d'URL dans urls (après déduplication, filtrage et plafond). |
urls |
string[] |
La liste d'URL dédupliquée, normalisée et plafonnée. La graine est toujours le premier candidat. |
pages_crawled |
integer |
GET HTTP émis par le crawl. 0 en mode sitemap pur. |
truncated |
boolean |
true quand davantage d'URL uniques existaient que ce qu'autorisait max_urls. |
robots_respected |
boolean |
Renvoie la valeur respect_robots que vous avez envoyée. |
sources |
object |
Décompte brut d'URL par source avant déduplication/filtrage, p. ex. {"sitemap": 42, "crawl": 30}. Les décomptes se chevauchent et leur somme dépasse total. |
warnings |
string[] |
Notes non fatales : un sitemap manquant, un crawl en échec, un robots.txt inaccessible. |
Les décomptes sources sont bruts — c'est le nombre d'URL que chaque voie a
produites avant l'exécution de la normalisation, de la vérification de même
domaine, de vos filtres et de la déduplication. Ils dépasseront couramment
total, car le mode hybrid trouve les mêmes pages à la fois depuis le sitemap
et le crawl. Utilisez-les pour voir quelle voie porte la carte, pas comme un
décompte post-filtrage.
Exemples de code
curl — carte hybride par défaut
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 seulement, pages de blog, plafonné à 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 schéma courant consiste à découvrir d'abord, puis à afficher : prenez
data.urls et transmettez-les à l'endpoint perceive —
des appels individuels pour une poignée de pages, ou sa voie par lots pour toute
la liste.
Conditionnement par plan
/v2/discover est conditionné par l'indicateur de plan discover_enabled et se
comporte différemment des endpoints V2 mesurés. Les endpoints mesurés —
perceive parmi eux — consomment chacun un compteur
mensuel (perceive_operations, lookup_queries, ingest_pages, watch_checks,
distill_operations). Discover n'a pas de tel compteur : il est uniquement HTTP,
il ne stocke rien, il n'y a donc aucun compteur par opération à épuiser.
Ce que cela signifie en pratique :
- Si votre plan porte
discover_enabled, vous pouvez l'appeler. Il n'y a aucun quota mensuel de discover à épuiser. - Si votre plan ne le porte pas, l'appel renvoie
402avec une invite de mise à niveau. - Les plans admin contournent entièrement la barrière.
Les plans qui incluent discover_enabled sont définis par palier ; les quotas
actuels figurent sur la page tarifs. Les plans de conversion V1
n'incluent pas les endpoints V2 — un appel discover ne consomme jamais de quota
de conversion V1.
Réponses d'erreur
| Status | Condition |
|---|---|
400 Bad Request |
L'URL n'est pas http(s), porte des identifiants embarqués, n'a aucun nom d'hôte ou se résout vers une adresse privée, de bouclage, link-local ou de métadonnées cloud (protection SSRF). |
401 Unauthorized |
Clé API / jeton JWT manquant ou invalide. |
402 Payment Required |
Discover n'est pas activé sur votre plan actuel. |
403 Forbidden |
/v2/discover ne figure pas dans les endpoints autorisés de la clé API. |
422 Unprocessable Entity |
La validation de la requête a échoué : enum mode incorrect, max_urls hors de 1–1 000, max_depth hors de 1–5, plus de 50 motifs d'inclusion/exclusion, une regex mal formée ou une url de plus de 2 048 caractères. |
500 Internal Server Error |
La découverte d'URL a échoué de façon inattendue. Le client reçoit un message générique ; le détail complet ne va que dans les journaux serveur. |
Notez qu'une récupération de sitemap en échec ou une faute de crawl ne produit
pas de statut d'erreur — ces cas se dégradent en entrées dans le tableau
warnings, et la requête renvoie tout de même 200 avec ce qui a été trouvé. La
référence complète des codes de statut figure dans
le guide des codes d'erreur.
Limites
| Limit | Value |
|---|---|
| Longueur d'URL | 2 048 caractères |
max_urls |
1–1 000 (par défaut 100) |
max_depth |
1–5 (par défaut 2) |
include_patterns |
50 motifs max |
exclude_patterns |
50 motifs max |
| Pages récupérées en mode crawl | 50 (min(max_urls, 50)) |
| Délai de requête | 300 secondes |
| Quota par opération | Aucun — pas de compteur mensuel de discover |