Node.js SDK für Dateikonvertierung#

@enconvert/node-sdk ist der offizielle JavaScript- und TypeScript-Client für die EnConvert-API. Dreizehn typisierte Konvertierungsmethoden bilden REST-Endpunkte wie POST /v1/convert/url-to-pdf 1:1 ab, und ein zweiter Namensraum, client.v2, ergänzt Web-Intelligenz: eine URL in agentenfertige Artefakte überführen (perceive), die URLs einer Site ermitteln (discover), eine Websuche ausführen (lookup), strukturierte Daten destillieren (distill), eine Site als RAG-fertiges JSONL ingestieren (ingest) und Seiten auf Änderungen überwachen (watch). Das SDK zielt auf Node.js 18+ ohne Laufzeit-Abhängigkeiten, baut auf nativem fetch, FormData und node:stream auf und erholt sich transparent von Reverse-Proxy-Timeouts, indem es den Job-Status abfragt. Es wird mit dualen ESM- und CJS-Builds sowie vollständigen TypeScript-Deklarationen ausgeliefert.

npm: @enconvert/node-sdk · Quelle: enconvert/node-sdk · Node: 18+

Installation#

npm install @enconvert/node-sdk
pnpm add @enconvert/node-sdk
yarn add @enconvert/node-sdk

Schnellstart#

import { Enconvert } from "@enconvert/node-sdk";

const client = new Enconvert({ apiKey: process.env.ENCONVERT_API_KEY! });

// V1: Eine URL in ein PDF konvertieren und auf die Festplatte streamen.
const result = await client.convertUrlToPdf("https://example.com", {
    saveTo: "page.pdf",
});
console.log(result.presignedUrl);

// V2: Eine Seite so lesen, wie dein Agent es tun sollte, mit angehängtem Qualitätswert.
const op = await client.v2.perceive("https://example.com", {
    outputs: ["markdown", "structured"],
});
console.log(op.outputs.markdown.url, op.renderQuality); // z. B. 0.93

Das SDK läuft in jeder modernen Node-Runtime (Node 18+, Bun, Deno über den npm-Spezifizierer). Es ist ausschließlich serverseitig, bündle deinen privaten API-Schlüssel also niemals in eine Browser-Anwendung.


Was der Client bereitstellt#

Ein Client, zwei Oberflächen. Beide sind über dieselbe Enconvert-Instanz erreichbar und teilen sich einen API-Schlüssel.

Oberfläche Erreichbar über Was sie abdeckt
Dateikonvertierung client.convertUrlToPdf(...), client.convertImage(...) und so weiter Dreizehn typisierte Methoden für URL-Rendering, Bildkonvertierung, Bildkomprimierung, Dokumentkonvertierung sowie Job- und Website-Batch-Polling. Siehe Dateikonvertierung.
Web-Intelligenz (V2) client.v2.perceive(...), client.v2.distill(...) und so weiter Dreiundzwanzig Methoden über sechs Fähigkeiten hinweg: perceive, discover, lookup, distill, ingest, watch. Siehe Web-Intelligenz (V2).

V2-Endpunkte verlangen einen privaten API-Schlüssel (sk_...); öffentliche Schlüssel werden abgelehnt. Wie sich die beiden Schlüsseltypen unterscheiden, steht unter Authentifizierung, und die REST-Oberfläche hinter client.v2 beschreibt die V1 und V2.


Dateikonvertierung#

Die Konvertierungs-Oberfläche stellt dreizehn Methoden bereit, die die REST-API 1:1 abbilden:

Methode Endpunkt Rückgabe
convertUrlToPdf(url, options?) POST /v1/convert/url-to-pdf ConversionResult
convertUrlToScreenshot(url, options?) POST /v1/convert/url-to-screenshot ConversionResult
convertUrlToMarkdown(url, options?) POST /v1/convert/url-to-markdown ConversionResult
convertImage(file, options) POST /v1/convert/{from}-to-{to} ConversionResult
convertDocument(file, options?) POST /v1/convert/{from}-to-{to} ConversionResult
compressImage(file, options?) POST /v1/convert/compress-image ConversionResult
convertToMarkdown(file, options?) POST /v1/convert/anything-to-markdown ConversionResult
convertToPdf(file, options?) POST /v1/convert/anything-to-pdf ConversionResult
getJobStatus(jobId) GET /v1/convert/status/{jobId} JobStatus
convertWebsiteToPdf(url, options?) POST /v1/convert/website-to-pdf BatchSubmission
convertWebsiteToScreenshot(url, options?) POST /v1/convert/website-to-screenshot BatchSubmission
getBatchStatus(batchId) GET /v1/convert/batch/{batchId} BatchStatus
waitForBatch(batchId, options?) GET /v1/convert/batch/{batchId} (per Polling) BatchStatus

Jede Methode liefert ein typisiertes Promise. Alle Optionsfelder sind optional, sofern nicht anders vermerkt. Die letzten vier sind Batch-Helfer für ganze Websites: Sie reichen asynchrone Jobs ein und fragen sie ab, geben also eine BatchSubmission oder einen BatchStatus zurück statt eines ConversionResult.


convertUrlToPdf#

Rendert jede öffentliche URL als PDF.

const result = await client.convertUrlToPdf("https://example.com", {
    pdfOptions: { pageSize: "A4", orientation: "landscape" },
    singlePage: false,
    viewportWidth: 1440,
    saveTo: "report.pdf",
});
Option Typ Standard Beschreibung
saveTo string -- Lokaler Pfad, auf den das PDF gestreamt wird. Übergeordnete Verzeichnisse werden automatisch angelegt.
singlePage boolean true true erzeugt eine durchgehende Seite. false paginiert anhand von pdfOptions.pageSize.
pdfOptions PdfOptions -- Seitengröße, Ausrichtung, Ränder, Skalierung, Graustufen, Kopf- und Fußzeile. Siehe PDF-Optionen.
viewportWidth number 1920 Breite des Browser-Viewports in Pixeln.
viewportHeight number 1080 Höhe des Browser-Viewports in Pixeln.
loadMedia boolean true Wartet vor der Aufnahme auf Bilder und Videos.
enableScroll boolean true Scrollt von oben nach unten, um Lazy Loader auszulösen.
outputFilename string auto Überschreibt den generierten Dateinamen. .pdf wird ergänzt, falls es fehlt.

convertUrlToScreenshot#

Nimmt ein ganzseitiges PNG einer beliebigen URL auf.

const result = await client.convertUrlToScreenshot("https://example.com", {
    viewportWidth: 1440,
    saveTo: "screenshot.png",
});

Akzeptiert dieselben Optionen für Viewport, Medien, Scrollen und Dateinamen wie convertUrlToPdf (ohne singlePage und pdfOptions).


convertUrlToMarkdown#

Extrahiert sauberes GitHub-Flavored Markdown aus jeder URL. Der Konverter entfernt Navigation, Fußzeilen, Werbung und Skripte, behält den Hauptartikeltext und stellt YAML-Frontmatter voran (title, description, url, links, images).

const result = await client.convertUrlToMarkdown("https://example.com/article", {
    saveTo: "article.md",
});

Nützlich, um RAG-Pipelines zu bauen, fremde Inhalte in ein CMS zu importieren oder Trainingsdaten zu erzeugen. Wenn du zusätzlich zum Markdown einen Render-Qualitätswert möchtest, nimm stattdessen client.v2.perceive.


convertImage#

Konvertiert zwischen jpeg, png, svg, heic und webp.

// Aus einem Pfad
await client.convertImage("photo.heic", {
    outputFormat: "webp",
    saveTo: "photo.webp",
});

// Aus Bytes
import { readFile } from "node:fs/promises";
const buf = await readFile("photo.heic");

await client.convertImage(
    { data: buf, filename: "photo.heic" },
    { outputFormat: "webp", saveTo: "photo.webp" },
);

// Ein SVG mit fester Breite rastern
await client.convertImage("logo.svg", { outputFormat: "png", width: 512, saveTo: "logo.png" });

Das Eingabeformat wird aus der Endung des Pfads bzw. Dateinamens erkannt. Das Ausgabeformat ist erforderlich.

Option Typ Erforderlich Beschreibung
outputFormat string Ja Zielformat: jpeg, png, svg, heic oder webp (und jpeg bei einer .pdf-Eingabe). Die Aliase jpg, yml, htm und md werden normalisiert. Nicht unterstützte Paare werfen einen Fehler, bevor die Anfrage rausgeht.
saveTo string -- Lokaler Pfad, auf den das Ergebnis gestreamt wird.
outputFilename string -- Überschreibt den generierten Dateinamen.
width number -- Nur bei SVG-Eingabe (svg-to-png, svg-to-jpeg, svg-to-webp), 1 bis 10000. Allein gesetzt skaliert der Wert proportional und übernimmt die Höhe aus dem Seitenverhältnis des SVG.
height number -- Nur bei SVG-Eingabe (svg-to-png, svg-to-jpeg, svg-to-webp), 1 bis 10000. Allein gesetzt skaliert der Wert proportional und übernimmt die Breite aus dem Seitenverhältnis des SVG.

Setze width und height gemeinsam, um eine exakte Leinwand festzulegen, was das Seitenverhältnis verändern kann. Lässt du beide weg, behält die Ausgabe die intrinsische Breite, Höhe oder viewBox des SVG. Die Gesamtzahl der Ausgabepixel ist auf 25.000.000 begrenzt. Keine der beiden Optionen wird von svg-to-heic akzeptiert, und das SDK wirft einen Fehler, bevor es die Anfrage sendet, wenn du sie einer anderen Konvertierung übergibst.


convertDocument#

Konvertiert Dokumente und Datenformate. Der Standardwert von outputFormat ist "pdf".

// docx zu pdf
await client.convertDocument("report.docx", { saveTo: "report.pdf" });

// json zu yaml
await client.convertDocument("data.json", {
    outputFormat: "yaml",
    saveTo: "data.yaml",
});

// markdown zu pdf mit eigenem Seitenlayout
await client.convertDocument("README.md", {
    outputFormat: "pdf",
    pdfOptions: { pageSize: "A4", margins: { top: 20, bottom: 20, left: 25, right: 25 } },
    saveTo: "readme.pdf",
});

Unterstützte Eingaben: doc, docx, xls, xlsx, ppt, pptx, html, htm, odt, ods, odp, ots, pages, numbers, markdown (.md, .markdown), csv, json, xml, yaml (.yaml, .yml), toml.

Für EPUB gibt es kein eigenes Dokumentkonvertierungs-Paar. Schicke .epub-Dateien stattdessen durch convertToPdf oder convertToMarkdown.

Option Typ Standard Beschreibung
outputFormat string "pdf" Zielformat.
saveTo string -- Lokaler Pfad, auf den das Ergebnis gestreamt wird.
outputFilename string -- Überschreibt den generierten Dateinamen.
pdfOptions PdfOptions -- Seitenlayout. Wird nur berücksichtigt, wenn die Ausgabe ein PDF ist.

compressImage#

Verkleinert ein PNG, JPEG oder WebP, ohne das Format zu ändern.

// Nur der verlustfreie Durchgang
const result = await client.compressImage("photo.jpg", { saveTo: "photo-small.jpg" });

// Auf ein Budget von 200 KB zielen
const capped = await client.compressImage("photo.jpg", {
    targetSizeKb: 200,
    saveTo: "photo-capped.jpg",
});

console.log(capped.fileSize);

Unterstützte Eingaben: .png, .jpg, .jpeg, .webp.

Die Ausgabe behält Format und Endung der Eingabe, es gibt hier also kein Ausgabeformat zu wählen. Die erste Stufe ist verlustfrei: Metadaten werden entfernt, ICC-Profil und EXIF-Ausrichtung bleiben erhalten, und das Ergebnis ist nie größer als die Eingabe. targetSizeKb ergänzt eine zweite Stufe, die bei gesperrtem Seitenverhältnis herunterskaliert, bis das Budget erreicht ist. Dieses Ziel gilt als Best Effort: Ein unerreichbares Budget liefert die kleinste erzielte Datei statt eines Fehlers, prüfe also result.fileSize. Animierte APNG- und animierte WebP-Dateien werden mit 400 abgelehnt, und die dekodierte Leinwand ist auf 40.000.000 Pixel begrenzt.

Option Typ Erforderlich Beschreibung
targetSizeKb number -- Größenbudget in KB, ganzzahlig, Minimum 1. Weglassen, um nur den verlustfreien Durchgang auszuführen.
saveTo string -- Lokaler Pfad, auf den das Ergebnis gestreamt wird.
outputFilename string -- Überschreibt den generierten Dateinamen. Die Eingabe-Endung bleibt erhalten.

convertToMarkdown#

Konvertiert jede unterstützte Dokument-, Tabellen-, Präsentations-, E-Book-, Web- oder Klartextdatei nach Markdown.

await client.convertToMarkdown("handbook.docx", {
    saveTo: "handbook.md",
});

Unterstützte Eingaben (22): .csv, .doc, .docx, .epub, .htm, .html, .markdown, .md, .mdown, .mkd, .odp, .ods, .odt, .pdf, .ppt, .pptx, .rtf, .text, .txt, .xhtml, .xls, .xlsx.

Die Ausgabe ist eine einzelne, überschriftenbewusste .md-Datei, gebaut für RAG-Chunking: Die Überschriftenhierarchie des Dokuments übersteht die Konvertierung, sodass ein semantischer Chunker an Überschriften trennen kann statt an willkürlichen Zeichenzahlen. Dieser Endpunkt kennt keine PDF-Optionen. Jede andere Endung wirft einen Fehler, bevor eine Anfrage gestellt wird.

Option Typ Erforderlich Beschreibung
saveTo string -- Lokaler Pfad, auf den das Markdown gestreamt wird.
outputFilename string -- Überschreibt den generierten Dateinamen.

Wenn das Chunking gleich mit erledigt werden soll, übergib dieselben Dateien an client.v2.ingestFiles.


convertToPdf#

Konvertiert jede unterstützte Dokument-, Bild-, E-Book-, Web- oder Klartextdatei nach PDF.

// docx zu pdf
await client.convertToPdf("contract.docx", { saveTo: "contract.pdf" });

// html zu pdf mit vollständiger Seitengeometrie
await client.convertToPdf("invoice.html", {
    pdfOptions: { pageSize: "A4", margins: { top: 15, bottom: 15, left: 15, right: 15 } },
    saveTo: "invoice.pdf",
});

// pdf zu Graustufen-pdf (Durchreichen)
await client.convertToPdf("scan.pdf", {
    pdfOptions: { grayscale: true },
    saveTo: "scan-gray.pdf",
});

Unterstützte Eingaben (36): .bmp, .csv, .doc, .docx, .epub, .gif, .heic, .heif, .htm, .html, .jpeg, .jpg, .markdown, .md, .mdown, .mkd, .numbers, .odp, .ods, .odt, .ots, .pages, .pdf, .png, .ppt, .pptx, .rtf, .svg, .text, .tif, .tiff, .txt, .webp, .xhtml, .xls, .xlsx.

Eine .pdf-Eingabe wird akzeptiert und durchgereicht, mit pdfOptions: { grayscale: true } dient diese Methode also zugleich als PDF-Normalisierungspfad. EPUB wird ebenfalls hier abgewickelt, da es kein eigenes Dokumentkonvertierungs-Paar hat. Jede andere Endung wirft einen Fehler, bevor eine Anfrage gestellt wird.

Die Geometrie hängt von der Eingabe ab. Die vollständige Seitengeometrie (Seitengröße, Seitenbreite und -höhe, Ausrichtung, Ränder, Skalierung, Kopfzeile, Fußzeile) wird bei HTML (.html, .htm, .xhtml), Markdown, Klartext, EPUB, Bild- und SVG-Eingaben berücksichtigt. Office-, ODF-, iWork-, RTF- und CSV-Eingaben sowie das Durchreichen von PDFs unterstützen nur grayscale und antworten mit 400, wenn eine explizite Geometrie-Option gesetzt ist. grayscale selbst wird bei jeder Eingabe berücksichtigt.
Option Typ Erforderlich Beschreibung
saveTo string -- Lokaler Pfad, auf den das PDF gestreamt wird.
outputFilename string -- Überschreibt den generierten Dateinamen. .pdf wird ergänzt, falls es fehlt.
pdfOptions PdfOptions -- Seitenlayout. Siehe den Hinweis oben dazu, welche Eingaben die Geometrie berücksichtigen.

getJobStatus#

Fragt den Status eines asynchronen oder wiederhergestellten Jobs ab.

const status = await client.getJobStatus("job_abc123");

if (status.status === "success") {
    console.log(status.presignedUrl);
} else if (status.status === "failed") {
    console.error(status.error);
}

Liefert { status: "processing" | "success" | "failed", presignedUrl?, objectKey?, error? }.

In der Regel musst du das nicht direkt aufrufen. Das SDK fragt automatisch ab, wenn eine synchrone Anfrage 5xx liefert. Siehe Timeout-Recovery weiter unten.

Batch-Helfer für ganze Websites#

convertWebsiteToPdf und convertWebsiteToScreenshot ermitteln die Seiten einer Website, stellen sie alle in die Warteschlange und bündeln die Ausgaben in einem ZIP. Beide liefern sofort eine BatchSubmission; frage anschließend mit getBatchStatus ab oder blockiere mit waitForBatch. Gemeinsame Optionen sind crawlMode ("auto", "sitemap" oder "full"), includePatterns, excludePatterns, notificationEmail und callbackUrl; convertWebsiteToPdf ergänzt singlePage und pdfOptions.

const batch = await client.convertWebsiteToPdf("https://example.com", {
    crawlMode: "sitemap",
    excludePatterns: ["/tag/"],
});

const done = await client.waitForBatch(batch.batchId, { saveTo: "site.zip" });
console.log(done.status, done.completed, done.failed, done.zipDownloadUrl);

waitForBatch akzeptiert intervalMs (Standard 5_000), timeoutMs (Standard 1_800_000, also dreißig Minuten) und saveTo. Wird die Frist überschritten, wirft es APIError(504, ...). Die REST-Oberfläche beschreibt die Endpunkt-Übersicht.


Web-Intelligenz (V2)#

Alles unterhalb von client.v2 liefert Daten, denen ein Agent vertrauen kann, denn jedes V2-Rendering trägt einen renderQuality-Wert zwischen 0.0 und 1.0. Eine blockierte Seite, eine Bot-Challenge, eine Login-Schranke, eine HTTP-Fehlerseite, ein Soft-404 oder eine leere SPA-Hülle kommt mit niedrigem Wert samt benannten deductions und warnings zurück und wird damit markiert, statt für echten Inhalt gehalten zu werden. Der Inhalt wird trotzdem zurückgegeben; was du damit machst, entscheidest du. Werte unterhalb von etwa 0.40 bedeuten, dass das Rendering in keinem sinnvollen Sinn geglückt ist.

Dreiundzwanzig Methoden über sechs Fähigkeiten hinweg:

Methode Endpunkt Rückgabe
v2.perceive(url, options?) POST /v2/perceive PerceiveResult
v2.perceiveDirect(url, options?) POST /v2/perceive PerceiveDirectResult
v2.getPerceiveOperation(operationId) GET /v2/perceive/{operationId} PerceiveResult
v2.downloadPerceiveArtifact(operationId, output?) GET /v2/perceive/{operationId} PerceiveDirectResult
v2.perceiveBatch(urls, options?) POST /v2/perceive/batch PerceiveBatchResult
v2.getPerceiveBatch(jobId) GET /v2/perceive/batch/{jobId} PerceiveBatchResult
v2.discover(url, options?) POST /v2/discover DiscoverResult
v2.lookup(query, options?) POST /v2/lookup LookupResult
v2.distill(options) POST /v2/distill DistillResult
v2.ingest(options) POST /v2/ingest IngestJob
v2.ingestFiles(files, options?) POST /v2/ingest/files IngestJob
v2.listIngestJobs(options?) GET /v2/ingest IngestJobList
v2.getIngestJob(jobId) GET /v2/ingest/{jobId} IngestJob
v2.cancelIngestJob(jobId) DELETE /v2/ingest/{jobId} IngestJob
v2.retryIngestWebhook(jobId) POST /v2/ingest/{jobId}/retry-webhook WebhookRetryResult
v2.getWebhookSecret() GET /v2/ingest/webhook-secret WebhookSecret
v2.rotateWebhookSecret() POST /v2/ingest/webhook-secret/rotate WebhookSecret
v2.createWatcher(url, options?) POST /v2/watch Watcher
v2.listWatchers(options?) GET /v2/watch WatcherList
v2.getWatcher(watcherId) GET /v2/watch/{watcherId} Watcher
v2.getWatcherSnapshots(watcherId, options?) GET /v2/watch/{watcherId}/snapshots WatcherSnapshotList
v2.updateWatcher(watcherId, updates) PATCH /v2/watch/{watcherId} Watcher
v2.deleteWatcher(watcherId) DELETE /v2/watch/{watcherId} Watcher

Optionen sind auf der SDK-Oberfläche in camelCase und werden in das snake_case-Wire-Format der API serialisiert; Antworten werden zurück nach camelCase abgebildet. Deine eigenen Payloads (Extraktionsschemata, extrahierte Daten, verfolgte Felder, Diff-Einträge) werden unverändert durchgereicht.


Perceive#

Rendert eine URL in die Artefakte, die du anforderst: Markdown, bereinigtes oder rohes HTML, einen Viewport- oder Ganzseiten-Screenshot, ein PDF, eine Linkliste, eine Bildliste oder strukturierte Daten. perceive arbeitet synchron und liefert die abgeschlossene Operation mit 15 Minuten gültigen signierten Artefakt-URLs. Vollständige Referenz: Perceive.

const op = await client.v2.perceive("https://example.com", {
    outputs: ["markdown", "screenshot", "structured"],
    extract: ["tables", "metadata"],
    onlyMainContent: true,
    waitFor: "css:.article-body",
    viewport: { width: 1440, height: 900 },
});

console.log(op.renderQuality);        // 0.0 bis 1.0
console.log(op.deductions);           // z. B. { http_error: 0.7 }
console.log(op.outputs.markdown.url); // 15 Minuten gültige signierte URL
console.log(op.structured);

if ((op.renderQuality ?? 0) < 0.4) {
    console.warn("Bad read, do not feed this to the model:", op.warnings);
}

// Artefakt-URLs später neu signieren, ohne erneut zu rendern:
const again = await client.v2.getPerceiveOperation(op.operationId);
Option Typ Standard Beschreibung
outputs PerceiveOutputName[] ["markdown", "structured"] Beliebige aus markdown, html_cleaned, html_raw, screenshot, screenshot_full_page, pdf, links, images, structured.
extract PerceiveExtractName[] -- Heuristische Ziele: tables, prices, contacts, metadata, main_content, headings, structured_data, technologies, all.
onlyMainContent boolean true Entfernt Navigation, Header, Footer und Cookie-Banner aus der Markdown-Ausgabe, abgesichert durch einen Fidelity-Guard. false liefert die vollständige Seite unangetastet.
schema Record<string, unknown> -- JSON-Schema für die strukturierte Extraktion auf der LLM-Stufe.
waitFor string -- CSS-Selektor (optional als "css:...") oder "js:<expr>", auf den vor der Aufnahme gewartet wird.
waitTimeoutMs number 30000 0 bis 60000.
jsCode string -- JavaScript, das nach der Navigation ausgeführt wird. Maximal 20000 Zeichen.
viewport { width?, height? } 1920 x 1080 Breite 320 bis 3840, Höhe 240 bis 2160.
headers Record<string, string> -- Zusätzliche Request-Header.
cookies BrowserCookie[] -- Cookies, die vor dem Rendern gesetzt werden. Jedes braucht name, value und entweder domain oder url.
auth { username, password } -- HTTP Basic Auth.
cacheMode "enabled" \| "bypass" \| "refresh" "enabled" Cache für eine Stunde. bypass überspringt ihn, refresh erzwingt ein neues Rendering.
pdfOptions PdfOptions -- Nur sinnvoll, wenn outputs den Wert "pdf" enthält. Siehe PDF-Optionen.
blockResources PerceiveResourceType[] -- Ressourcentypen, die der Browser nicht laden soll, zum Beispiel ["image", "font", "media"].
respectRobots boolean -- Beachtet die Robots-Regeln der Website.
mobile boolean -- Rendert mit einem mobilen Profil.
directDownload boolean -- Nur bei perceive. Nimm besser perceiveDirect, das die Option für dich setzt.
Drei Optionen sind deklariert, aber noch nicht aktiv. proxyUrl, geolocation und actionChain sind auf PerceiveOptions typisiert, werden serverseitig aber derzeit mit 422 abgelehnt. Sie sind reserviert, nicht nutzbar.

Direkter Download. perceiveDirect spart den Umweg über die signierte URL: Der HTTP-Antwortkörper enthält die Artefakt-Bytes, und die Metadaten reisen in den Headern mit. Es verlangt genau eine artefakterzeugende Ausgabe, und das SDK wirft lokal einen Fehler vor dem Senden, wenn das nicht der Fall ist ("structured" darf mitfahren, bleibt aber serverseitig inline und wird nicht zurückgegeben).

import { writeFile } from "node:fs/promises";

const direct = await client.v2.perceiveDirect("https://example.com", { outputs: ["markdown"] });
console.log(direct.contentType, direct.renderQuality, direct.sourceStatusCode);
await writeFile(direct.filename ?? "page.md", direct.content);

// Ein gespeichertes Artefakt einer früheren Operation erneut als rohe Bytes laden.
// `output` darf entfallen, wenn die Operation genau ein Artefakt erzeugt hat.
const raw = await client.v2.downloadPerceiveArtifact(op.operationId, "markdown");

downloadPerceiveArtifact liefert 410, sobald das gespeicherte Artefakt abgelaufen ist, und 400 (mit Auflistung der verfügbaren Ausgaben), wenn die Operation mehr als ein Artefakt erzeugt hat und du output weggelassen hast.

Batches. perceiveBatch nimmt bis zu 1000 URLs mit einem gemeinsamen Optionsblock entgegen. Kleine Batches laufen inline und kommen fertig zurück; größere liefern den Status "queued", frage sie also mit der zurückgegebenen jobId über getPerceiveBatch ab.

const batch = await client.v2.perceiveBatch(["https://example.com/a", "https://example.com/b"], {
    outputs: ["markdown"],
    outputMode: "zip",
});

let job = await client.v2.getPerceiveBatch(batch.jobId);
while (job.status === "queued" || job.status === "processing") {
    await new Promise((r) => setTimeout(r, 3000));
    job = await client.v2.getPerceiveBatch(batch.jobId);
}
console.log(job.completed, job.failed, job.zip?.url);

outputMode ist "manifest" (Standard, ein Eintrag pro URL in items) oder "zip" (jedes erfolgreiche Artefakt wird gebündelt, sobald der Job fertig ist). Der Batch-Endpunkt lehnt directDownload ab; nutze stattdessen outputMode: "zip".


Discover#

Listet die URLs einer Website auf, ohne etwas zu rendern. Es ist kein Browser beteiligt, das macht es schnell und günstig im Vergleich dazu, jede Seite wahrzunehmen. Vollständige Referenz: Discover.

const found = await client.v2.discover("https://example.com", {
    mode: "hybrid",
    maxUrls: 200,
    maxDepth: 3,
    excludePatterns: ["/tag/", "/author/"],
    sameDomainOnly: true,
});

console.log(found.total, found.truncated, found.sources); // z. B. { sitemap: 42, crawl: 30 }
for (const url of found.urls) console.log(url);
Option Typ Standard Beschreibung
mode "sitemap" \| "crawl" \| "hybrid" "hybrid" Nur Sitemap, nur HTTP-Crawl oder beides.
maxUrls number 100 1 bis 1000. truncated ist true, wenn es mehr URLs gab, als dieses Limit zuließ.
maxDepth number 2 1 bis 5. Crawl-Tiefe ab der Start-URL.
includePatterns string[] -- Regex-Positivliste, maximal 50 Einträge.
excludePatterns string[] -- Regex-Negativliste, angewandt nach includePatterns, maximal 50 Einträge.
sameDomainOnly boolean true Hält den Crawl auf der Start-Domain.
respectRobots boolean -- Beachtet die Robots-Regeln der Website. robotsRespected im Ergebnis meldet, was tatsächlich passiert ist.

Lookup#

Führt eine kategorisierte Websuche aus und nimmt auf Wunsch die besten Treffer automatisch wahr, sodass jeder Treffer sein eigenes vollständiges PerceiveResult inline mitbringt. Vollständige Referenz: Lookup.

const search = await client.v2.lookup("best static site generators", {
    category: "web",
    numResults: 10,
    country: "us",
    locale: "en",
    timeFilter: "month",
    perceiveTop: 3,
});

for (const hit of search.results) {
    console.log(hit.position, hit.title, hit.url);
    if (hit.perceive) {
        console.log("  quality:", hit.perceive.renderQuality);
        console.log("  markdown:", hit.perceive.outputs.markdown?.url);
    }
}
console.log(search.answerBox, search.knowledgeGraph);
Option Typ Standard Beschreibung
category "web" \| "news" \| "images" \| "scholar" \| "patents" \| "maps" "web" Suchvertikale.
country string -- Google-gl-Ländercode, zum Beispiel "us" oder "in".
locale string -- Google-hl-Oberflächensprache, zum Beispiel "en".
timeFilter "hour" \| "day" \| "week" \| "month" \| "year" -- Aktualitätsfenster.
numResults number 10 1 bis 100.
page number 1 1 bis 10.
location string -- Freitext-Ort, zum Beispiel "Austin, Texas".
autocorrect boolean true Lässt den Anbieter offensichtliche Tippfehler korrigieren.
perceiveTop number 0 0 bis 10. Rendert die Top-N-Ergebnis-URLs automatisch; jede davon ist ein vollständiges Browser-Rendering.

perceiveTop im Ergebnis meldet, wie viele Treffer tatsächlich wahrgenommen wurden, was weniger sein kann als angefordert, und perceiveOperationIds liefert dir die Operations-IDs zum späteren Neusignieren.


Distill#

Schemagesteuerte strukturierte Extraktion. Gib eine Form und eine Menge URLs vor (oder eine Website, die zuerst ermittelt werden soll), und du bekommst Datensätze in dieser Form zurück. Ein optionales cssSchema beantwortet alles, was es über Selektoren kann, bevor irgendetwas auf die LLM-Stufe eskaliert. Vollständige Referenz: Distill.

const extraction = await client.v2.distill({
    urls: ["https://example.com/pricing"],
    schema: { plans: "list of plan names with monthly prices" },
    cssSchema: {
        baseSelector: ".plan-card",
        fields: [
            { name: "name", type: "text", selector: "h3" },
            { name: "price", type: "text", selector: ".price" },
            { name: "url", type: "attribute", selector: "a", attribute: "href" },
        ],
    },
});

const first = extraction.results[0];
console.log(first.data);
console.log(first.extractionTier);  // "css" | "llm" | "mixed" | "none"
console.log(first.fieldsFromCss, first.fieldsFromLlm, first.renderQuality);

Übergib genau eines von urls oder discoverFrom; das SDK wirft lokal einen Fehler, wenn du beides oder keines übergibst, und ebenso, wenn schema fehlt oder kein Objekt ist.

// Zuerst eine Website ermitteln, dann jede gefundene Seite destillieren.
await client.v2.distill({
    discoverFrom: { url: "https://example.com", mode: "sitemap", maxPages: 10 },
    schema: { title: "page title", summary: "one-line summary" },
});
Option Typ Standard Beschreibung
urls string[] -- Explizite URLs zum Destillieren, maximal 50. Schließt discoverFrom aus.
discoverFrom { url, mode?, maxPages? } -- Erst ermitteln, dann destillieren. maxPages liegt zwischen 1 und 50, Standard 10, und begrenzt sowohl Ermittlung als auch Destillation.
schema Record<string, unknown> erforderlich Ein JSON-Schema-Objekt ({ type: "object", properties: {...} }) oder eine flache { field: description }-Zuordnung.
cssSchema CssSchema -- Kostenloser Selektor-Durchgang vor jeder LLM-Eskalation.
waitFor string -- CSS-Selektor oder "js:<expr>", auf den gewartet wird.
waitTimeoutMs number 30000 0 bis 60000.
headers Record<string, string> -- Zusätzliche Request-Header.
cookies BrowserCookie[] -- Cookies, die vor dem Rendern gesetzt werden.
respectRobots boolean -- Beachtet die Robots-Regeln der Website.

Ein CssSchema besitzt einen baseSelector (den sich wiederholenden Container, ein Datensatz pro Treffer), eine fields-Liste, einen optionalen name und ein optionales targetField, das die Eigenschaft des Ausgabeschemas benennt, welche die Datensätze füllen. Jedes Feld hat die Form { name, type, selector?, attribute?, pattern?, default?, transform?, fields? }, wobei type einer der Werte text, attribute, html, regex, nested, list, nested_list ist. attribute ist bei attribute-Feldern erforderlich, pattern bei regex-Feldern und ein nicht leeres fields-Array bei den verschachtelten Typen (maximale Tiefe 5).


Ingest#

Verwandelt eine Website oder einen Stapel hochgeladener Dokumente in gechunktes, RAG-fertiges JSONL. Ingest läuft immer asynchron: Beide Einstiegspunkte liefern einen eingereihten IngestJob, und du fragst ihn entweder ab oder richtest einen Webhook ein. Vollständige Referenz: Ingest.

// Von einer Website.
const job = await client.v2.ingest({
    mode: "sitemap",
    url: "https://docs.example.com",
    maxPages: 100,
    chunk: { maxWords: 512, sentenceOverlap: 1 },
    webhookUrl: "https://my.app/hooks/enconvert",
});

// Von hochgeladenen Dateien: PDF, DOCX, PPTX, XLSX, CSV, HTML, EPUB, TXT/MD
// sowie alte oder ODF-Office-Dokumente.
const fileJob = await client.v2.ingestFiles(["handbook.pdf", "notes.docx"], {
    chunk: { maxWords: 512, sentenceOverlap: 1 },
});

// Beide werden gleich abgefragt. Nicht-terminale Zustände: queued, discovering, processing.
let status = await client.v2.getIngestJob(job.jobId);
while (!["completed", "failed", "canceled"].includes(status.status)) {
    await new Promise((r) => setTimeout(r, 5000));
    status = await client.v2.getIngestJob(job.jobId);
}
console.log(status.totalChunks, status.outputUrl, status.errorMessage);

const page = await client.v2.listIngestJobs({ limit: 20, skip: 0 });
console.log(page.jobs.length, page.hasMore);

await client.v2.cancelIngestJob(job.jobId); // idempotent
Option Typ Standard Beschreibung
mode "urls" \| "sitemap" \| "crawl" "urls" "urls" braucht urls und lehnt url ab. "sitemap" und "crawl" brauchen eine Start-url und lehnen urls ab. Beide Regeln werden lokal vor der Anfrage geprüft. Die IngestMode-Union kennt außerdem "files", was ingestFiles bei seinem Job meldet; übergib es hier nicht.
url string -- Start-URL für sitemap und crawl.
urls string[] -- Explizite URLs für den Modus "urls", maximal 1000.
maxPages number 50 Ermittlungs-Limit für sitemap und crawl, 1 bis 1000.
maxDepth number 2 1 bis 5.
sameDomainOnly boolean true Hält den Crawl auf der Start-Domain.
includePatterns / excludePatterns string[] -- Regex-Positiv- und Negativliste.
respectRobots boolean -- Beachtet die Robots-Regeln der Website.
waitFor / waitTimeoutMs string / number -- / 30000 Wartezeit pro Seite beim Rendern.
chunk { maxWords?, sentenceOverlap? } 512 / 1 maxWords liegt zwischen 32 und 4000, sentenceOverlap zwischen 0 und 10.
webhookUrl string -- Webhook bei Fertigstellung, HMAC-signiert.

ingestFiles nimmt ein FileInput[] entgegen, also Pfad-Strings, Uint8Array / Buffer oder { data, filename, contentType? }-Objekte in beliebiger Mischung. Es akzeptiert nur chunk und webhookUrl und wirft bei einer leeren Liste lokal einen Fehler.

Webhook-Signierung. Webhooks bei Fertigstellung sind HMAC-signiert. Hole das Secret (es wird beim ersten Aufruf erzeugt), um Zustellungen zu prüfen, rotiere es bei Bedarf und stelle einen Webhook erneut zu, den dein Endpunkt verpasst hat.

const secret = await client.v2.getWebhookSecret();
console.log(secret.signatureHeader, secret.timestampHeader);
console.log(secret.signatureScheme, secret.replayToleranceSeconds);

// Rotieren macht Signaturen des vorherigen Secrets sofort ungültig.
const rotated = await client.v2.rotateWebhookSecret();

// Den Webhook eines abgeschlossenen Jobs erneut zustellen.
const retry = await client.v2.retryIngestWebhook(job.jobId);
console.log(retry.delivered, retry.attempts, retry.statusCode, retry.detail);

retryIngestWebhook liefert 409, wenn der Job nicht abgeschlossen ist, und 400, wenn für den Job kein Webhook konfiguriert wurde.


Watch#

Erstellt einen Watcher, der eine URL in festem Takt neu rendert und dich benachrichtigt, wenn sich die Seite ändert: per E-Mail, per Webhook oder beides. Vollständige Referenz: Watch.

const watcher = await client.v2.createWatcher("https://example.com/pricing", {
    frequencyMinutes: 60,
    diffMode: "auto",
    webhookUrl: "https://my.app/hooks/changes",
    notifyEmail: true,
});
console.log(watcher.watcherId, watcher.nextCheckAt);

const list = await client.v2.listWatchers({ limit: 20 });
const one = await client.v2.getWatcher(watcher.watcherId);

const history = await client.v2.getWatcherSnapshots(watcher.watcherId, { limit: 10 });
for (const snap of history.snapshots) {
    console.log(snap.checkedAt, snap.hasChanges, snap.similarity, snap.changeCount);
}

await client.v2.updateWatcher(watcher.watcherId, { status: "paused" });
await client.v2.updateWatcher(watcher.watcherId, { webhookUrl: "" }); // löscht den Webhook
await client.v2.deleteWatcher(watcher.watcherId);                     // Soft Delete, idempotent
Option Typ Standard Beschreibung
frequencyMinutes number 60 Minuten zwischen zwei Prüfungen, 60 bis 43200. Die Stundengrenze ist hart.
diffMode "auto" \| "text" \| "structured" \| "tables" \| "metadata" "auto" "auto" lässt die Diff-Engine nach Inhaltstyp entscheiden.
trackFields Record<string, unknown> -- Feld- oder Selektor-Auswahl, um den Diff einzugrenzen.
webhookUrl string -- Webhook für Änderungsbenachrichtigungen, HMAC-signiert.
notifyEmail boolean true Benachrichtigt den Projektinhaber bei Änderungen per E-Mail.

updateWatcher nimmt dieselben Felder plus status ("active" oder "paused") und verlangt mindestens eines davon; bei einem leeren Update wirft das SDK lokal einen Fehler. webhookUrl: "" löscht den Webhook explizit. Das Löschen ist ein Soft Delete: deleteWatcher liefert den stillgelegten Watcher mit Status "deleted" zurück, und ein gelöschter Watcher antwortet bei getWatcher mit 404.

Jeder Snapshot trägt checkedAt, hasChanges, similarity (0.0 bis 1.0 gegenüber der vorherigen Aufnahme), renderQuality, changeCount und ein changes-Array.

Snapshot-Diffs enthalten nicht vertrauenswürdige Seiteninhalte. Einträge in snapshot.changes stammen direkt von der überwachten Seite. Escape sie, bevor du sie in HTML renderst oder in einen Log-Viewer schreibst.

PDF-Optionen#

Werden über das Feld pdfOptions an convertUrlToPdf, convertDocument, convertToPdf, convertWebsiteToPdf und client.v2.perceive übergeben (bei letzterem, wenn outputs den Wert "pdf" enthält).

const result = await client.convertUrlToPdf("https://example.com", {
    pdfOptions: {
        pageSize: "A4",
        orientation: "landscape",
        margins: { top: 10, bottom: 10, left: 15, right: 15 },
        scale: 0.9,
        grayscale: false,
    },
    saveTo: "report.pdf",
});
Feld Typ Beschreibung
pageSize string "A4", "A3", "Letter", "Legal" usw.
pageWidth / pageHeight number Explizite Seitengeometrie als Alternative zu pageSize.
orientation "portrait" \| "landscape" Standard ist Hochformat.
margins { top, bottom, left, right } (mm) Alle vier sind optional.
scale number Render-Skalierung, z. B. 0.9 für 90 %.
grayscale boolean Wandelt das PDF per Ghostscript-Nachbearbeitung in Graustufen um.
header PdfHeaderFooter { content?, height? }. content ist auf 2000 Zeichen begrenzt.
footer PdfHeaderFooter Gleiche Form wie header.

Jeder Parameter ist vollständig unter Synchrone und asynchrone Jobs beschrieben.

Fehlerbehandlung#

Fehler sind typisierte Exception-Klassen, die du per instanceof abgleichen kannst. Dieselbe Hierarchie deckt sowohl die Konvertierungsmethoden als auch client.v2 ab.

import {
    Enconvert,
    APIError,
    AuthenticationError,
    QuotaError,
    RateLimitError,
} from "@enconvert/node-sdk";

try {
    await client.v2.perceive("https://example.com", { outputs: ["markdown"] });
} catch (e) {
    if (e instanceof AuthenticationError) {
        console.error("Invalid API key. Check ENCONVERT_API_KEY.");
    } else if (e instanceof QuotaError) {
        console.error("Request rejected with 402.");
    } else if (e instanceof RateLimitError) {
        console.error("Too many requests. Back off and retry.");
    } else if (e instanceof APIError) {
        console.error(`API error [${e.statusCode}]: ${e.message}`);
    } else {
        throw e;
    }
}
Klasse Ausgelöst bei Statuscode
AuthenticationError Ungültiger, fehlender oder widerrufener Schlüssel 401, 403 (beide melden statusCode 401)
QuotaError Wird bei HTTP 402 ausgelöst 402
RateLimitError Zu viele Anfragen 429
APIError Jeder andere 4xx-/5xx-Fehler der tatsächliche Code
EnconvertError Basisklasse für alle oben genannten --

QuotaError und RateLimitError erweitern beide APIError, das wiederum EnconvertError erweitert. Ordne deine instanceof-Prüfungen also von spezifisch nach allgemein. Jeder APIError trägt ein Feld statusCode.

Manche Fehler erreichen das Netzwerk nie: eine nicht unterstützte Dateiendung, ein distill-Aufruf mit urls und discoverFrom gleichzeitig, ein ingest-Aufruf, dessen Modus und Argumente nicht zusammenpassen, ein perceiveDirect-Aufruf mit mehr als einer Artefakt-Ausgabe oder ein updateWatcher-Aufruf ohne Felder. Diese werfen lokal einen einfachen Error, damit du den Fehler in der Entwicklung findest.

Die vollständige Zuordnung der Fehlermeldungen steht in der Referenz Fehlercodes.


Timeout-Recovery#

Lange URL-zu-PDF-Vorgänge oder große Dokumentkonvertierungen können das Reverse-Proxy-Timeout von 60 bis 120 Sekunden überschreiten, selbst wenn die Konvertierung auf dem Server am Ende gelingt. Das SDK erledigt das bei den V1-Konvertierungsmethoden transparent:

  1. Vor jeder Anfrage erzeugt das SDK eine UUID und sendet sie als job_id im Request-Body.
  2. Liefert die ursprüngliche Anfrage 5xx, wechselt das SDK still auf Polling von GET /v1/convert/status/{job_id} alle 3 Sekunden.
  3. Sobald der Job als success verbucht ist, gibt das SDK das Ergebnis zurück. Sobald er als failed verbucht ist, wirft das SDK APIError.
  4. Die Polling-Frist beträgt 5 Minuten. Wird sie überschritten, wirft das SDK APIError(504, "Conversion timed out").

Dafür musst du keine Zeile Code schreiben, es funktioniert einfach. Setze timeout im Konstruktor, wenn du die erste Anfrage begrenzen willst.

V2 nutzt statt impliziter Recovery explizite Job-Objekte: perceiveBatch und ingest liefern eine ID, die du mit getPerceiveBatch bzw. getIngestJob abfragst, und ingest kann stattdessen einen Webhook aufrufen.


Konfiguration#

const client = new Enconvert({
    apiKey: process.env.ENCONVERT_API_KEY!,
    timeout: 300_000, // ms, Standard 5 Min.
    baseUrl: "https://api.enconvert.com", // für selbst gehostete Gateways überschreiben
});
Option Typ Standard Beschreibung
apiKey string -- (erforderlich) Privater API-Schlüssel (sk_...). Der Konstruktor wirft sofort einen Fehler, wenn er fehlt.
timeout number 300_000 Request-Timeout in ms. Bricht das zugrunde liegende fetch über AbortController ab.
baseUrl string https://api.enconvert.com Basis-URL der API. Schrägstriche am Ende werden entfernt.

Der Schlüssel reist bei jeder Anfrage als X-API-Key-Header mit, bei V1 wie bei V2. client.v2 wird für dich konstruiert und teilt sich Schlüssel, Basis-URL und Timeout des Clients, es gibt also nichts zusätzlich zu konfigurieren.

Hardcode den API-Schlüssel niemals. Lies ihn aus einer Umgebungsvariable oder deinem Secret-Manager. Wer deinen privaten Schlüssel erhält, kann Konvertierungen und V2-Operationen auf deinem Konto ausführen. Schlüssel rotierst du im Dashboard.

Form des Ergebnisses#

Jede Konvertierungsmethode liefert ein ConversionResult:

interface ConversionResult {
    presignedUrl: string;          // signierte URL zum Download der Ausgabe (1 Stunde)
    objectKey: string;             // Objektschlüssel im Speicher
    filename: string;              // serverseitiger Dateiname
    fileSize?: number;             // Bytes
    conversionTimeSeconds?: number;
    jobId?: string;                // vorhanden, wenn die Timeout-Recovery abgefragt hat
}

Die vorsignierte URL ist eine Stunde lang gültig. Brauchst du dauerhaften Zugriff, lade die Datei herunter (per saveTo oder indem du die URL selbst abrufst) und lege sie in deinem eigenen Bucket ab.

V2-Ergebnisse sind anders aufgebaut. Ein PerceiveResult trägt operationId, status, url, urlFinal, contentHash, renderQuality, statusCode, deductions, cacheHit, eine nach Ausgabenamen geschlüsselte outputs-Zuordnung, structured, extractionTier, tokens, costCents, durationMs, optionsEcho, error und warnings. Jeder Eintrag in outputs ist ein V2OutputArtifact der Form { url?, objectKey, sizeBytes, contentType, expiresIn }, wobei expiresIn in Sekunden angegeben ist und standardmäßig 900 beträgt. V2-Artefakt-URLs halten also 15 Minuten statt einer Stunde und werden bei jedem Lesen neu signiert. Ein erneuter Aufruf von getPerceiveOperation(operationId) liefert dir daher frische Links, ohne die Seite neu zu rendern.


TypeScript#

Die Typdefinitionen werden mit dem Paket ausgeliefert, eine @types/...-Installation ist also nicht nötig. Das Paket wird dual veröffentlicht (ESM + CJS) mit sauberen exports, types sowie .d.ts / .d.cts, sodass es unter jedem Node-Modulauflösungsmodus funktioniert.

import type {
    ClientOptions,
    CompressImageOptions,
    ConversionResult,
    ConvertDocumentOptions,
    ConvertImageOptions,
    ConvertToMarkdownOptions,
    ConvertToPdfOptions,
    FileInput,
    JobStatus,
    PdfOptions,
    UrlToMarkdownOptions,
    UrlToPdfOptions,
    UrlToScreenshotOptions,
    // Die V2-Typen kommen aus demselben Einstiegspunkt.
    DiscoverOptions, DiscoverResult,
    DistillOptions, DistillResult,
    IngestJob, IngestOptions,
    LookupOptions, LookupResult,
    PerceiveOptions, PerceiveResult, PerceiveOutputName,
    PerceiveBatchResult, PerceiveDirectResult,
    Watcher, WatcherSnapshotList,
} from "@enconvert/node-sdk";

Die Klasse EnconvertV2 selbst wird ebenfalls exportiert, falls du einen Funktionsparameter als V2-Namensraum typisieren willst.


Aktualisieren#

Das Paket bringt eine kleine CLI mit, enconvert-sdk, um sich selbst aktuell zu halten.

npx enconvert-sdk upgrade
npx enconvert-sdk upgrade --dry-run
npx enconvert-sdk version

upgrade erkennt npm, pnpm, yarn oder bun aus der Umgebung und gibt den exakten Installationsbefehl immer aus, bevor es ihn ausführt. So passiert mit deiner Lockfile nichts unbemerkt. --dry-run gibt diesen Befehl aus und hört dann auf. version meldet die installierte SDK-Version.


Quellcode und Issues#


Häufig gestellte Fragen#

Wie konvertiere ich Dateien in Node.js mit einem npm-Paket?#

Installiere @enconvert/node-sdk, erzeuge einen Client mit deinem API-Schlüssel (new Enconvert({ apiKey: process.env.ENCONVERT_API_KEY! })) und rufe eine typisierte Methode wie convertUrlToPdf, convertImage oder convertDocument auf. Übergib saveTo, um das Ergebnis direkt auf die Festplatte zu streamen.

Wie lese ich eine Webseite in Node.js als sauberes Markdown aus?#

Rufe client.v2.perceive(url, { outputs: ["markdown"] }) auf. Du bekommst in op.outputs.markdown.url eine 15 Minuten gültige signierte URL zum Markdown sowie einen renderQuality-Wert für den Lesevorgang. Willst du stattdessen direkt die Bytes, rufe client.v2.perceiveDirect(url, { outputs: ["markdown"] }) auf und lies result.content.

Was ist renderQuality und warum ist das wichtig?#

renderQuality ist ein Wert zwischen 0.0 und 1.0, der an jedem V2-Rendering hängt. Eine Bot-Challenge, eine Login-Schranke, eine HTTP-Fehlerseite, ein Soft-404 oder eine leere SPA-Hülle erhalten alle einen niedrigen Wert und kommen mit benannten deductions und warnings zurück. So wird ein schlechter Lesevorgang markiert, statt still als echte Seite in den Kontext deines Agenten zu wandern. Werte unterhalb von etwa 0.40 bedeuten, dass das Rendering praktisch fehlgeschlagen ist, auch wenn die Anfrage 200 geliefert hat.

Wie konvertiere ich HEIC in Node.js nach WebP?#

Rufe convertImage mit der HEIC-Datei (ein Pfad oder ein { data, filename }-Buffer-Objekt) und outputFormat: "webp" auf. Das SDK konvertiert zwischen jpeg, png, svg, heic und webp; das Eingabeformat wird aus der Dateiendung erkannt.

Wie komprimiere ich in Node.js ein Bild, ohne das Format zu ändern?#

Rufe compressImage mit einer .png-, .jpg-, .jpeg- oder .webp-Datei auf. Die Ausgabe behält Format und Endung der Eingabe, entfernt Metadaten und bewahrt dabei ICC-Profil und EXIF-Ausrichtung, und ist nie größer als die Eingabe. Ergänze targetSizeKb, um in Richtung eines Größenbudgets herunterzuskalieren; das Ziel gilt als Best Effort, lies also result.fileSize, um zu sehen, was tatsächlich erreicht wurde.

Wie konvertiere ich ein beliebiges Dokument für eine RAG-Pipeline nach Markdown?#

Rufe convertToMarkdown mit der Datei auf und übergib saveTo, um die .md-Datei direkt auf die Festplatte zu schreiben. Die Methode akzeptiert 22 Endungen aus Office, OpenDocument, PDF, EPUB, HTML, CSV und Klartext und liefert eine überschriftenbewusste Markdown-Datei zurück, sodass dein Chunker an den Überschriften des Dokuments trennen kann statt an willkürlichen Zeichenzahlen.

Wie verwandle ich in Node.js eine ganze Website in RAG-fertige Chunks?#

Rufe client.v2.ingest({ mode: "sitemap", url, maxPages, chunk: { maxWords: 512, sentenceOverlap: 1 } }) auf. Ingest läuft immer asynchron, frage also client.v2.getIngestJob(job.jobId) ab, bis status den Wert "completed" hat, und lies outputUrl für das signierte JSONL. Alternativ setzt du webhookUrl und lässt dich vom Fertigstellungs-Webhook informieren. Für lokale Dokumente statt einer Website durchläuft client.v2.ingestFiles([...]) dieselbe Pipeline.

Wie extrahiere ich in Node.js strukturiertes JSON aus einer Seite?#

Rufe client.v2.distill({ urls, schema }) auf, wobei schema entweder ein JSON-Schema-Objekt oder eine flache { field: description }-Zuordnung ist. Ergänze ein cssSchema, dann beantwortet der Selektor-Durchgang alles, was er kann, bevor irgendetwas auf die LLM-Stufe eskaliert; result.extractionTier, fieldsFromCss und fieldsFromLlm verraten dir, welche Stufe die Arbeit erledigt hat.

Wie überwache ich in Node.js eine Webseite auf Änderungen?#

Rufe client.v2.createWatcher(url, { frequencyMinutes: 60, diffMode: "auto", webhookUrl }) auf. Die Stundengrenze ist hart, 60 ist also der kleinste Takt. Die Historie liest du mit getWatcherSnapshots, pausieren kannst du mit updateWatcher(id, { status: "paused" }), und entfernen mit deleteWatcher, was ein idempotenter Soft Delete ist.

Wie geht das SDK mit langen Konvertierungen um, die ins Reverse-Proxy-Timeout laufen?#

Vor jeder V1-Konvertierungsanfrage erzeugt das SDK eine UUID und sendet sie als job_id; liefert die Anfrage 5xx, fragt es still alle 3 Sekunden GET /v1/convert/status/{job_id} ab, bis der Job success oder failed meldet. Die Polling-Frist beträgt 5 Minuten, danach wirft es APIError(504, "Conversion timed out"). V2 nutzt stattdessen explizite Job-IDs, die mit getPerceiveBatch oder getIngestJob abgefragt werden.

Kann ich das Node.js-SDK in einer Browser-Anwendung verwenden?#

Nein, das SDK ist ausschließlich serverseitig, weil es sich mit einem privaten API-Schlüssel (sk_...) authentifiziert, der niemals in clientseitigen Code gebündelt werden darf. Es läuft in Node 18+, Bun und Deno über den npm-Spezifizierer.

Wie lange ist die vorsignierte Download-URL gültig?#

Die presignedUrl in jedem ConversionResult ist eine Stunde lang gültig. V2-Artefakt-URLs gelten 15 Minuten und werden bei jedem Lesen neu signiert, getPerceiveOperation(operationId) liefert dir also frische Links. Für dauerhaften Zugriff lade die Datei herunter und lege sie in deinem eigenen Bucket ab.