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.
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]).
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.
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.
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:
- Vor jeder Einzeldatei- oder Einzel-URL-Konvertierung erzeugt das SDK einen UUID4-Hex-String und sendet ihn als
job_id. - Kommt diese Anfrage mit 5xx zurück, wechselt das SDK alle 3 Sekunden auf Polling von
GET /v1/convert/status/{job_id}. Ein404dort bedeutet, dass die Job-Zeile noch nicht geschrieben ist, das Polling läuft also weiter. - Bei
successliefert das SDK das Ergebnis, als wäre nichts gewesen; beifailedlöst esAPIError(500, ...)mit der Fehlermeldung des Servers aus; und jenseits der Polling-Frist von 300 Sekunden löst esAPIError(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.
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#
- PyPI: enconvert
- GitHub: conversionapi/python-sdk
- Python: 3.9, 3.10, 3.11, 3.12, 3.13
- Lizenz: MIT
- Andere Clients: Alle SDKs
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.