Discover

POST /v2/discover ermittelt die URLs einer Website, ohne eine einzige Seite zu rendern. Kein Browser, kein Screenshot, kein PDF, kein gespeichertes Artefakt — nur ein reiner HTTP-Crawl plus Sitemap-Parsing, das eine flache Liste von URLs zurückgibt sowie eine Zählung, woher jede einzelne stammt. Es ist das günstige "Site mappen"-Primitiv: Richte es auf eine Domain, erhalte die verarbeitenswerten Seiten zurück und gib diese Liste dann an den perceive-Endpunkt oder einen Ingest-Job im Crawl-Modus weiter.

Hier ist der kleinste nützliche Aufruf. Sende eine URL, erhalte die Map zurück:

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"
  }'

Die Antwort ist eine schlichte URL-Liste mit Herkunfts-Zählern — keine signierten URLs, keine Operations-ID, kein 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": []
}

Vorab erwähnenswert. /v2/discover ist ein V2-Endpunkt, geschützt durch das Plan-Flag discover_enabled. Anders als die meisten V2-Endpunkte hat er keinen Quota-Zähler pro Operation — er ist günstig (kein Browser, kein Speicher), daher gibt es kein monatliches Kontingent, das aufgebraucht werden könnte. Wenn dein Plan V2 discover nicht enthält, gibt der Aufruf 402 zurück. Siehe die Preisseite dazu, welche Pläne ihn umfassen.


Endpoints

Method Path Purpose
POST /v2/discover Mappt die URLs einer Website ausschließlich über HTTP und gibt eine flache Liste mit Quellenzählungen zurück.

/v2/discover stellt einen einzigen Pfad bereit. Er ist zustandslos — es gibt keine Operations-Zeile zum erneuten Abrufen und keinen Job zum Pollen, also gibt es auch keinen begleitenden GET-Endpunkt, wie perceive ihn hat.

Content-Type: application/json beim POST.


Authentication

Authentifiziere dich mit einem privaten Schlüssel im X-API-Key-Header für Server-zu-Server-Aufrufe. Das ist der Weg, den die Beispiele unten verwenden.

X-API-Key: sk_live_your_private_key

Öffentliche Schlüssel mit einem JWT-Bearer-Token funktionieren ebenfalls, über denselben Ablauf wie bei jedem anderen Endpunkt — generiere ein Token mit deinem pk_-Schlüssel und sende es dann als Authorization: Bearer <token>. Der vollständige Ablauf, einschließlich Domain-Sperre und Token-Refresh, findet sich in der Authentifizierungsanleitung.

Jeder API-Schlüssel trägt eine Allowlist erlaubter Endpunkte. Wenn /v2/discover nicht auf der Liste des Schlüssels steht, wird die Anfrage mit 403 abgelehnt, samt einer Meldung, die den blockierten Pfad nennt.


How discover works

Eine Anfrage läuft vollständig über HTTP. Das Singleton-Headless-Chrome, das perceive antreibt, wird nie berührt — der Crawl-Pfad nutzt die HTTP-Crawler-Strategie von Crawl4AI (ein httpx GET plus ein lxml-Link-Parse pro Seite), und der Sitemap-Pfad verwendet dieselben reinen HTTP-Sitemap- und Feed-Helfer wie der Rest der Plattform.

  1. Den Seed prüfen. Die URL, die du sendest, wird vor jedem Abruf auf SSRF geprüft: Schema, eingebettete Zugangsdaten, gesperrte Hostnamen und die aufgelöste IP werden allesamt validiert. Eine URL, die auf eine private, Loopback-, Link-Local- oder Cloud-Metadata-Adresse auflöst, wird mit 400 abgelehnt. Der Seed ist stets der erste Eintrag in seiner eigenen Map.
  2. Aus Sitemaps sammeln. Im sitemap- oder hybrid-Modus liest discover robots.txt, prüft sitemap.xml (mit Rekursion in <sitemapindex>-Kinder) und zieht RSS/Atom-Feed-Seiten heran. Eine fehlende oder defekte Sitemap wird zu einer Warnung, nicht zu einem Fehler.
  3. Über HTTP crawlen. Im crawl- oder hybrid-Modus führt discover einen Breitensuche-Crawl vom Seed aus bis zu max_depth durch und liest die <a href>-Links aus dem rohen HTML jeder abgerufenen Seite. Jeder verfolgte Link wird vor dem Abruf auf SSRF geprüft.
  4. Normalisieren und filtern. Die kombinierte Rohliste wird kanonisiert (Fragmente und Tracking-Parameter entfernt, Standard-Ports entfernt, Query-Keys sortiert), dann durch die Same-Domain-Prüfung, deine include- und exclude-Regex-Muster, den robots.txt-Filter, die Deduplizierung und schließlich das max_urls-Limit geführt.

Da es kein Rendering gibt, gibt eine clientseitig gerenderte Single-Page-App im crawl-Modus nur ihre HTML-Hülle zurück — typischerweise der Seed plus null oder eine URL. Das ist korrektes Verhalten für einen Endpunkt ohne Browser, kein Fehler. Verwende für SPAs den sitemap-Modus (die meisten SEO-bewussten SPAs veröffentlichen eine Sitemap) oder weiche auf einzelne perceive-Aufrufe pro URL aus.


Request parameters

Core

Parameter Type Default Description
url string Die zu mappende Website. Muss mit http:// oder https:// beginnen. Maximal 2.048 Zeichen. Erforderlich.
mode string "hybrid" sitemap, crawl oder hybrid. Siehe Modi.
max_urls integer 100 Maximale Anzahl zurückgegebener URLs. 1–1.000. Die Liste wird hier gekappt, und truncated wird gesetzt, falls mehr vorhanden waren.
max_depth integer 2 Crawl-Tiefe vom Seed aus, im crawl/hybrid-Modus. 1–5.
same_domain_only boolean true Behält nur URLs auf dem Host des Seeds. Bei false werden auch beim Crawl entdeckte Off-Host-Links behalten.

Filtering

Parameter Type Default Description
include_patterns string[] [] Python-Regex-Allowlist (re.search-Semantik, kein Glob). Eine URL muss mindestens einem Muster entsprechen, um behalten zu werden. Leer bedeutet alle erlauben. Maximal 50 Muster.
exclude_patterns string[] [] Python-Regex-Denylist. Eine URL, die einem beliebigen Muster entspricht, wird verworfen. Wird nach include_patterns angewendet. Maximal 50 Muster.
respect_robots boolean false Bei true werden URLs, die durch die robots.txt der Site verboten sind, aus der zurückgegebenen Liste entfernt.

Die Muster sind echte reguläre Python-Ausdrücke, kompiliert zur Validierungszeit. Ein fehlerhaftes Muster ergibt ein 422 am Rand, kein 500 mitten im Crawl. Da die Semantik re.search ist, passt eine bloße Teilzeichenkette wie "/blog/" überall in der URL — verankere mit ^/$, wenn du eine positionsgebundene Übereinstimmung brauchst.

Vorab erwähnenswert. respect_robots wird zur Ausgabe-Filterzeit durchgesetzt, nicht zur Abrufzeit. Crawl4AI 0.8.9 hat kein natives robots-Gate, sodass im crawl- oder hybrid-Modus eine verbotene Seite dennoch über HTTP abgerufen und dann verworfen werden kann, bevor sie die Antwort erreicht. Anders als bei perceive, wo respect_robots=true eine verbotene URL mit 403 ablehnt, gibt discover niemals 403 für eine robots-Regel zurück — es verwirft die URL still und meldet nichts.

Modes

mode What it does
sitemap robots.txt-Sitemap-Einträge, geprüfte sitemap.xml (mit Index-Rekursion) und RSS/Atom-Feed-Seiten. Sofort, kein Crawl.
crawl Breitensuche-Crawl ausschließlich über HTTP vom Seed aus, der <a href>-Links aus dem rohen Markup liest.
hybrid (default) Die deduplizierte Vereinigung von sitemap und crawl.

Um es klar zu sagen: Der crawl-Modus begrenzt die Anzahl der Seiten, die er tatsächlich abruft, auf min(max_urls, 50). Ein max_urls von 1.000 löst keine tausend HTTP-GETs aus — discover hört bei 50 Seiten auf abzurufen, doch jede Seite steuert viele <a href>-Links bei, sodass die zurückgegebene Liste dennoch max_urls erreichen kann. Die Abruf-Obergrenze von 50 Seiten hält die synchrone Anfrage deutlich unter dem Anfrage-Timeout von 300 Sekunden. pages_crawled in der Antwort sagt dir genau, wie viele GETs liefen.


Response

POST /v2/discover gibt dieses Objekt direkt zurück — es gibt keinen asynchronen Job und keinen zweiten Aufruf.

Field Type Description
url string Die Seed-URL, die du gesendet hast.
mode string Der ausgeführte Modus: sitemap, crawl oder hybrid.
total integer Anzahl der URLs in urls (nach Dedup, Filterung und Limit).
urls string[] Die deduplizierte, normalisierte, gekappte URL-Liste. Der Seed ist stets der erste Kandidat.
pages_crawled integer Vom Crawl ausgelöste HTTP-GETs. 0 im reinen sitemap-Modus.
truncated boolean true, wenn mehr eindeutige URLs vorhanden waren, als max_urls zuließ.
robots_respected boolean Gibt den von dir gesendeten respect_robots-Wert zurück.
sources object Rohe URL-Zählung pro Quelle vor Dedup/Filter, z. B. {"sitemap": 42, "crawl": 30}. Die Zählungen überschneiden sich und summieren sich über total.
warnings string[] Nicht-fatale Hinweise: eine fehlende Sitemap, ein fehlgeschlagener Crawl, eine nicht erreichbare robots.txt.

Die sources-Zählungen sind roh — sie sind die Anzahl der URLs, die jeder Pfad produziert hat, bevor Normalisierung, die Same-Domain-Prüfung, deine Filter und die Deduplizierung liefen. Sie werden sich regelmäßig zu mehr als total summieren, weil der hybrid-Modus dieselben Seiten sowohl aus der Sitemap als auch aus dem Crawl findet. Nutze sie, um zu sehen, welcher Pfad die Map trägt, nicht als Zählung nach der Filterung.


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));

Ein gängiges Muster ist, zuerst zu discovern und dann zu rendern: Nimm data.urls und gib sie an den perceive-Endpunkt weiter — einzelne Aufrufe für eine Handvoll Seiten oder seinen Batch-Pfad für die gesamte Liste.


Plan gating

/v2/discover ist durch das Plan-Flag discover_enabled geschützt und verhält sich anders als die gezählten V2-Endpunkte. Die gezählten — darunter perceive — verbrauchen jeweils einen monatlichen Zähler (perceive_operations, lookup_queries, ingest_pages, watch_checks, distill_operations). Discover hat keinen solchen Zähler: Es läuft nur über HTTP, es speichert nichts, also gibt es kein Kontingent pro Operation, das aufgebraucht werden könnte.

Was das in der Praxis bedeutet:

  • Wenn dein Plan discover_enabled trägt, kannst du es aufrufen. Es gibt kein monatliches discover-Kontingent, das ausgehen könnte.
  • Wenn nicht, gibt der Aufruf 402 mit einer Upgrade-Aufforderung zurück.
  • Admin-Pläne umgehen das Gate vollständig.

Welche Pläne discover_enabled enthalten, wird pro Stufe festgelegt; die aktuellen Kontingente stehen auf der Preisseite. V1-Conversion-Pläne enthalten keine V2-Endpunkte — ein discover-Aufruf verbraucht niemals V1-Conversion-Quota.


Error responses

Status Condition
400 Bad Request URL ist nicht http(s), trägt eingebettete Zugangsdaten, hat keinen Hostnamen oder löst auf eine private, Loopback-, Link-Local- oder Cloud-Metadata-Adresse auf (SSRF-Schutz).
401 Unauthorized Fehlender oder ungültiger API-Schlüssel / JWT-Token.
402 Payment Required Discover ist in deinem aktuellen Plan nicht aktiviert.
403 Forbidden /v2/discover steht nicht unter den erlaubten Endpunkten des API-Schlüssels.
422 Unprocessable Entity Anfrage-Validierung fehlgeschlagen: ungültiges mode-Enum, max_urls außerhalb 1–1.000, max_depth außerhalb 1–5, mehr als 50 include/exclude-Muster, ein fehlerhaftes Regex oder eine url über 2.048 Zeichen.
500 Internal Server Error URL-Discovery unerwartet fehlgeschlagen. Der Client erhält eine generische Meldung; das vollständige Detail geht nur in die Server-Logs.

Beachte, dass ein fehlgeschlagener Sitemap-Abruf oder ein Crawl-Fehler keinen Fehlerstatus erzeugt — diese werden zu Einträgen im warnings-Array herabgestuft, und die Anfrage gibt weiterhin 200 mit dem zurück, was gefunden wurde. Die vollständige Status-Code-Referenz steht in der Anleitung zu Fehlercodes.


Limits

Limit Value
URL-Länge 2.048 Zeichen
max_urls 1–1.000 (Standard 100)
max_depth 1–5 (Standard 2)
include_patterns maximal 50 Muster
exclude_patterns maximal 50 Muster
Im crawl-Modus abgerufene Seiten 50 (min(max_urls, 50))
Anfrage-Timeout 300 Sekunden
Quota pro Operation Keine — kein monatlicher discover-Zähler