Python SDK für Dateikonvertierung#

enconvert ist der offizielle Python-Client für die EnConvert-API: ein pip install, ein API-Schlüssel und typisierte Methoden, um Dateien zu konvertieren und das Live-Web zu lesen. Der Client läuft ab Python 3.9 mit einer einzigen Laufzeit-Abhängigkeit, requests, und liefert Inline-Typhinweise sowie einen py.typed-Marker mit, damit mypy und Pyright alles sehen. Er hat zwei Oberflächen. Zwölf Konvertierungsmethoden decken 43 {input}-to-{output}-Paare ab, dazu URL zu PDF, Screenshot und Markdown. Der Namensraum client.v2 ergänzt Web-Intelligenz: perceive, discover, lookup, distill, ingest und watch.

PyPI: enconvert · Quelle: conversionapi/python-sdk · Python: 3.9+ · Laufzeit-Abhängigkeit: requests>=2.28 · Lizenz: MIT

Installation#

pip install enconvert

uv add enconvert und poetry add enconvert funktionieren genauso. requests ist die einzige Laufzeit-Abhängigkeit, und die Type Stubs stecken im Wheel, es gibt also kein types--Paket hinterherzujagen.


Schnellstart#

Lies eine Seite so, wie dein Agent es tun sollte, mit angehängtem Qualitätswert, und konvertiere anschließend über denselben Client eine lokale Datei.

import os

from enconvert import Enconvert

client = Enconvert(api_key=os.environ["ENCONVERT_API_KEY"])

op = client.v2.perceive("https://example.com", outputs=["markdown", "structured"])
print(op.outputs["markdown"].url, op.render_quality)

print(client.convert_to_pdf("report.docx", save_to="report.pdf").presigned_url)

Jede Methode arbeitet synchron und blockierend. Das SDK ist ausschließlich serverseitig: Es authentifiziert sich mit einem privaten API-Schlüssel, liefere es also niemals in einem Desktop-, Mobil- oder Browser-Client aus. Einen Schlüssel bekommst du in deinem Dashboard, und wie Schlüssel abgegrenzt sind, steht unter Authentifizierung.


Was der Client bereitstellt#

Enconvert ist die gesamte öffentliche Oberfläche. Konvertierungsmethoden hängen direkt am Client; alles Webseitige lebt unter client.v2.

Konvertierungsmethode Endpunkt Rückgabe
convert_url_to_pdf(url, ...) POST /v1/convert/url-to-pdf ConversionResult
convert_url_to_screenshot(url, ...) POST /v1/convert/url-to-screenshot ConversionResult
convert_url_to_markdown(url, ...) POST /v1/convert/url-to-markdown ConversionResult
convert_image(file, output_format=...) POST /v1/convert/{input}-to-{output} ConversionResult
convert_document(file, ...) POST /v1/convert/{input}-to-{output} ConversionResult
convert_to_markdown(file, ...) POST /v1/convert/anything-to-markdown ConversionResult
convert_to_pdf(file, ...) POST /v1/convert/anything-to-pdf ConversionResult
convert_website_to_pdf(url, ...) POST /v1/convert/website-to-pdf BatchSubmission
convert_website_to_screenshot(url, ...) POST /v1/convert/website-to-screenshot BatchSubmission
get_job_status(job_id) GET /v1/convert/status/{job_id} JobStatus
get_batch_status(batch_id) GET /v1/convert/batch/{batch_id} BatchStatus
wait_for_batch(batch_id, ...) GET /v1/convert/batch/{batch_id} (per Polling) BatchStatus
client.v2-Fähigkeit Methoden Rückgabe
Perceive perceive, perceive_direct, get_perceive_operation, download_perceive_artifact, perceive_batch, get_perceive_batch PerceiveResult, PerceiveDirectResult, PerceiveBatchResult
Discover, Lookup, Distill discover, lookup, distill DiscoverResult, LookupResult, DistillResult
Ingest ingest, ingest_files, list_ingest_jobs, get_ingest_job, cancel_ingest_job, retry_ingest_webhook, get_webhook_secret, rotate_webhook_secret IngestJob, IngestJobList, WebhookSecret, WebhookRetryResult
Watch create_watcher, list_watchers, get_watcher, get_watcher_snapshots, update_watcher, delete_watcher Watcher, WatcherList, WatcherSnapshotList

Jedes Argument nach dem ersten positionellen ist keyword-only, in snake_case und optional, sofern eine Tabelle nichts anderes sagt. Ergebnisse sind eingefrorene Dataclasses, erzeuge also eine neue Instanz, statt eine bestehende zu verändern. Die REST-Formen stehen in der Endpunkt-Übersicht.


Dateikonvertierung#

Jede Konvertierungsmethode nimmt save_to entgegen (ein str oder os.PathLike; das Ergebnis wird dorthin gestreamt, übergeordnete Verzeichnisse werden angelegt) sowie output_filename (überschreibt den generierten Namen). Beide fehlen in den Tabellen unten.

convert_url_to_pdf#

Rendert jede erreichbare URL als PDF.

result = client.convert_url_to_pdf(
    "https://example.com", single_page=False, viewport_width=1440, save_to="report.pdf"
)
print(result.presigned_url, result.file_size)
Option Typ Standard Beschreibung
single_page bool True True liefert eine durchgehende Seite. False paginiert anhand von pdf_options.page_size.
pdf_options PdfOptions -- Seitengröße, Ausrichtung, Ränder, Skalierung, Graustufen, Kopfzeile, Fußzeile. Siehe PDF-Optionen.
viewport_width / viewport_height int 1920 / 1080 Größe des Browser-Viewports in Pixeln.

load_media und enable_scroll sind beide standardmäßig True: Ersteres wartet auf Bilder und Videos, Letzteres scrollt von oben nach unten, damit Lazy Loader auslösen. Drei weitere Optionen erreichen Seiten hinter einer Schranke: auth (HttpBasicAuth), cookies (list[BrowserCookie]) und headers (dict[str, str]).

Kombiniere auth nicht mit einem Authorization-Header. Die API lehnt den Konflikt ab, statt zu raten, welche Anmeldedaten gelten sollen. Entscheide dich für eines von beidem.

convert_url_to_screenshot#

Nimmt ein PNG einer beliebigen URL auf.

client.convert_url_to_screenshot("https://example.com", viewport_width=1440, save_to="shot.png")

Nimmt dieselben Optionen für Viewport, Medien, Scrollen, Dateinamen und Browser-Zugang entgegen wie convert_url_to_pdf, ohne single_page und pdf_options.


convert_url_to_markdown#

Zieht sauberes GitHub-Flavored Markdown aus einer URL. Navigation, Fußzeilen, Werbung und Skripte werden entfernt, der Hauptartikeltext bleibt erhalten, und YAML-Frontmatter mit title, description, url, links und images wird vorangestellt.

client.convert_url_to_markdown("https://example.com/article", save_to="article.md")

Derselbe Optionssatz wie bei convert_url_to_screenshot. Wenn du zusätzlich einen Qualitätswert, Seitenmetadaten oder eine strukturierte Extraktion im selben Lesevorgang möchtest, nimm stattdessen v2.perceive.


convert_image#

Konvertiert zwischen jpeg, png, svg, heic und webp oder rastert ein PDF nach JPEG. Das Eingabeformat ergibt sich aus der Dateiendung.

client.convert_image("photo.heic", output_format="webp", save_to="photo.webp")
client.convert_image("scan.pdf", output_format="jpeg", save_to="scan.jpeg")

output_format ist erforderlich und muss einer der Werte jpeg, png, svg, heic oder webp sein; die Aliase jpg, yml, htm und md werden für dich normalisiert. file akzeptiert einen Pfad-String, ein os.PathLike, rohe bytes oder einen FileData(data, filename)-Wrapper. Rohe bytes tragen keinen Dateinamen und werden als upload.bin hochgeladen, nimm also FileData, sobald die Endung eine Rolle spielt.

from pathlib import Path

from enconvert import FileData

blob = FileData(data=Path("photo.heic").read_bytes(), filename="photo.heic")
client.convert_image(blob, output_format="webp", save_to="photo.webp")

convert_document#

Konvertiert Dokumente und Datenformate. output_format ist standardmäßig "pdf", und pdf_options wird berücksichtigt, wenn die Ausgabe ein PDF ist.

from enconvert import PdfMargins, PdfOptions

client.convert_document("report.docx", save_to="report.pdf")
client.convert_document("data.json", output_format="yaml", save_to="data.yaml")
client.convert_document(
    "README.md", pdf_options=PdfOptions(page_size="A4", margins=PdfMargins(top=20)), save_to="r.pdf"
)

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

Für EPUB gibt es kein eigenes Dokumentpaar. Schicke .epub durch convert_to_pdf oder convert_to_markdown.


convert_to_markdown#

Erkennt ein hochgeladenes Dokument serverseitig automatisch und liefert sauberes Markdown zurück. Das ist der RAG-Ingestion-Baustein für eine einzelne Datei.

client.convert_to_markdown("handbook.docx", save_to="handbook.md")

Akzeptierte Eingaben: PDF, DOCX, PPTX, XLSX, CSV, HTML, EPUB, TXT und MD sowie alte und ODF-Office-Formate. Bilder werden hier nicht unterstützt, und der Endpunkt kennt keine PDF-Optionen. Für eine ganze Website statt einer einzelnen Datei nimm v2.ingest.


convert_to_pdf#

Erkennt eine hochgeladene Datei serverseitig automatisch und liefert ein PDF zurück.

from enconvert import PdfOptions

client.convert_to_pdf("slides.pptx", save_to="slides.pdf")
client.convert_to_pdf("scan.pdf", pdf_options=PdfOptions(grayscale=True), save_to="gray.pdf")

Akzeptierte Eingaben: Office, ODF, Pages, Numbers, RTF, CSV, HTML, Markdown, Klartext, Rasterbilder, SVG, EPUB und ein bestehendes PDF zum Durchreichen. Weil eine .pdf-Eingabe durchgereicht wird, dient die Methode zugleich als Graustufen-Normalisierer.

Nur pdf_options.grayscale wird an diesem Endpunkt berücksichtigt. Die übrigen Felder zur Seitengeometrie werden hier ignoriert. Wenn du echtes Seitenlayout brauchst, leite die Datei stattdessen über convert_document oder convert_url_to_pdf.

convert_website_to_pdf und convert_website_to_screenshot#

Ermittelt jede Seite einer Website, konvertiert sie im Hintergrund und sammelt alles in einem einzigen ZIP. Beide sind bewusst asynchron und liefern sofort eine BatchSubmission.

batch = client.convert_website_to_pdf(
    "https://example.com", crawl_mode="sitemap", exclude_patterns=["/blog/tag/"]
)
print(batch.batch_id, batch.url_count, batch.discovery_method)

status = client.wait_for_batch(batch.batch_id, save_to="site.zip")
print(status.completed, "of", status.total, "pages converted")

for item in client.get_batch_status(batch.batch_id).items:
    print(item.source_url, item.status, item.download_url)
Option Typ Standard Beschreibung
crawl_mode "auto" \| "sitemap" \| "full" Server-Standard sitemap liest ausschließlich sitemap.xml. full ergänzt einen Breitensuch-Crawl. auto wählt den höchsten für den Schlüssel verfügbaren Modus.
include_patterns / exclude_patterns list[str] -- URLs anhand eines Pfadfragments behalten oder verwerfen. Ausschlüsse greifen nur im Full-Crawl-Modus.
notification_email / callback_url str -- Adresse für die E-Mail und Webhook für den Aufruf, sobald der Batch fertig ist.
single_page bool Server-Standard Nur PDF.
pdf_options PdfOptions -- Nur PDF. Siehe PDF-Optionen.

Die Render-Optionen pro Seite gelten ebenfalls für jede ermittelte URL: viewport_width, viewport_height, load_media, enable_scroll, auth, cookies und headers. Was du nicht setzt, behält den Standard des Gateways.

wait_for_batch akzeptiert interval_seconds (Standard 5.0), timeout_seconds (Standard 1800.0) und save_to. Es löst APIError(504, ...) aus, wenn der Batch bei Ablauf der Frist noch läuft.


Unterstützte Konvertierungspaare#

Das SDK trägt eine Kopie der Konvertierungskarte des Gateways bei sich und lehnt ein nicht implementiertes Paar lokal ab, vor jedem Netzwerk-Roundtrip, und nennt in der Meldung die gültigen Ausgaben für diese Eingabe.

Eingabe Ausgaben
json csv, toml, xml, yaml
xml csv, json
yaml, toml json
csv json, xml
markdown html, pdf
html pdf
doc, excel, ppt, odt, ods, odp, ots, pages, numbers pdf
jpeg, png, svg, heic, webp jeweils untereinander, alle 20 Paare
pdf jpeg

Das sind 43 implementierte Paare. Programmatisch prüfen kannst du sie so:

from enconvert import IMPLEMENTED_CONVERSIONS, valid_outputs_for

valid_outputs_for("json")                  # ['csv', 'toml', 'xml', 'yaml']
"heic-to-webp" in IMPLEMENTED_CONVERSIONS  # True

Die vollständige Parameter-Referenz steht unter Parameter und Optionen.


Web-Intelligenz (V2)#

Jeder V2-Lesevorgang trägt render_quality, einen Float zwischen 0.0 und 1.0, der angibt, wie ehrlich die Seite gerendert hat. Ein Challenge-Screen, eine Cookie-Wand, eine Login-Schranke, eine HTTP-Fehlerseite oder eine leere SPA-Hülle kommt mit niedrigem Wert, einer deductions-Zuordnung mit den ausgelösten Abzügen und einer warnings-Liste zurück. Der Inhalt wird trotzdem geliefert, nur eben markiert, damit ein schlechter Lesevorgang nie unbemerkt in den Kontext deines Agenten gelangt. Behandle den Wert als Schranke und prüfe ihn, bevor du den Text verwendest. PerceiveResult, PerceiveDirectResult, DistillItem, LookupItem.perceive und WatcherSnapshot stellen ihn alle bereit. Die Konzepte stehen in der V2-Übersicht.

Perceive#

Rendert eine URL in agentenfertige Artefakte. Synchron: Der Aufruf liefert die abgeschlossene Operation mit signierten Artefakt-URLs zurück.

op = client.v2.perceive(
    "https://example.com",
    outputs=["markdown", "screenshot", "structured"],
    extract=["tables", "metadata"],
    only_main_content=True,
)

print(op.render_quality)            # 0.0 bis 1.0
print(op.status_code)               # HTTP-Status der Seite selbst
print(op.deductions)                # z. B. {"login_wall": 0.65}
print(op.outputs["markdown"].url)   # signierte URL, 15 Minuten
print(op.structured)

if (op.render_quality or 0) < 0.6:
    print("Low-confidence read:", op.warnings)
Option Typ Standard Beschreibung
outputs list[str] ["markdown", "structured"] Beliebige aus markdown, html_cleaned, html_raw, screenshot, screenshot_full_page, pdf, links, images, structured.
extract list[str] -- Beliebige aus tables, prices, contacts, metadata, main_content, headings, structured_data, technologies, all.
wait_for str -- CSS-Selektor, auf den vor der Aufnahme gewartet wird.
viewport PerceiveViewport 1920 x 1080 width 320 bis 3840, height 240 bis 2160.
cache_mode "enabled" \| "bypass" \| "refresh" Server-Standard Das zwischengespeicherte Rendering wiederverwenden, überspringen oder neu schreiben.
block_resources list[str] -- Beliebige aus image, media, font, stylesheet, script, xhr, fetch, websocket, manifest, other.
only_main_content bool Server-Standard Entfernt Navigation, Header, Footer und sonstiges Seiten-Chrome aus dem extrahierten Inhalt.
direct_download bool False Liefert rohe Bytes statt einer signierten URL. Nur von perceive akzeptiert; der Batch-Endpunkt lehnt die Option ab.

Ebenfalls akzeptiert: schema (dict, ein frei geformtes Extraktionsschema, das unverändert durchgereicht wird), js_code (str, wird vor der Aufnahme in der Seite ausgeführt), wait_timeout_ms (int), headers (dict[str, str]), cookies (list[BrowserCookie]), auth (HttpBasicAuth), proxy_url (str), geolocation (dict), action_chain (list[dict] mit skriptgesteuerten Klick-, Tipp- und Scroll-Schritten), pdf_options (PdfOptions, angewandt auf die pdf-Ausgabe), mobile (bool) und respect_robots (bool). perceive_batch nimmt denselben Satz entgegen, abgesehen von direct_download.

Artefakt-URLs werden bei jedem Lesen neu signiert, rufe also client.v2.get_perceive_operation(op.operation_id) für eine frische URL auf, statt den String zwischenzuspeichern.

Rohe Bytes, ohne Umweg über eine signierte URL. perceive_direct erzwingt direct_download und reicht dir das Artefakt selbst, mit Metadaten aus den Antwort-Headern.

direct = client.v2.perceive_direct("https://example.com", outputs=["markdown"])
print(direct.filename, direct.content_type, len(direct.content), direct.render_quality)

raw = client.v2.download_perceive_artifact(op.operation_id, output="markdown")

perceive_direct verlangt genau eine artefakterzeugende Ausgabe aus markdown, html_cleaned, html_raw, screenshot, screenshot_full_page, pdf, links und images. structured darf mitfahren, bleibt serverseitig aber inline; übergibst du etwas anderes, löst das SDK EnconvertError aus, bevor es die Anfrage sendet. Bei download_perceive_artifact darf output entfallen, wenn die Operation genau ein Artefakt erzeugt hat, und ein Artefakt jenseits seiner Aufbewahrungsfrist antwortet mit 410.

Batches. Bis zu 1000 URLs teilen sich einen Optionsblock. Kleine Batches werden inline fertig; größere kommen mit dem Status "queued" zurück, frage also die Job-ID ab.

batch = client.v2.perceive_batch(
    ["https://a.example.com", "https://b.example.com"], outputs=["markdown"], output_mode="zip"
)

done = client.v2.get_perceive_batch(batch.job_id)
print(done.status, done.completed, done.failed, done.pending)
for item in done.items:
    print(item.url, item.render_quality)

output_mode ist "manifest" (Standard) oder "zip"; bei "zip" liegt das fertige Bündel unter done.zip.url. Details unter Perceive.

Discover#

Zählt die URLs einer Website ohne Browser-Rendering auf, was den Schritt zum günstigen ersten Zug macht, bevor du überhaupt etwas wahrnimmst.

found = client.v2.discover(
    "https://example.com", mode="hybrid", max_urls=200, exclude_patterns=["/tag/"]
)
print(found.total, found.truncated, found.sources)
Option Typ Standard Beschreibung
mode "sitemap" \| "crawl" \| "hybrid" Server-Standard Sitemap-Auswertung, ein HTTP-Crawl oder beides zusammengeführt und dedupliziert.
max_urls / max_depth int Server-Standard Limit für zurückgegebene URLs (truncated ist True, wenn es mehr gab) und Crawl-Tiefe ab der Start-URL.
same_domain_only bool Server-Standard Bleibt auf dem Start-Host.
respect_robots bool Server-Standard Beachtet robots.txt.

include_patterns und exclude_patterns (beide list[str]) filtern die Ergebnismenge. DiscoverResult.sources enthält die rohen Zählungen pro Quelle vor der Deduplizierung, etwa {"sitemap": 42, "crawl": 30}. Mehr dazu unter Discover.

Lookup#

Führt eine kategorisierte Websuche aus und rendert auf Wunsch die besten Treffer im selben Aufruf.

search = client.v2.lookup(
    "best static site generators", category="web", num_results=10, perceive_top=3
)

for hit in search.results:
    quality = hit.perceive.render_quality if hit.perceive else None
    print(hit.position, hit.title, hit.url, quality)
Option Typ Standard Beschreibung
category "web" \| "news" \| "images" \| "scholar" \| "patents" \| "maps" Server-Standard Suchvertikale.
time_filter "hour" \| "day" \| "week" \| "month" \| "year" -- Aktualitätsfenster.
num_results / page int Server-Standard Treffer pro Seite und die 1-basierte Seitennummer.
perceive_top int 0 Rendert die Top-N-Ergebnis-URLs automatisch. Jeder Treffer bringt sein vollständiges PerceiveResult unter hit.perceive mit.

Ebenfalls akzeptiert: country (str), locale (str), location (str) für ortsabhängige Suchanfragen und autocorrect (bool). LookupResult trägt answer_box, knowledge_graph, perceive_operation_ids und perceive_top, wobei Letzteres meldet, wie viele Treffer tatsächlich gerendert wurden, was weniger sein kann als angefordert. Referenz unter Lookup.

Distill#

Schemagesteuerte strukturierte Extraktion über eine oder viele Seiten. Übergib genau eines von urls oder discover_from; schema ist immer erforderlich. Beide Regeln werden clientseitig geprüft und lösen EnconvertError aus, bevor eine Anfrage rausgeht.

from enconvert import CssField, CssSchema

extraction = client.v2.distill(
    urls=["https://example.com/pricing"],
    schema={"plans": "list of plan names with monthly prices"},
    css_schema=CssSchema(
        base_selector=".plan-card",
        fields=[
            CssField(name="name", type="text", selector="h3"),
            CssField(name="price", type="text", selector=".price"),
        ],
        target_field="plans",
    ),
)

for item in extraction.results:
    print(item.url, item.extraction_tier, item.fields_from_css, item.fields_from_llm, item.data)

Das optionale css_schema läuft zuerst und beantwortet alles, was die Selektoren erreichen. Nur die Felder, die es verfehlt, eskalieren auf die Sprachmodell-Stufe, und extraction_tier meldet bei jedem Eintrag, welcher Pfad gelaufen ist: css, llm, mixed oder none.

Ermitteln und destillieren in einem Aufruf:

from enconvert import DistillDiscoverFrom

client.v2.distill(
    discover_from=DistillDiscoverFrom(url="https://example.com", mode="sitemap", max_pages=10),
    schema={"title": "page title"},
)
Option Typ Standard Beschreibung
schema dict -- (erforderlich) Die Ausgabeform, die du zurückbekommen willst, wird unverändert durchgereicht.
urls list[str] -- Explizite Seitenliste. Schließt discover_from aus.
discover_from DistillDiscoverFrom -- url, optionales mode, optionales max_pages (1 bis 50, Standard 10).
css_schema CssSchema -- Selektor-Durchgang vor jedem Modellaufruf.

Ebenfalls akzeptiert: wait_for (str), wait_timeout_ms (int), headers (dict[str, str]), cookies (list[BrowserCookie]) und respect_robots (bool). CssField unterstützt die Typen text, attribute, html, regex, nested, list und nested_list, mit optionalem attribute, pattern, default, transform (lowercase, uppercase, strip) und verschachtelten fields bis zu fünf Ebenen tief. Siehe Distill.

Ingest#

Verwandelt eine ganze Website, eine explizite URL-Liste oder einen Stapel hochgeladener Dokumente in gechunktes, RAG-fertiges JSONL. Ingest läuft immer asynchron.

import time

from enconvert import IngestChunkOptions

job = client.v2.ingest(
    mode="sitemap",
    url="https://docs.example.com",
    max_pages=100,
    chunk=IngestChunkOptions(max_words=512, sentence_overlap=1),
    webhook_url="https://my.app/hooks/enconvert",
)

status = client.v2.get_ingest_job(job.job_id)
while status.status in ("queued", "discovering", "processing"):
    time.sleep(10)
    status = client.v2.get_ingest_job(job.job_id)

print(status.status, status.pages_processed, status.total_chunks, status.output_url)

Hochgeladene Dateien durchlaufen denselben Job-Lebenszyklus im Modus files. Akzeptiert werden PDF, DOCX, PPTX, XLSX, CSV, HTML, EPUB, TXT und MD sowie alte und ODF-Office-Formate, und jeder Eintrag darf ein Pfad, ein os.PathLike, rohe bytes oder ein FileData sein.

file_job = client.v2.ingest_files(["handbook.pdf", "notes.docx"])
Option Typ Standard Beschreibung
mode "urls" \| "sitemap" \| "crawl" \| "files" "urls" urls braucht eine nicht leere urls-Liste und lehnt url ab. Jeder andere Modus braucht eine Start-url und lehnt urls ab.
url / urls str / list[str] -- Start-URL für sitemap und crawl oder die explizite Seitenliste für den Modus urls.
max_pages int Server-Standard Limit für die Anzahl ingestierter Seiten.
chunk IngestChunkOptions -- max_words 32 bis 4000, Standard 512. sentence_overlap 0 bis 10, Standard 1. Kombiniere es mit webhook_url (str), das aufgerufen wird, sobald der Job einen Endzustand erreicht.

Crawl-Steuerung und Render-Optionen werden ebenfalls akzeptiert: max_depth, same_domain_only, include_patterns, exclude_patterns, respect_robots, wait_for und wait_timeout_ms. ingest_files nimmt nur chunk und webhook_url entgegen.

for summary in client.v2.list_ingest_jobs(limit=20).jobs:
    print(summary.job_id, summary.status, summary.total_chunks)

client.v2.cancel_ingest_job(job.job_id)
client.v2.retry_ingest_webhook(job.job_id)

secret = client.v2.get_webhook_secret()
print(secret.signature_header, secret.signature_scheme, secret.replay_tolerance_seconds)
client.v2.rotate_webhook_secret()

cancel_ingest_job ist idempotent; das Abbrechen eines bereits beendeten Jobs liefert ihn unverändert zurück. retry_ingest_webhook antwortet mit 409, wenn der Job nicht abgeschlossen ist, und mit 400, wenn kein Webhook konfiguriert war. rotate_webhook_secret macht das vorherige Secret sofort ungültig. Ausführlicher unter Ingest.

Watch#

Rendert eine URL in festem Takt neu und meldet dir, wenn sie sich ändert.

watcher = client.v2.create_watcher(
    "https://example.com/pricing",
    frequency_minutes=60,
    diff_mode="auto",
    webhook_url="https://my.app/hooks/changes",
    notify_email=True,
)

for snap in client.v2.get_watcher_snapshots(watcher.watcher_id, limit=10).snapshots:
    print(snap.checked_at, snap.has_changes, snap.similarity, snap.change_count)

client.v2.list_watchers(limit=20)
client.v2.update_watcher(watcher.watcher_id, status="paused")
client.v2.update_watcher(watcher.watcher_id, webhook_url="")
client.v2.delete_watcher(watcher.watcher_id)
Option Typ Standard Beschreibung
frequency_minutes int Server-Standard Prüfintervall. Eine Stunde ist die Untergrenze.
diff_mode "auto" \| "text" \| "structured" \| "tables" \| "metadata" Server-Standard Welche Ebene der Seite die Diff-Engine vergleicht.
track_fields dict -- Benannte Felder zum Verfolgen, unverändert durchgereicht.
webhook_url str -- Wird bei jeder erkannten Änderung aufgerufen.
notify_email bool Server-Standard Verschickt Änderungsbenachrichtigungen per E-Mail.

update_watcher braucht mindestens ein Feld und löst sonst EnconvertError aus; status akzeptiert "active" oder "paused", und ein leeres webhook_url löscht den Webhook. delete_watcher ist ein weicher, idempotenter Löschvorgang, der den stillgelegten Watcher mit Status "deleted" zurückgibt; danach antwortet der Watcher mit 404.

Snapshot-Diffs tragen nicht vertrauenswürdige Seiteninhalte. Die Dicts in WatcherSnapshot.changes stammen direkt von der überwachten Website. Escape sie, bevor du sie in HTML, einen E-Mail-Text oder eine Chat-Nachricht renderst.

Die Diff-Engine und die Benachrichtigungs-Payloads sind unter Watch dokumentiert.


PDF-Optionen#

PdfOptions ist eine eingefrorene Dataclass, die sich convert_url_to_pdf, convert_website_to_pdf, convert_document, convert_to_pdf und die v2.perceive-Familie teilen.

from enconvert import BrowserCookie, HttpBasicAuth, PdfHeaderFooter, PdfMargins, PdfOptions

client.convert_url_to_pdf(
    "https://internal.example.com/report",
    pdf_options=PdfOptions(
        page_size="A4",
        orientation="landscape",
        margins=PdfMargins(top=10, bottom=10, left=15, right=15),
        scale=0.9,
        header=PdfHeaderFooter(content="Quarterly Report", height=15),
        footer=PdfHeaderFooter(content="Confidential", height=12),
    ),
    auth=HttpBasicAuth(username="user", password="pass"),
    cookies=[BrowserCookie(name="session", value="abc123", domain="internal.example.com")],
    save_to="report.pdf",
)
Feld Typ Beschreibung
page_size str "A4", "A3", "Letter", "Legal" und Verwandte.
page_width / page_height float Eigene Seitengeometrie. Gemeinsam gesetzt überschreiben sie page_size.
orientation "portrait" \| "landscape" Standard ist Hochformat.
margins PdfMargins top, bottom, left, right, alle optional.
scale float Render-Skalierung, zum Beispiel 0.9 für 90 Prozent.
grayscale bool Wandelt das PDF per Nachbearbeitung in Graustufen um.
header PdfHeaderFooter content (bis zu 2000 Zeichen) und height.
footer PdfHeaderFooter content (bis zu 2000 Zeichen) und height.

BrowserCookie nimmt name, value und entweder domain oder url entgegen, dazu optional path, expires, http_only, secure und same_site ("Strict", "Lax", "None"). Das SDK bildet http_only und same_site für dich auf ihre camelCase-Wire-Keys ab.


Fehlerbehandlung#

Fehler sind Exception-Klassen, fange sie also mit except ab. Ordne die Handler von spezifisch nach allgemein: AuthenticationError, QuotaError und RateLimitError erben alle von APIError, das wiederum von EnconvertError erbt.

from enconvert import APIError, AuthenticationError, EnconvertError, QuotaError, RateLimitError

try:
    op = client.v2.perceive("https://example.com")
except AuthenticationError:
    print("Invalid or missing API key. Check ENCONVERT_API_KEY.")
except QuotaError as e:
    print(f"402 from the API: {e.message}")
except RateLimitError:
    print("Too many requests. Back off and retry.")
except APIError as e:
    print(f"API error [{e.status_code}]: {e.message}")
except EnconvertError as e:
    print(f"Rejected before the request was sent: {e}")
Klasse Ausgelöst bei Statuscode
AuthenticationError Ungültiger, fehlender oder widerrufener Schlüssel 401, 403
QuotaError HTTP 402 402
RateLimitError Zu viele Anfragen 429
APIError Jeder andere 4xx- oder 5xx-Fehler der tatsächliche Code
EnconvertError Basisklasse sowie clientseitige Validierung, die das Netzwerk nie erreicht --

APIError trägt status_code und message, und sein str() liest sich als [404] Not found. Clientseitige Fehler, auf die du stoßen kannst: ein leerer api_key, eine nicht unterstützte Dateiendung, ein nicht implementiertes Konvertierungspaar, ein distill-Aufruf mit beiden oder keinem von urls und discover_from, eine Diskrepanz zwischen ingest-Modus und Argumenten, ein leeres update_watcher-Payload und eine perceive_direct-Ausgabeliste, die nicht genau ein Artefakt ergibt. Die vollständige Zuordnung der Meldungen steht unter Fehlercodes.


Timeout-Recovery#

Lange URL- und Dokumentkonvertierungen können ein Reverse-Proxy-Timeout überdauern, selbst wenn der Server den Job zu Ende bringt. Das Python-SDK fängt das transparent ab:

  1. Vor jeder Einzeldatei- oder Einzel-URL-Konvertierung erzeugt das SDK einen UUID4-Hex-String und sendet ihn als job_id.
  2. Kommt diese Anfrage mit 5xx zurück, wechselt das SDK alle 3 Sekunden auf Polling von GET /v1/convert/status/{job_id}. Ein 404 dort bedeutet, dass die Job-Zeile noch nicht geschrieben ist, das Polling läuft also weiter.
  3. Bei success liefert das SDK das Ergebnis, als wäre nichts gewesen; bei failed löst es APIError(500, ...) mit der Fehlermeldung des Servers aus; und jenseits der Polling-Frist von 300 Sekunden löst es APIError(504, "Conversion timed out") aus.

Dafür schreibst du keine Zeile Code. Kommt eine Antwort über den Recovery-Pfad, ist ConversionResult.job_id gefüllt, sodass du sie in deinen Logs zuordnen kannst.

Zwei Ausnahmen. convert_website_to_pdf und convert_website_to_screenshot steigen aus, weil eine Website-Einreichung keine eigene Job-Zeile hat. Ein 5xx bedeutet dort, dass die Einreichung selbst fehlgeschlagen ist, und das kommt direkt an die Oberfläche. V2-Endpunkte werden ebenfalls nicht abgefragt; nutze deren eigene Job-IDs mit get_perceive_batch oder get_ingest_job.


Konfiguration#

client = Enconvert(
    api_key=os.environ["ENCONVERT_API_KEY"], timeout=300.0, base_url="https://api.enconvert.com"
)
Option Typ Standard Beschreibung
api_key str -- (erforderlich) Privater API-Schlüssel. Ein leerer Wert löst beim Konstruieren EnconvertError aus.
timeout float 300.0 Timeout pro Anfrage in Sekunden, angewandt auf jeden Aufruf einschließlich Uploads.
base_url str https://api.enconvert.com Basis-URL der API. Schrägstriche am Ende werden entfernt.

Der Client hält eine requests.Session, Verbindungen werden also über Aufrufe hinweg gepoolt; verwende einen Client wieder, statt pro Anfrage einen neuen zu bauen. Dein Schlüssel reist als X-API-Key-Header mit, und save_to-Downloads gehen als schlichtes, unauthentifiziertes GET an die signierte Storage-URL und werden in 64-KB-Blöcken auf die Festplatte gestreamt, der Schlüssel verlässt den API-Host also nie.

Hardcode den API-Schlüssel niemals. Lies ihn aus einer Umgebungsvariable oder deinem Secret-Manager und halte ihn serverseitig. Wer deinen privaten Schlüssel besitzt, kann Arbeit gegen dein Projekt laufen lassen.

Form des Ergebnisses#

Konvertierungsmethoden liefern ein eingefrorenes ConversionResult:

@dataclass(frozen=True)
class ConversionResult:
    presigned_url: str
    object_key: str
    filename: str
    file_size: int | None = None
    conversion_time_seconds: float | None = None
    job_id: str | None = None

Die vorsignierte URL ist kurzlebig. Übergib save_to oder rufe die URL selbst ab und lege die Bytes in deinem eigenen Bucket ab, wenn du dauerhaften Zugriff brauchst.

Typ Zurückgegeben von Wichtige Felder
JobStatus get_job_status status (processing, success, failed), presigned_url, object_key, error
BatchSubmission, BatchStatus convert_website_to_*, get_batch_status, wait_for_batch batch_id, status, url_count, total, completed, failed, discovery_method, zip_download_url, items
PerceiveResult v2.perceive, v2.get_perceive_operation operation_id, render_quality, status_code, deductions, outputs, structured, warnings, cache_hit
PerceiveDirectResult v2.perceive_direct, v2.download_perceive_artifact content (rohe Bytes), content_type, filename, render_quality, source_status_code
IngestJob v2.ingest, v2.ingest_files, v2.get_ingest_job job_id, status, mode, pages_processed, total_chunks, output_url, error_message
Watcher die v2-Watch-Methoden watcher_id, status, frequency_minutes, diff_mode, checks_count, next_check_at, last_change_at

V2-Artefakt-URLs sind 15 Minuten lang signiert und werden bei jedem Lesen der Operation neu signiert, rufe also get_perceive_operation auf, statt einen URL-String zwischenzuspeichern.


Quellcode und Issues#


Häufig gestellte Fragen#

Wie konvertiere ich Dateien in Python?#

Führe pip install enconvert aus, baue einen Client mit Enconvert(api_key=os.environ["ENCONVERT_API_KEY"]) und rufe eine typisierte Methode wie convert_document, convert_image, convert_to_pdf oder convert_to_markdown auf. Übergib save_to="out.pdf", dann streamt das SDK die fertige Datei direkt auf die Festplatte, statt dir eine URL zum Selbstabholen zu geben.

Wie konvertiere ich in Python eine URL nach PDF?#

Rufe client.convert_url_to_pdf("https://example.com", save_to="page.pdf") auf. Setze single_page=False plus pdf_options=PdfOptions(page_size="A4") für paginierte Ausgabe und passe viewport_width, load_media oder enable_scroll an, wenn eine Seite eine breitere Leinwand oder nachgeladene Bilder braucht.

Wie konvertiere ich in Python DOCX nach PDF?#

Entweder client.convert_document("report.docx", save_to="report.pdf"), denn output_format steht ohnehin standardmäßig auf "pdf", oder client.convert_to_pdf("report.docx", save_to="report.pdf"), wenn du die serverseitige Formaterkennung möchtest. Derselbe Aufruf bewältigt XLSX, PPTX, ODT, ODS, ODP, OTS, Pages und Numbers.

Wie konvertiere ich in Python HEIC nach WebP?#

client.convert_image("photo.heic", output_format="webp", save_to="photo.webp"). Das Eingabeformat ergibt sich aus der Dateiendung, und jpeg, png, svg, heic und webp lassen sich alle ineinander umwandeln. Du arbeitest aus dem Speicher statt von der Festplatte? Verpacke die Bytes in FileData(data=blob, filename="photo.heic"), damit die Endung erhalten bleibt.

Wie lese ich eine Webseite in Python als sauberes Markdown aus?#

Nimm client.v2.perceive(url, outputs=["markdown"]) und lies op.outputs["markdown"].url, oder client.v2.perceive_direct(url, outputs=["markdown"]), um die Bytes im Antwortkörper zurückzubekommen. Ergänze only_main_content=True, um Navigation und Fußzeilen zu entfernen. Für eine schlichte Konvertierung ohne Bewertung und Extraktion ist convert_url_to_markdown der leichtere Aufruf.

Was bedeutet render_quality, und wann sollte ich eine Seite erneut abrufen?#

Es ist ein Float zwischen 0.0 und 1.0 bei jedem V2-Lesevorgang, der angibt, wie ehrlich die Seite gerendert hat. Ein Challenge-Screen, eine Cookie-Wand, eine Login-Schranke, eine HTTP-Fehlerseite oder eine leere SPA-Hülle bekommt einen niedrigen Wert und benennt in deductions, was ausgelöst hat, mit Details in warnings und dem eigenen HTTP-Status der Seite in status_code. Nutze ihn als Schranke: Behandle einen niedrigen Wert als Signal, es mit cache_mode="refresh", einem wait_for-Selektor, auf den nur echter Inhalt passt, oder anderen cookies erneut zu versuchen, statt den Text an dein Modell zu geben.

Wie verwandle ich in Python eine ganze Dokumentations-Website in RAG-Chunks?#

Rufe client.v2.ingest(mode="sitemap", url="https://docs.example.com", max_pages=100, chunk=IngestChunkOptions(max_words=512, sentence_overlap=1)) auf. Der Job ist asynchron, frage also entweder get_ingest_job(job_id) ab, bis der Status queued, discovering und processing verlassen hat, oder übergib webhook_url und warte auf den Rückruf. Das fertige JSONL liegt unter output_url. Für lokale Dokumente statt einer Website durchläuft ingest_files dieselbe Pipeline.

Unterstützt das Python-SDK asyncio?#

Nicht von Haus aus. Jede Methode arbeitet synchron und baut auf requests auf. In einer asynchronen Anwendung verpackst du Aufrufe in await asyncio.to_thread(client.convert_to_pdf, "report.docx") oder übergibst sie einem concurrent.futures.ThreadPoolExecutor, damit die Event Loop weiterläuft. Der Client lässt sich gefahrlos über Threads hinweg teilen, weil er eine gepoolte requests.Session hält.

Wie übersteht das SDK ein Proxy-Timeout bei einer langen Konvertierung?#

Jede Einzeldatei- und Einzel-URL-Konvertierung sendet eine erzeugte job_id. Liefert die Anfrage 5xx, fragt das SDK bis zu 300 Sekunden lang alle 3 Sekunden GET /v1/convert/status/{job_id} ab, kehrt normal zurück, sobald der Job success meldet, löst APIError(500, ...) aus, wenn er failed meldet, und löst APIError(504, "Conversion timed out") aus, wenn die Frist verstreicht. Website-Batch-Einreichungen überspringen diesen Pfad, denn ein Fehlschlag bedeutet dort, dass die Einreichung selbst nicht angekommen ist.