Perceive
POST /v2/perceive rendert eine URL einmal in einem echten Browser und
liefert jede Ausgabe zurück, die du aus diesem einen Render anforderst:
Markdown, bereinigtes oder rohes HTML, einen Screenshot, ein PDF, das
Link- und Bildverzeichnis sowie strukturierte Daten (Seitenmetadaten,
JSON-LD, Überschriften, Tabellen). Es ersetzt einen Stapel separater
Aufrufe — url-to-markdown, url-to-screenshot, url-to-pdf, plus dein
eigenes Scraping — durch eine einzige Anfrage.
Hier ist der kleinste nützliche Aufruf. Sende eine URL, erhalte sauberes Markdown und die strukturierten Metadaten der Seite zurück:
curl -X POST https://api.enconvert.com/v2/perceive \
-H "X-API-Key: sk_live_your_private_key" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com/pricing",
"outputs": ["markdown", "structured"]
}'
Die Antwort enthält eine vorsignierte Download-URL für die Markdown-Datei und den strukturierten Block inline:
{
"operation_id": "per_3f9a2c1b8e7d4a6f90b1c2d3e4f5a6b7",
"status": "completed",
"url": "https://example.com/pricing",
"url_final": "https://example.com/pricing",
"content_hash": "9f2b8c1a...d4e5",
"render_quality": 0.93,
"cache_hit": false,
"outputs": {
"markdown": {
"url": "https://spaces.example.com/...signed...",
"object_key": "env/files/4127/v2-perceive/per_3f9a..._markdown.md",
"size_bytes": 8421,
"content_type": "text/markdown; charset=utf-8",
"expires_in": 900
}
},
"structured": {
"metadata": {
"title": "Pricing",
"description": "Simple, usage-based pricing."
},
"structured_data": [
{"@type": "Product", "name": "Pro", "offers": {"price": "99"}}
]
},
"extraction_tier": "heuristic",
"tokens": {"input": 0, "output": 0},
"cost_cents": 0.0,
"duration_ms": 6230,
"warnings": []
}
Gleich vorweg erwähnenswert.
/v2/perceiveist ein V2-Endpunkt mit eigenem Kontingentzähler. V1-Konvertierungspläne enthalten keine V2-perceive-Operationen — du brauchst einen V2-inklusiven Plan, um ihn aufzurufen. Siehe die Preisseite für die Kontingente pro Stufe.
Endpunkte
| Method | Path | Zweck |
|---|---|---|
POST |
/v2/perceive |
Eine einzelne URL perceiven und die angeforderten Ausgaben zurückgeben. |
GET |
/v2/perceive/{operation_id} |
Eine vergangene Operation mit frisch signierten Download-URLs erneut abrufen. |
POST |
/v2/perceive/batch |
Bis zu 1.000 URLs perceiven, die einen gemeinsamen Satz von Optionen teilen. |
GET |
/v2/perceive/batch/{job_id} |
Status und Ergebnisse pro URL eines Batches abfragen. |
Content-Type: application/json bei jedem POST.
Authentifizierung
Authentifiziere dich mit einem privaten Schlüssel im X-API-Key-Header
für Server-zu-Server-Aufrufe. Diesen Weg nutzen die Beispiele unten.
X-API-Key: sk_live_your_private_key
Öffentliche Schlüssel mit einem JWT-Bearer-Token funktionieren ebenfalls,
über denselben Ablauf wie jeder andere Endpunkt — erzeuge ein Token mit
deinem pk_-Schlüssel und sende es dann als Authorization: Bearer
<token>. Der vollständige Ablauf, inklusive Domain-Sperre und
Token-Aktualisierung, steht in der Authentifizierungs-Anleitung.
Jeder API-Schlüssel trägt eine Allowlist erlaubter Endpunkte. Wenn
/v2/perceive nicht auf der Liste des Schlüssels steht, wird die Anfrage
mit 403 abgelehnt.
Wie perceive funktioniert
Eine Anfrage löst einen Browser-Render über einen gemeinsam genutzten Headless-Chrome-Singleton aus und materialisiert dann jede Ausgabe aus diesem Render. Du zahlst nie zweimal für dieselbe Seite in einem einzigen Aufruf.
- Render. Die Seite lädt in Headless Chrome. Cookie-Banner werden weggeklickt, die Seite wird gescrollt, um lazy-geladene Inhalte auszulösen, Sticky-Header werden behandelt und Bilder bekommen Zeit zum Laden — dieselbe Capture-Pipeline, die den url-to-pdf-Endpunkt antreibt.
- Materialisieren. Aus dem gerenderten DOM baut perceive, was auch
immer du in
outputsaufgelistet hast: Markdown, bereinigtes/rohes HTML, Links, Bilder, einen Screenshot, ein PDF. - Extrahieren. Wenn du
structured-Ausgabe angefordert hast, führt perceive einen heuristischen Durchlauf für Seitenmetadaten, JSON-LD, Überschriften und Tabellen durch. Wenn du außerdem einschemasendest und dein Plan die LLM-Stufe trägt, füllt ein Anthropic Claude Haiku-Modell das Schema, wenn der heuristische Durchlauf zu kurz greift. - Bewerten. Ein Render-Qualitätswert (0.0–1.0) markiert Seiten, die durch Anti-Bot-Schutz blockiert oder hinter einer Login-Wand verborgen wirken, sodass du einen echten Render von einer Challenge-Seite unterscheiden kannst.
Binär- und Textausgaben (Markdown, HTML, Screenshots, PDFs, das Link- und
Bild-JSON) werden in den Speicher hochgeladen und als vorsignierte
URLs zurückgegeben, die nach 15 Minuten ablaufen. Der
structured-Block wird inline im JSON zurückgegeben. Rufe jede Operation
mit GET /v2/perceive/{operation_id} erneut ab, um einen frischen Satz
signierter URLs zu erhalten.
Anfrageparameter
Kern
| Parameter | Type | Default | Beschreibung |
|---|---|---|---|
url |
string |
— | Die zu perceivende Seite. Muss mit http:// oder https:// beginnen. Maximal 2.048 Zeichen. Erforderlich. |
outputs |
string[] |
["markdown", "structured"] |
Welche Ausgaben erzeugt werden. Siehe Outputs. |
extract |
string[] |
[] |
Welche strukturierten Felder gezogen werden, wenn structured in outputs steht. Siehe Structured extraction. |
schema |
object |
null |
Ein JSON-Schema, das die Felder beschreibt, die du extrahieren möchtest. Löst die LLM-Extraktionsstufe auf Plänen aus, die sie enthalten. |
cache_mode |
string |
"enabled" |
enabled, bypass oder refresh. Siehe Caching. |
Outputs
outputs akzeptiert jede Kombination dieser Namen:
| Output | Zurückgegeben als | Was du bekommst |
|---|---|---|
markdown |
signierte URL | Sauberes Markdown aus dem Hauptinhalt, Links und Bilder zu absoluten URLs aufgelöst. |
markdown_fit |
signierte URL | "Fit"-Markdown — eine beschnittene, dichtere Variante, abgestimmt auf LLM-Kontextfenster. |
html_cleaned |
signierte URL | Das gerenderte HTML mit entfernten Skripten, Styles und Boilerplate. |
html_raw |
signierte URL | Das vollständige gerenderte HTML genau so, wie der Browser es erzeugt hat. |
screenshot |
signierte URL | Ein Viewport-PNG in der angeforderten (oder Standard-)Viewport-Größe. |
screenshot_full_page |
signierte URL | Ein Vollseiten-PNG, das die gesamte Scrollhöhe erfasst. |
pdf |
signierte URL | Ein PDF der Seite. Akzeptiert die vollständige pdf_options-Fläche (siehe unten). |
links |
signierte URL | Ein JSON-Array jedes gefundenen Links, mit absoluten URLs und Ankertext. |
images |
signierte URL | Ein JSON-Array jedes Bildes, mit absolutem src und alt-Text. |
structured |
inline JSON | Strukturierte Daten, die aus der Seite extrahiert wurden (das structured-Antwortfeld). |
Rendern und Warten
| Parameter | Type | Default | Beschreibung |
|---|---|---|---|
viewport |
object |
1920 x 1080 |
{"width": <int>, "height": <int>}. Breite 320–3840, Höhe 240–2160. |
mobile |
boolean |
false |
In einem mobilen Viewport rendern (390 x 844), sofern viewport nicht explizit gesetzt ist. |
wait_for |
string |
null |
Nach der Navigation auf einen CSS-Selektor (".price" oder "css:.price") oder einen JS-Ausdruck ("js:window.dataReady === true") warten. |
wait_timeout_ms |
integer |
30000 |
Wie lange wait_for warten darf, in Millisekunden. 0–60.000. Ein Timeout wird zu einer Warnung herabgestuft; die Seite wird so erfasst, wie sie ist. |
js_code |
string |
null |
JavaScript, das nach der Navigation auf der Seite ausgeführt wird. Maximal 20.000 Zeichen. Ein Fehler wird zu einer Warnung, nicht zu einem Fehlschlag. |
block_resources |
string[] |
[] |
Ressourcentypen, die vor dem Laden abgebrochen werden. Beliebige von image, media, font, stylesheet, script, xhr, fetch, websocket, manifest, other. Nützlich für schnellere, reine Text-Renders. |
respect_robots |
boolean |
false |
Wenn true, wird eine durch die robots.txt der Seite verbotene URL mit 403 abgelehnt. |
pdf_options |
object |
null |
Seitenformat, Ränder, Kopf- und Fußzeilen, Skalierung und Ausrichtung für pdf-Ausgabe. Dasselbe Objekt wie url-to-pdf. Ohne pdf_options erzeugt perceive eine einzige durchgehende Seite, byteidentisch zu V1 url-to-pdf. |
Authentifizierte und benutzerdefinierte Anfragen
| Parameter | Type | Default | Beschreibung |
|---|---|---|---|
auth |
object |
null |
HTTP Basic Auth für die Zielseite: {"username": "...", "password": "..."}. |
cookies |
array |
null |
Cookies, die vor der Navigation eingespeist werden. Maximal 50. Jedes braucht name, value und entweder domain oder url. |
headers |
object |
null |
Benutzerdefinierte Anfrage-Header. Maximal 20. Blockierte Namen: host, content-length, transfer-encoding, connection, upgrade, te, trailer. |
proxy_url (Business+),
geolocation und action_chain werden vom
Anfrageschema akzeptiert, geben heute aber 422 zurück. Sie
kommen in einer späteren Version; sie jetzt zu senden sagt dir genau,
welcher Regler noch nicht bereit ist, statt ihn stillschweigend zu
ignorieren.
Structured extraction
Wenn structured in outputs steht, steuert die extract-Liste, welche
Felder perceive zieht. Fordere nichts an, und es fällt auf metadata und
structured_data zurück.
extract-Wert |
Feld in structured |
Status |
|---|---|---|
metadata |
metadata |
Live |
structured_data |
structured_data (JSON-LD) |
Live |
headings |
headings |
Live |
tables |
tables |
Live |
main_content |
main_content (Text, gedeckelt bei 50.000 Zeichen) |
Live |
all |
erweitert sich zu jedem Live-Feld oben | Live |
prices |
— | Noch nicht — gibt eine Warnung zurück, ausgelassen |
contacts |
— | Noch nicht — gibt eine Warnung zurück, ausgelassen |
technologies |
— | Noch nicht — gibt eine Warnung zurück, ausgelassen |
Um es klar zu sagen: prices, contacts und technologies sind
reservierte Namen. Fordere heute einen davon an, und es gibt keinen
Fehler — er landet im warnings-Array und wird aus structured
entfernt.
Schema-gesteuerte Extraktion
Sende ein schema, um bestimmte Felder in structured.extracted zu
ziehen:
{
"url": "https://example.com/product/widget",
"outputs": ["markdown", "structured"],
"schema": {
"type": "object",
"properties": {
"name": {"type": "string"},
"price": {"type": "number"},
"in_stock": {"type": "boolean"}
}
}
}
Die LLM-Extraktionsstufe (Anthropic Claude Haiku) füllt das Schema und
greift nur, wenn alle dieser Bedingungen zutreffen: du hast ein
schema gesendet, dein Plan trägt die LLM-Stufe (Starter und aufwärts),
die Seite wurde nicht als blockiert bewertet, und der heuristische
Durchlauf ließ Schemafelder leer. Wenn sie läuft, ist extraction_tier
gleich "llm", und tokens und cost_cents melden, was diese
Extraktion gekostet hat; andernfalls ist extraction_tier gleich
"heuristic" und beide sind null.
Hinweis. Schema-Extraktion ist fest gedeckelt, um deine Rechnung zu schützen: eine einzelne Extraktion ist pro Anfrage gedeckelt, und die tägliche Ausgabe pro Projekt ist durch den Plan gedeckelt. Wird ein Deckel erreicht, gibt perceive das heuristische Ergebnis mit einem Hinweis in
warningszurück, statt zu überzahlen. Auf einem Plan ohne die LLM-Stufe bekommst du nur heuristischestructured-Daten.
Antwort
Sowohl POST /v2/perceive als auch GET /v2/perceive/{operation_id}
geben dasselbe Objekt zurück.
| Field | Type | Beschreibung |
|---|---|---|
operation_id |
string |
Opake ID (per_...). Verwende sie mit dem GET-Endpunkt und nenne sie dem Support. |
status |
string |
queued, processing, completed oder failed. |
url |
string |
Die URL, die du gesendet hast. |
url_final |
string |
Die URL nach Weiterleitungen. |
content_hash |
string |
SHA-256 der gerenderten Seite. Treibt den 1-Stunden-Cache an. |
render_quality |
number |
0.0–1.0. Niedrige Werte markieren Anti-Bot-Challenges oder Login-Wände. |
cache_hit |
boolean |
true, wenn das Ergebnis aus dem Cache statt aus einem frischen Render kam. |
outputs |
object |
Zuordnung von Ausgabename zu {url, object_key, size_bytes, content_type, expires_in}. Signierte URLs laufen in 900 Sekunden ab. |
structured |
object |
Inline strukturierte Daten, vorhanden, wenn structured angefordert wurde. |
extraction_tier |
string |
heuristic, css oder llm. |
tokens |
object |
{input, output} verwendete LLM-Token. Null, sofern die LLM-Stufe nicht lief. |
cost_cents |
number |
LLM-Kosten in Cent für diese Operation. Null, sofern die LLM-Stufe nicht lief. |
duration_ms |
integer |
End-to-End-Render-Zeit. |
error |
string |
Nur gesetzt, wenn status gleich failed ist. |
warnings |
string[] |
Nicht-fatale Hinweise: ein wait_for-Timeout, ein übersprungenes Extract, ein Blocked-Page-Flag. |
Eine Operation abrufen
Signierte URLs laufen nach 15 Minuten ab. Um eine Ausgabe später herunterzuladen, rufe die Operation erneut ab — perceive signiert jede URL aus den gespeicherten Object Keys neu. Es findet kein erneuter Render statt, also verbraucht dies kein Kontingent.
curl https://api.enconvert.com/v2/perceive/per_3f9a2c1b8e7d4a6f90b1c2d3e4f5a6b7 \
-H "X-API-Key: sk_live_your_private_key"
Eine unbekannte Operations-ID oder eine, die zu einem anderen Projekt
gehört, gibt 404 zurück — Existenz wird nie projektübergreifend
preisgegeben.
Batch-Perception
POST /v2/perceive/batch perceived eine Liste von URLs, die einen
gemeinsamen options-Block teilen. Jede URL wird durch dieselbe Pipeline
wie ein Einzelaufruf gerendert und erzeugt ihre eigene Operationszeile.
curl -X POST https://api.enconvert.com/v2/perceive/batch \
-H "X-API-Key: sk_live_your_private_key" \
-H "Content-Type: application/json" \
-d '{
"urls": [
"https://example.com/a",
"https://example.com/b",
"https://example.com/c"
],
"options": {"outputs": ["markdown"]},
"output_mode": "manifest"
}'
Batches mit 10 URLs oder weniger laufen inline und antworten mit 200,
wobei jedes Ergebnis befüllt ist. Größere Batches antworten mit 202 und
einer job_id; die URLs werden eine nach der anderen abgearbeitet, und du
fragst die Ergebnisse ab:
curl https://api.enconvert.com/v2/perceive/batch/{job_id} \
-H "X-API-Key: sk_live_your_private_key"
Die Batch-Antwort meldet den aggregierten Fortschritt und trägt ein vollständiges perceive-Ergebnis pro URL, sobald gerendert:
{
"job_id": "bat_8c1a...",
"status": "partial",
"output_mode": "manifest",
"total": 3,
"completed": 2,
"failed": 1,
"pending": 0,
"items": [
{"operation_id": "per_...", "status": "completed", "url": "https://example.com/a"},
{"operation_id": "per_...", "status": "completed", "url": "https://example.com/b"},
{"operation_id": "per_...", "status": "failed", "url": "https://example.com/c"}
]
}
status ist queued, processing, completed, failed oder partial
(einige URLs erfolgreich, einige fehlgeschlagen). Setze output_mode auf
zip, um jedes Artefakt in ein einziges ZIP zu bündeln, das unter dem
zip-Feld zurückgegeben wird, sobald der Batch fertig ist. Batch-Zugang
und die maximale Batch-Größe sind plangebunden; siehe die Preisseite.
Caching
cache_mode steuert, wie perceive seinen 1-Stunden-Ergebnis-Cache
behandelt, verschlüsselt nach deinem Projekt, der URL und den
render-beeinflussenden Anfrageoptionen.
cache_mode |
Verhalten |
|---|---|
enabled (Standard) |
Ein zwischengespeichertes Ergebnis zurückgeben, wenn eine identische Anfrage innerhalb der letzten Stunde gerendert wurde. cache_hit ist true, cost_cents ist 0. |
bypass |
Den Cache überspringen und frisch rendern. |
refresh |
Frisch rendern und den Cache-Eintrag ersetzen. |
Erwähnenswert: ein Cache-Treffer zählt trotzdem als eine perceive-Operation gegen dein Monatskontingent. Das Kontingent misst Operationen, nicht Browser-Renders — der Cache spart dir Render-Zeit, nicht Kontingent.
Code-Beispiele
curl — Nur Markdown
curl -X POST https://api.enconvert.com/v2/perceive \
-H "X-API-Key: sk_live_your_private_key" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com/blog/post",
"outputs": ["markdown"]
}'
curl — Markdown plus strukturierte Daten
curl -X POST https://api.enconvert.com/v2/perceive \
-H "X-API-Key: sk_live_your_private_key" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com/pricing",
"outputs": ["markdown", "structured"],
"extract": ["metadata", "structured_data", "tables"]
}'
curl — Vollständige Ausgaben plus PDF
curl -X POST https://api.enconvert.com/v2/perceive \
-H "X-API-Key: sk_live_your_private_key" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com/report",
"outputs": ["markdown", "html_cleaned", "screenshot_full_page", "pdf"],
"pdf_options": {"format": "A4", "print_background": true}
}'
Python
import requests
response = requests.post(
"https://api.enconvert.com/v2/perceive",
headers={"X-API-Key": "sk_live_your_private_key"},
json={
"url": "https://example.com/pricing",
"outputs": ["markdown", "structured"],
"extract": ["metadata", "tables"],
},
)
response.raise_for_status()
data = response.json()
# Download the Markdown artifact from its signed URL
markdown_url = data["outputs"]["markdown"]["url"]
markdown_text = requests.get(markdown_url).text
print(data["structured"])
print(markdown_text)
Node.js
const res = await fetch("https://api.enconvert.com/v2/perceive", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-Key": "sk_live_your_private_key"
},
body: JSON.stringify({
url: "https://example.com/pricing",
outputs: ["markdown", "structured"],
extract: ["metadata", "tables"]
})
});
const data = await res.json();
// Download the Markdown artifact from its signed URL
const markdownText = await fetch(data.outputs.markdown.url).then(r => r.text());
console.log(data.structured);
console.log(markdownText);
Wenn du EnConvert von Claude, Cursor oder einem anderen MCP-Client aus
aufrufst, wird dieselbe Fähigkeit als das perceive_url-Tool
bereitgestellt — siehe die MCP-Server-Seite.
Plan-Gating
V2-perceive-Operationen sind ein separater Zähler von V1-Konvertierungen. V1-Pläne enthalten kein perceive-Kontingent; du brauchst einen V2-inklusiven Plan.
| Plan | Perceive-Operationen / Monat | Schema-Extraktion (LLM-Stufe) |
|---|---|---|
| Free | 50 | Nicht enthalten |
| Starter | 850 | Claude Haiku |
| Pro | 8.500 | Claude Haiku |
| Business | 42.500 | Claude Haiku |
| Enterprise | Unbegrenzt | Enthalten |
Batch-Zugang und Größenlimits pro Batch, Basic-Auth-/Cookie-/Header-
Unterstützung und proxy_url (Business+, sobald es kommt) sind durch den
Plan gebunden. Aktuelle Zahlen stehen auf der Preisseite.
Fehlerantworten
| Status | Bedingung |
|---|---|
400 Bad Request |
URL ist nicht http(s), trägt eingebettete Anmeldedaten oder löst zu einer privaten, Loopback- oder Link-local-Adresse auf (SSRF-Schutz). |
400 Bad Request |
Ungültiges auth (fehlender username/password), cookies (kein Array, über 50 Einträge, fehlende Felder) oder headers (kein Objekt, über 20 Einträge, blockierter Name). |
401 Unauthorized |
Fehlender oder ungültiger API-Schlüssel / JWT-Token. |
402 Payment Required |
Perceive ist nicht in deinem aktuellen Plan, oder dein monatliches perceive-Kontingent ist erschöpft. |
403 Forbidden |
/v2/perceive ist nicht in den erlaubten Endpunkten des API-Schlüssels. |
403 Forbidden |
Batch in deinem Plan nicht verfügbar, oder Batch-Größe überschreitet das Limit deines Plans. |
403 Forbidden |
respect_robots=true und die robots.txt der Seite verbietet die URL. |
404 Not Found |
Unbekannte operation_id oder job_id, oder eine, die einem anderen Projekt gehört. |
422 Unprocessable Entity |
Anfragevalidierung fehlgeschlagen (ungültiges Enum in outputs/extract, wait_timeout_ms außerhalb des Bereichs, Viewport außerhalb der Grenzen). |
422 Unprocessable Entity |
proxy_url, geolocation oder action_chain wurde gesendet — reserviert für eine spätere Version. |
500 Internal Server Error |
Der Render ist fehlgeschlagen. Die Meldung enthält die operation_id, die dem Support genannt werden kann. |
Die vollständige Statuscode-Referenz steht in der Fehlercodes-Anleitung.
Limits
| Limit | Wert |
|---|---|
| URL-Länge | 2.048 Zeichen |
wait_timeout_ms |
0–60.000 ms |
js_code-Länge |
20.000 Zeichen |
| Viewport-Breite | 320–3.840 px |
| Viewport-Höhe | 240–2.160 px |
| Cookies pro Anfrage | 50 |
| Benutzerdefinierte Header pro Anfrage | 20 |
main_content-Extract |
50.000 Zeichen |
| Batch-URLs pro Anfrage | 1.000 (Schema-Deckel; das Batch-Limit deines Plans ist niedriger) |
| Inline-Batch-Schwelle | 10 URLs (größere Batches laufen asynchron) |
| Ergebnis-Cache-TTL | 1 Stunde |
| Ablauf signierter URL | 15 Minuten |
| Monatliche perceive-Operationen | Planabhängig (Free: 50) |