Ruby SDK für Dateikonvertierung#

enconvert ist das offizielle Ruby-Gem für die EnConvert API. Es konvertiert URLs, Bilder und Dokumente über 43 implementierte Konvertierungs-Endpunkte und stellt einen zweiten Namensraum bereit, client.v2, der lebende Webseiten in agentenfertiges Markdown, JSON, Screenshots und PDFs liest. Jeder V2-Lesevorgang trägt einen render_quality-Wert, sodass eine blockierte Seite oder eine leere SPA-Hülle nie als echter Inhalt durchgeht. Das Gem zielt auf Ruby 3.0+ und kommt ohne Laufzeitabhängigkeiten, aufgebaut auf net/http, json und securerandom aus der Standardbibliothek. Antworten kommen als Structs mit snake_case-Lesern zurück.

RubyGems: enconvert · Quelle: conversionapi/ruby-sdk · Ruby: 3.0.0 oder neuer · Laufzeitabhängigkeiten: keine

Installation#

gem install enconvert

Oder füge es deinem Gemfile hinzu:

gem "enconvert"
bundle install

Schnellstart#

require "enconvert"

client = Enconvert::Client.new(api_key: ENV.fetch("ENCONVERT_API_KEY"))

# Eine Live-Seite in ein PDF konvertieren und auf die Festplatte streamen.
result = client.convert_url_to_pdf("https://example.com", save_to: "page.pdf")
puts result.presigned_url

# Dieselbe Seite so lesen, wie es ein Agent tun sollte, mit angehängtem Quality-Score.
op = client.v2.perceive("https://example.com", outputs: %w[markdown structured])
puts op.outputs["markdown"].url, op.render_quality  # z. B. 0.93

Das Gem ist ausschließlich serverseitig. Dein privater API-Key muss auf dem Server bleiben, lies ihn also aus einer Umgebungsvariablen oder einem Secret-Manager und liefere ihn nie an einen Browser oder ein Mobile-Binary aus.


Was der Client bereitstellt#

Enconvert::Client trägt die Datei-Konvertierungs-Oberfläche direkt, und die gesamte Web-Intelligence-Oberfläche hängt an client.v2.

Gruppe Methoden Rückgabe
Einzelne URL convert_url_to_pdf, convert_url_to_screenshot, convert_url_to_markdown ConversionResult
Hochgeladene Datei convert_image, convert_document, convert_to_markdown, convert_to_pdf ConversionResult
Batch für ganze Website convert_website_to_pdf, convert_website_to_screenshot BatchSubmission
Status und Polling get_job_status, get_batch_status, wait_for_batch JobStatus, BatchStatus
Web-Intelligence client.v2.*, 23 Methoden über sechs Fähigkeiten V2-Structs
Format-Helfer Enconvert.valid_outputs_for, Enconvert::IMPLEMENTED_CONVERSIONS Array, Set

Jeder Antworttyp ist ein Struct, erzeugt mit keyword_init: true, du liest Felder also als gewöhnliche Methoden: result.presigned_url, op.render_quality, job.total_chunks.


Datei-Konvertierung#

convert_url_to_pdf#

Rendere jede öffentliche URL zu einem PDF.

result = client.convert_url_to_pdf(
  "https://example.com",
  single_page: false,
  pdf_options: { page_size: "A4", orientation: "landscape" },
  viewport_width: 1440,
  save_to: "report.pdf"
)

puts result.filename, result.file_size
Option Typ Standard Beschreibung
save_to String nil Lokaler Pfad, in den das PDF geladen wird. Übergeordnete Verzeichnisse werden für dich angelegt.
single_page Boolean true true erzeugt eine einzige fortlaufende Seite. false paginiert anhand von pdf_options[:page_size].
pdf_options Hash nil Seitengeometrie. Siehe PDF-Optionen.
viewport_width Integer 1920 Breite des Browser-Viewports in Pixeln.
viewport_height Integer 1080 Höhe des Browser-Viewports in Pixeln.
load_media Boolean true Vor der Erfassung auf Bilder und Videos warten.
enable_scroll Boolean true Von oben nach unten scrollen, damit Lazy-Loader auslösen.
output_filename String nil Überschreibt den generierten Dateinamen.
auth Hash nil HTTP-Basic-Zugangsdaten, zum Beispiel { username: "user", password: "pass" }.
cookies, headers Array, Hash nil Zu setzende Cookies und zusätzliche Request-Header. Alle drei Browser-Zugriffsfelder werden unverändert durchgereicht.
Kombiniere auth nicht mit einem Authorization-Header. Die API lehnt diesen Konflikt ab, entscheide dich also für eines von beiden.

convert_url_to_screenshot#

Erfasse ein ganzseitiges PNG einer beliebigen URL.

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

Akzeptiert dieselben Optionen für Viewport, Medien, Scrollen, Dateiname und Browser-Zugriff wie convert_url_to_pdf. single_page und pdf_options nimmt es nicht.

convert_url_to_markdown#

Extrahiere sauberes GitHub-Flavored Markdown aus jeder URL. Der Konverter entfernt Navigation, Fußzeilen, Werbung und Skripte, behält den Haupttext des Artikels und stellt YAML-Frontmatter mit Titel, Beschreibung, URL, Links und Bildern voran.

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

Nützlich, um eine RAG-Pipeline zu füttern, fremde Inhalte in ein CMS zu importieren oder ein Trainingskorpus aufzubauen.

convert_image#

Konvertiere zwischen jpeg, png, svg, heic und webp in jede Richtung oder rastere ein PDF zu JPEG.

# Von einem Pfad auf der Festplatte.
client.convert_image("photo.heic", output_format: "webp", save_to: "photo.webp")

# Aus Bytes, die du bereits hältst.
bytes = File.binread("photo.heic")
client.convert_image({ data: bytes, filename: "photo.heic" }, output_format: "webp", save_to: "photo.webp")

# Die erste Seite eines PDFs rastern.
client.convert_image("invoice.pdf", output_format: "jpeg", save_to: "invoice.jpg")

Das Eingabeformat wird aus der Dateiendung aufgelöst, ein rohes IO ohne Dateinamen lässt sich hier also nicht verwenden. Übergib stattdessen einen Pfad oder einen { data:, filename: }-Hash.

Option Typ Erforderlich Beschreibung
output_format String Ja Zielformat. Die Aliasse jpg, yml, htm und md werden für dich normalisiert.
save_to String Nein Lokaler Pfad, in den das Ergebnis geladen wird.
output_filename String Nein Überschreibt den generierten Dateinamen.

convert_document#

Konvertiere Dokumente und Datenformate. output_format ist standardmäßig "pdf".

# docx zu pdf
client.convert_document("report.docx", save_to: "report.pdf")

# json zu yaml
client.convert_document("data.json", output_format: "yaml", save_to: "data.yaml")

# markdown zu pdf mit eigener Seiteneinrichtung
client.convert_document(
  "README.md",
  output_format: "pdf",
  pdf_options: { page_size: "A4", margins: { top: 20, bottom: 20, left: 25, right: 25 } },
  save_to: "readme.pdf"
)

Erkannte Eingabe-Erweiterungen: .doc, .docx, .xls, .xlsx, .ppt, .pptx, .html, .htm, .odt, .ods, .odp, .ots, .pages, .numbers, .md, .markdown, .csv, .json, .xml, .yaml, .yml, .toml.

EPUB hat kein eigenes Dokumentpaar. Schicke .epub-Dateien stattdessen durch convert_to_pdf oder convert_to_markdown.

Option Typ Standard Beschreibung
output_format String "pdf" Zielformat.
save_to String nil Lokaler Pfad, in den das Ergebnis geladen wird.
output_filename String nil Überschreibt den generierten Dateinamen.
pdf_options Hash nil Seiteneinrichtung, wird berücksichtigt, wenn die Ausgabe ein PDF ist.

convert_to_markdown#

Konvertiere eine hochgeladene Datei fast jedes Dokumentformats in sauberes Markdown. Das Format wird auf dem Server erkannt.

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

Akzeptierte Eingaben: PDF, DOCX, PPTX, XLSX, CSV, HTML, EPUB, TXT und MD sowie ältere und ODF-Office-Formate. Bilder werden an diesem Endpunkt nicht unterstützt.

Die Ausgabe ist eine einzelne, überschriften-bewusste .md-Datei, was sie zu einem guten Baustein für die RAG-Aufnahme macht: Ein semantischer Chunker kann anhand der Überschriften-Hierarchie des Dokuments trennen statt anhand willkürlicher Zeichenzahlen. Die einzigen Optionen sind save_to: und output_filename:.

convert_to_pdf#

Konvertiere eine hochgeladene Datei fast jedes Formats in ein PDF.

# pptx zu pdf
client.convert_to_pdf("slides.pptx", save_to: "slides.pdf")

# pdf-Durchreichung, auf Graustufen normalisiert
client.convert_to_pdf("scan.pdf", pdf_options: { grayscale: true }, save_to: "gray.pdf")

Akzeptierte Eingaben: Office, ODF, Pages, Numbers, RTF, CSV, HTML, Markdown, reiner Text, Rasterbilder, SVG, EPUB oder ein bestehendes PDF, das durchgereicht und normalisiert wird.

Nur pdf_options[:grayscale] wird hier berücksichtigt. Die übrigen Felder zur Seitengeometrie werden an diesem Endpunkt ignoriert. Wenn du volle Kontrolle über Seitengröße, Ausrichtung und Ränder brauchst, nimm convert_document oder convert_url_to_pdf.

Die Optionen sind save_to:, output_filename: und pdf_options: (nur Graustufen).

convert_website_to_pdf und convert_website_to_screenshot#

Ermittle jede Seite einer Website, konvertiere jede einzelne im Hintergrund und sammle die Ergebnisse als ein einziges ZIP. Beide Methoden arbeiten asynchron und geben sofort ein BatchSubmission zurück.

batch = client.convert_website_to_pdf(
  "https://example.com",
  crawl_mode: "sitemap",
  exclude_patterns: ["/tag/"],
  notification_email: "[email protected]"
)

status = client.wait_for_batch(batch.batch_id, save_to: "site.zip")
puts "#{status.completed} of #{status.total} pages converted"
status.items.each { |item| puts "#{item.status}\t#{item.source_url}" }

convert_website_to_screenshot funktioniert identisch und erzeugt ein ZIP mit PNGs.

Option Typ Beschreibung
crawl_mode String Wie Seiten ermittelt werden, zum Beispiel "sitemap".
include_patterns, exclude_patterns Array Nur URLs crawlen, die zu diesen Mustern passen, oder sie überspringen.
notification_email String E-Mail-Adresse, die benachrichtigt wird, wenn der Batch fertig ist.
callback_url String Webhook, der bei Abschluss aufgerufen wird.
output_filename String Überschreibt den generierten ZIP-Dateinamen.
viewport_width, viewport_height, load_media, enable_scroll Integer, Boolean Render-Verhalten pro Seite. Wird nur gesendet, wenn du es setzt, sonst wendet das Gateway seine eigenen Standardwerte an.
auth, cookies, headers Hash, Array, Hash Browser-Zugriff für geschützte Seiten.
single_page, pdf_options Boolean, Hash Nur für PDF-Batches.

wait_for_batch(batch_id, interval: 5, timeout: 1800, save_to: nil) fragt ab, bis der Batch "processing" verlässt, und gibt dann den finalen BatchStatus zurück. Mit save_to lädt es zusätzlich das ZIP herunter. Es löst Enconvert::APIError mit Status 504 aus, wenn das Timeout erreicht wird, und mit Status 500, wenn ein Batch ohne speicherbares ZIP endet.

get_job_status#

Frage einen einzelnen Konvertierungsjob per ID ab.

status = client.get_job_status("job_abc123")
case status.status
when "success" then puts status.presigned_url
when "failed"  then warn status.error
else                puts "still processing"
end

Gibt einen JobStatus mit status, presigned_url, object_key und error zurück. Du musst ihn selten selbst aufrufen, weil das SDK bereits für dich abfragt. Siehe Timeout-Recovery.

Unterstützte Konvertierungen#

Das Gem trägt die vollständige Tabelle der implementierten {input}-to-{output}-Endpunkte und prüft jedes Paar lokal, ein nicht unterstütztes Paar löst also Enconvert::Error aus, bevor eine Anfrage deinen Prozess verlässt.

Enconvert.valid_outputs_for("json")      # => ["csv", "toml", "xml", "yaml"]
Enconvert.valid_outputs_for("pdf")       # => ["jpeg"]
Enconvert::IMPLEMENTED_CONVERSIONS.size  # => 43
Eingabe Ausgaben
json csv, toml, xml, yaml
xml csv, json
yaml json
csv json, xml
toml json
markdown html, pdf
html pdf
doc, excel, ppt, odt, ods, odp, ots, pages, numbers pdf
jpeg, png, svg, heic, webp untereinander, alle 20 Paare
pdf jpeg

Das sind 43 Endpunkte: 13 für strukturierten Text, 9 für Dokumente und 21 für Bildkonvertierungen.


Web-Intelligence (V2)#

client.v2 verwandelt lebende Webseiten in agentenfertige Daten: rendern, aufzählen, suchen, extrahieren, aufnehmen und überwachen. Jedes Rendering trägt render_quality, einen Wert von 0.0 bis 1.0, der an jedem Lesevorgang hängt. Ein niedriger Wert bedeutet, dass die Seite nicht sauber gerendert wurde, typischerweise eine Challenge-Seite, eine Cookie-Wall, ein Login-Gate oder eine leere JavaScript-Hülle. Der Inhalt wird trotzdem zurückgegeben, aber markiert, zusammen mit einem warnings-Array und einem deductions-Hash, der sagt, welche Prüfungen angeschlagen haben. Lies den Wert, bevor du den Inhalt irgendwo ablegst, dann gelangt ein schlechter Lesevorgang nie unbemerkt in den Kontext deines Agenten.

Alle V2-Endpunkte verlangen einen privaten API-Key. Die Referenz auf Endpunktebene findest du in der V2-Übersicht.

Perceive#

Rendere eine URL in genau die Artefakte, die du anforderst. perceive arbeitet synchron und gibt die abgeschlossene Operation mit frisch signierten Artefakt-URLs zurück.

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

puts op.operation_id, op.render_quality          # "per_...", dann 0.0 bis 1.0
puts op.outputs["markdown"].url                  # signierte Artefakt-URL
puts op.outputs["markdown"].expires_in           # Sekunden, bis sie nicht mehr funktioniert
puts op.structured, op.warnings.inspect          # einfacher Ruby-Hash, dann Array of String

Signiere die Artefakt-URLs einer früheren Operation jederzeit neu:

again = client.v2.get_perceive_operation(op.operation_id)

Perceive bis zu 1000 URLs mit einem gemeinsamen Options-Block. Kleine Batches laufen inline und kommen abgeschlossen zurück. Größere kommen mit dem Status "queued" zurück, frage sie also per job_id ab:

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

done = client.v2.get_perceive_batch(batch.job_id)
puts "#{done.completed}/#{done.total} complete, #{done.failed} failed"
puts done.zip&.url

Überspringe den Umweg über die signierte URL vollständig mit perceive_direct, wo der HTTP-Response-Body das Artefakt selbst ist:

direct = client.v2.perceive_direct("https://example.com", outputs: %w[markdown])
puts direct.filename, direct.content_type, direct.content.bytesize
File.binwrite("page.md", direct.content)

# Ein gespeichertes Artefakt einer früheren Operation erneut herunterladen.
raw = client.v2.download_perceive_artifact(op.operation_id, output: "markdown")

perceive_direct braucht genau eine artefakterzeugende Ausgabe und löst andernfalls lokal Enconvert::Error aus. structured darf mitfahren, weil es serverseitig inline bleibt und nie gestreamt wird. download_perceive_artifact nimmt ein optionales output:, das du weglassen kannst, wenn die Operation genau ein Artefakt erzeugt hat, und benennen musst, wenn sie mehr als eines erzeugt hat; es gibt 410 zurück, sobald ein Artefakt seine Aufbewahrungsfrist überschritten hat.

Option Typ Beschreibung
outputs Array Beliebige aus markdown, html_cleaned, html_raw, screenshot, screenshot_full_page, pdf, links, images, structured. Standard serverseitig ["markdown", "structured"].
extract Array Beliebige aus tables, prices, contacts, metadata, main_content, headings, structured_data, technologies, all.
schema Hash Feldbeschreibungen für die strukturierte Extraktion.
wait_for, wait_timeout_ms String, Integer CSS-Selektor, auf den vor der Erfassung gewartet wird, und die Obergrenze für dieses Warten.
js_code String JavaScript, das vor der Erfassung in der Seite ausgeführt wird.
viewport, mobile Hash, Boolean Viewport-Maße und ob mit einem Mobile-Profil gerendert wird.
headers, cookies, auth Hash, Array, Hash Browser-Zugriff für geschützte Seiten.
proxy_url String Leitet das Rendering über deinen eigenen Proxy.
geolocation Hash Vorgetäuschte Geolokation für den Browser-Kontext.
action_chain Array Skriptgesteuerte Klicks, Scrolls und Eingaben vor der Erfassung.
cache_mode String enabled, bypass oder refresh.
pdf_options Hash Seitengeometrie, wenn pdf unter den Ausgaben ist.
block_resources Array Ressourcentypen, die blockiert werden, zum Beispiel image, font, script.
respect_robots Boolean Berücksichtigt robots.txt.
only_main_content Boolean Entfernt Navigation und Beiwerk aus der Markdown-Ausgabe.
direct_download Boolean Nur von perceive akzeptiert. perceive_batch lehnt es ab.

Die vollständige Parameter-Semantik findest du unter Perceive und Parameter und Optionen.

Discover#

Zähle die URLs einer Website auf, ohne irgendetwas zu rendern. Es ist kein Browser beteiligt, daher ist es schnell und günstig.

found = client.v2.discover(
  "https://example.com",
  mode: "hybrid",            # "sitemap", "crawl" oder "hybrid"
  max_urls: 200,
  max_depth: 3,
  exclude_patterns: ["/tag/"],
  same_domain_only: true,
  respect_robots: true
)

puts found.total, found.urls.first(10)
puts found.truncated        # true, wenn max_urls das Ergebnis begrenzt hat
puts found.sources.inspect  # woher jede URL stammt

Siehe Discover für das Verhalten je Modus.

Lookup#

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

search = client.v2.lookup(
  "best static site generators",
  category: "web",           # web, news, images, scholar, patents, maps
  num_results: 10,
  time_filter: "month",      # hour, day, week, month, year
  perceive_top: 3
)

search.results.each do |hit|
  puts hit.position, hit.title, hit.url
  puts hit.perceive&.render_quality  # vorhanden bei automatisch gerenderten Treffern
end

puts search.answer_box.inspect, search.knowledge_graph.inspect

Mit perceive_top: 3 werden die ersten drei Treffer-URLs gerendert und tragen ein vollständiges PerceiveResult inline auf hit.perceive. Ihre Operations-IDs werden zusätzlich in search.perceive_operation_ids gesammelt. Mehr dazu unter Lookup.

Distill#

Schemagesteuerte strukturierte Extraktion. Gib genau eines von urls: oder discover_from: an, und schema: ist immer erforderlich. Das SDK löst lokal Enconvert::Error aus, wenn du das falsch machst.

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

item = extraction.results.first
puts item.data.inspect
puts item.extraction_tier   # "css", "llm", "mixed" oder "none"
puts item.fields_from_css, item.fields_from_llm

Das optionale css_schema läuft zuerst und beantwortet alles, was es allein über Selektoren schafft. Nur die Felder, die es verfehlt, steigen in die Sprachmodell-Stufe auf, weshalb fields_from_css und fields_from_llm getrennt gemeldet werden.

Ermitteln und destillieren in einem Aufruf:

client.v2.distill(
  discover_from: { url: "https://example.com", mode: "sitemap", max_pages: 10 },
  schema: { title: "page title", summary: "one-line summary" }
)

Die CSS-Feldtypen sind text, attribute, html, regex, nested, list und nested_list. Verschachtelte Feldlisten werden rekursiv serialisiert. Siehe Distill.

Ingest#

Verwandle eine ganze Website oder einen Satz hochgeladener Dokumente über eine einzige Pipeline in gechunktes, RAG-fertiges JSONL. Ingest arbeitet immer asynchron.

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

status = client.v2.get_ingest_job(job.job_id)
puts status.status                                   # queued, discovering, processing, completed, failed, canceled
puts status.pages_processed, status.total_chunks
puts status.output_url if status.status == "completed"  # JSONL

Der Modus "urls" nimmt eine urls:-Liste und lehnt url: ab. Jeder andere Modus nimmt eine Start-url: und lehnt urls: ab. Beide Regeln werden lokal erzwungen, bevor die Anfrage gesendet wird.

Dateien hochladen statt crawlen:

file_job = client.v2.ingest_files(
  ["handbook.pdf", "notes.docx"],
  chunk: { max_words: 512, sentence_overlap: 1 }
)

ingest_files akzeptiert PDF, DOCX, PPTX, XLSX, CSV, HTML, EPUB, TXT und MD sowie ältere und ODF-Office-Formate. Jeder Eintrag kann ein Pfad, ein IO-artiges Objekt oder ein { data:, filename: }-Hash sein.

Jobs und Webhook-Zustellung verwalten:

list = client.v2.list_ingest_jobs(limit: 20)
list.jobs.each { |j| puts "#{j.job_id}\t#{j.status}\t#{j.total_chunks}" }
puts list.has_more

client.v2.cancel_ingest_job(job.job_id)   # idempotent

secret = client.v2.get_webhook_secret
puts secret.secret, secret.signature_header, secret.timestamp_header,
     secret.signature_scheme, secret.replay_tolerance_seconds

client.v2.rotate_webhook_secret            # alte Signaturen verifizieren sofort nicht mehr
client.v2.retry_ingest_webhook(job.job_id) # Webhook eines abgeschlossenen Jobs erneut zustellen

retry_ingest_webhook gibt ein WebhookRetryResult mit delivered, attempts, status_code und detail zurück. Es antwortet 409, wenn der Job nicht abgeschlossen ist, und 400, wenn für den Job kein Webhook konfiguriert ist. Siehe Ingest.

Watch#

Rendere eine URL in festem Takt erneut und lass dich benachrichtigen, wenn sie sich ändert.

watcher = client.v2.create_watcher(
  "https://example.com/pricing",
  frequency_minutes: 60,     # Untergrenze eine Stunde
  diff_mode: "auto",         # auto, text, structured, tables, metadata
  webhook_url: "https://my.app/hooks/changes",
  notify_email: true
)

puts watcher.watcher_id, watcher.next_check_at

history = client.v2.get_watcher_snapshots(watcher.watcher_id, limit: 10)
history.snapshots.each do |snap|
  puts snap.checked_at, snap.has_changes, snap.similarity, snap.change_count, snap.changes.inspect
end

client.v2.list_watchers(limit: 20)
client.v2.get_watcher(watcher.watcher_id)
client.v2.update_watcher(watcher.watcher_id, status: "paused")
client.v2.update_watcher(watcher.watcher_id, webhook_url: "")  # löscht den Webhook
client.v2.delete_watcher(watcher.watcher_id)                   # Soft Delete, idempotent

update_watcher löst Enconvert::Error aus, wenn du es ohne zu aktualisierende Felder aufrufst. delete_watcher gibt den als gelöscht markierten Watcher mit Status "deleted" zurück, und ein gelöschter Watcher liest sich danach als 404. Die vollständige Semantik steht unter Watch.


PDF-Optionen#

pdf_options ist ein einfacher Hash mit Symbol-Schlüsseln, akzeptiert von convert_url_to_pdf, convert_website_to_pdf, convert_document, convert_to_pdf (nur Graustufen) und client.v2.perceive, wenn pdf unter den Ausgaben ist. Nur die Schlüssel, die du setzt, gehen über die Leitung.

client.convert_url_to_pdf(
  "https://example.com",
  pdf_options: { page_size: "A4", orientation: "landscape", scale: 0.9,
                 margins: { top: 10, bottom: 10, left: 15, right: 15 } },
  save_to: "report.pdf"
)
Schlüssel Typ Beschreibung
page_size String "A4", "A3", "Letter", "Legal" und Ähnliches.
page_width Number Explizite Seitenbreite als Alternative zu page_size.
page_height Number Explizite Seitenhöhe.
orientation String "portrait" oder "landscape".
margins Hash { top:, bottom:, left:, right: }, alle optional.
scale Number Render-Skalierung, zum Beispiel 0.9 für 90 Prozent.
grayscale Boolean Wandelt das PDF nachträglich in Graustufen um.
header Hash Kopfzeilentext je Seitenbereich.
footer Hash Fußzeilentext je Seitenbereich.

Fehlerbehandlung#

Jeder Fehler ist ein Enconvert::Error oder eine seiner Unterklassen, rescue-Klauseln können also so breit oder so eng sein, wie du willst. Clientseitige Validierungsfehler, etwa ein nicht unterstütztes Konvertierungspaar oder ein fehlendes schema, lösen die Basisklasse Enconvert::Error aus, bevor eine HTTP-Anfrage gestellt wird.

begin
  client.v2.perceive("https://example.com")
rescue Enconvert::AuthenticationError
  warn "Invalid or missing API key"
rescue Enconvert::QuotaError
  warn "Request was rejected with 402"
rescue Enconvert::RateLimitError
  warn "Too many requests, back off and retry"
rescue Enconvert::APIError => e
  warn "API error [#{e.status_code}]: #{e.message}"
rescue Enconvert::Error => e
  warn "Client-side validation failed: #{e.message}"
end
Klasse Ausgelöst bei Statuscode
Enconvert::AuthenticationError Ungültiger, fehlender oder widerrufener API-Key 401, 403
Enconvert::QuotaError Ausgelöst bei HTTP 402 402
Enconvert::RateLimitError Zu viele Anfragen 429
Enconvert::APIError Jede andere Antwort ab 400 der tatsächliche Code
Enconvert::Error Basisklasse, plus alle lokalen Validierungsfehler keiner

APIError#status_code liefert dir den HTTP-Status, und die Meldung wird aus dem Feld detail oder error des Response-Body gezogen, wenn dieser JSON ist. Die vollständige Zuordnung der Meldungen steht unter Fehlercodes.


Timeout-Recovery#

Ein langes URL-zu-PDF-Rendering oder eine große Dokumentkonvertierung kann das Reverse-Proxy-Timeout überdauern, selbst wenn die Konvertierung auf dem Server gelingt. Das SDK fängt das transparent auf, ohne Code auf deiner Seite.

  1. Vor jeder Konvertierungsanfrage erzeugt der Client eine 32 Zeichen lange Job-ID und sendet sie als job_id im Body oder im Multipart-Formular.
  2. Kommt die Anfrage mit einem Status ab 500 zurück, wechselt der Client dazu, GET /v1/convert/status/{job_id} alle 3 Sekunden abzufragen. Ein 404, während die Job-Zeile noch geschrieben wird, wird ignoriert, und das Polling läuft weiter.
  3. Sobald der Job success meldet, wird das Ergebnis zurückgegeben. Sobald er failed meldet, wird Enconvert::APIError mit Status 500 und der Fehlermeldung des Servers ausgelöst.
  4. Die Polling-Frist beträgt 300 Sekunden. Danach löst der Client Enconvert::APIError mit Status 504 und der Meldung Conversion timed out aus.

Die vom Client erzeugte job_id wird außerdem in jede erfolgreiche Antwort übernommen, result.job_id ist also immer gefüllt, und du kannst get_job_status später selbst abfragen, wenn du möchtest.

Website-Batch-Übermittlungen nehmen nicht teil. convert_website_to_pdf und convert_website_to_screenshot erzeugen keine Job-Zeile, ein 5xx bedeutet dort also, dass die Übermittlung selbst fehlgeschlagen ist, und wird sofort gemeldet statt abgefragt.

Konfiguration#

client = Enconvert::Client.new(
  api_key: ENV.fetch("ENCONVERT_API_KEY"),
  timeout: 300,
  base_url: "https://api.enconvert.com"
)
Option Typ Standard Beschreibung
api_key String erforderlich Dein privater API-Key. Ein nil-Wert oder ein leerer Wert löst beim Erzeugen Enconvert::Error aus.
timeout Integer 300 Open- und Read-Timeout in Sekunden, angewendet auf jede Anfrage.
base_url String "https://api.enconvert.com" Basis-URL der API. Abschließende Schrägstriche werden entfernt. Überschreibe sie für ein selbst gehostetes Gateway.

Anfragen authentifizieren sich mit dem Header X-API-Key. Wenn du save_to übergibst, umgeht der Download diesen Header bewusst, denn die vorsignierte Speicher-URL darf deinen API-Key nicht erhalten.

Schreibe den API-Key niemals fest in den Code. Lies ihn aus einer Umgebungsvariablen, aus Rails Credentials oder aus deinem Secret-Manager. Wer deinen privaten Key hat, kann Konvertierungen in deinem Namen ausführen. Rotiere ihn im Dashboard, falls er durchsickert.

Ergebnisform#

Jede Konvertierung einer einzelnen Datei oder einer einzelnen Seite gibt ein Enconvert::ConversionResult zurück:

result = client.convert_document("report.docx", save_to: "report.pdf")

result.presigned_url            # signierte URL für die konvertierte Ausgabe
result.object_key               # Objekt-Key im Speicher
result.filename                 # serverseitiger Dateiname
result.file_size                # Bytes oder nil
result.conversion_time_seconds  # Sekunden oder nil
result.job_id                   # wird immer vom Client gefüllt

Die übrigen V1-Structs sind JobStatus (status, presigned_url, object_key, error), BatchSubmission (batch_id, status, url_count, total_discovered, discovery_method, output_format), BatchStatus (batch_id, status, total, completed, failed, in_progress, output_mode, zip_download_url, items) und BatchItem (source_url, status, download_url, output_file_size, duration).

Auf der V2-Seite ist PerceiveResult das entscheidende:

op.operation_id                              # "per_..."
op.status                                    # queued, processing, completed, failed
op.url, op.url_final, op.content_hash
op.render_quality, op.cache_hit              # 0.0 bis 1.0 (oder nil) und ob es aus dem Cache kam
op.outputs                                   # Hash aus Name => V2OutputArtifact
op.structured, op.extraction_tier            # Hash (oder nil) und wie extrahiert wurde
op.tokens.input, op.tokens.output, op.cost_cents, op.duration_ms
op.error, op.warnings                        # String oder nil, und Array of String
op.status_code                               # HTTP-Status der Ursprungsseite
op.deductions, op.options_echo               # warum render_quality sank, und die verwendeten Optionen

Ein V2OutputArtifact trägt url, object_key, size_bytes, content_type und expires_in (standardmäßig 900 Sekunden), signierte Artefakt-URLs sind also kurzlebig. Signiere sie mit get_perceive_operation neu oder lade die Bytes mit download_perceive_artifact herunter. Von dir gelieferte Payloads wie Schemata, extrahierte Daten, überwachte Felder und Diff-Änderungen werden unverändert als einfache Ruby-Hashes durchgereicht.

Vorsignierte URLs aus V1-Konvertierungen sind ebenfalls temporär. Übergib save_to, wenn du die Bytes auf der Festplatte willst, oder kopiere sie für dauerhafte Ablage in deinen eigenen Bucket.


Quelle und Issues#


Häufig gestellte Fragen#

Wie konvertiere ich Dateien in Ruby mit einem Gem?#

Führe gem install enconvert aus, baue einen Client mit Enconvert::Client.new(api_key: ENV.fetch("ENCONVERT_API_KEY")) und rufe eine Methode wie convert_document, convert_image oder convert_url_to_pdf auf. Übergib save_to:, und das SDK lädt das Ergebnis für dich an diesen Pfad und legt dabei übergeordnete Verzeichnisse an.

Wie konvertiere ich DOCX in Ruby in ein PDF?#

client.convert_document("report.docx", save_to: "report.pdf"). Das Ausgabeformat ist standardmäßig "pdf", du brauchst output_format: also nur, wenn du etwas anderes willst. Derselbe Aufruf funktioniert für .doc, .xls, .xlsx, .ppt, .pptx, .odt, .ods, .odp, .ots, .pages und .numbers.

Wie konvertiere ich eine URL in Ruby in ein PDF?#

client.convert_url_to_pdf("https://example.com", save_to: "page.pdf"). Setze single_page: false, um zu paginieren statt eine fortlaufende Seite zu erzeugen, und übergib pdf_options: für Seitengröße, Ausrichtung, Ränder und Skalierung. Für eine Seite hinter HTTP-Basic-Auth ergänze auth: { username: ..., password: ... }.

Wie konvertiere ich HEIC in Ruby in WebP?#

client.convert_image("photo.heic", output_format: "webp", save_to: "photo.webp"). Das Eingabeformat ergibt sich aus der Dateiendung, und das Gem konvertiert frei zwischen jpeg, png, svg, heic und webp. Mit output_format: "jpeg" rastert es außerdem ein PDF zu JPEG.

Wie scrape ich aus Ruby eine Webseite in Markdown?#

Es gibt zwei Möglichkeiten. client.convert_url_to_markdown(url, save_to: "article.md") liefert dir sauberes GitHub-Flavored Markdown mit YAML-Frontmatter. client.v2.perceive(url, outputs: %w[markdown]) liefert denselben Inhalt plus einen render_quality-Wert, Warnungen und optionale strukturierte Extraktion, und genau das willst du, wenn ein Agent das Ergebnis lesen soll.

Was bedeutet render_quality und warum sollte ich es prüfen?#

Es ist ein Wert von 0.0 bis 1.0, der an jedem V2-Lesevorgang hängt und widerspiegelt, wie sauber die Seite tatsächlich gerendert wurde. Eine Challenge-Seite, eine Cookie-Wall, ein Login-Gate, eine HTTP-Fehlerseite oder eine leere JavaScript-Hülle erhalten alle einen niedrigen Wert. Der Inhalt wird trotzdem zurückgegeben, damit du ihn prüfen kannst, und warnings und deductions erklären den Wert. Mach ihn zur Bedingung, bevor du den Text speicherst oder an ein Modell gibst.

Wiederholt das Ruby SDK den Versuch, wenn eine lange Konvertierung das Timeout erreicht?#

Ja. Jede Konvertierungsanfrage trägt eine vom Client erzeugte job_id, und wenn die Anfrage mit einem Status ab 500 scheitert, fragt das SDK GET /v1/convert/status/{job_id} alle 3 Sekunden für bis zu 300 Sekunden ab. Es gibt das Ergebnis zurück, sobald der Job gelingt, und löst Enconvert::APIError mit Status 504 aus, wenn die Frist verstreicht. Batch-Übermittlungen für ganze Websites sind ausgenommen, weil sie keine Job-Zeile haben.

Was passiert, wenn ich ein Konvertierungspaar anfordere, das die API nicht implementiert?#

Das Gem löst lokal Enconvert::Error aus, noch vor jedem Netzwerkaufruf, und die Meldung listet die gültigen Ausgaben für diese Eingabe auf. Dieselbe Tabelle kannst du selbst mit Enconvert.valid_outputs_for("json") prüfen, was ["csv", "toml", "xml", "yaml"] zurückgibt.

Kann ich das enconvert-Gem in Rails oder in einem Background-Job verwenden?#

Ja, und ein Background-Job ist der richtige Ort dafür. Das Gem ist reines net/http ohne Laufzeitabhängigkeiten und ohne globalen Zustand, eine Enconvert::Client-Instanz lässt sich also gefahrlos pro Job bauen oder pro Prozess memoisieren. Lange Konvertierungen blockieren den aufrufenden Thread bis zum konfigurierten timeout, das standardmäßig bei 300 Sekunden liegt, halte sie also aus dem Web-Request-Zyklus heraus.

Welche Ruby-Versionen unterstützt das SDK?#

Ruby 3.0.0 und neuer, wie über required_ruby_version in der Gemspec deklariert. Es gibt keine Laufzeitabhängigkeiten, es installiert sich also in jede Bundler-Gruppe, ohne einen Abhängigkeitsbaum nach sich zu ziehen.

Wie verifiziere ich die Signatur eines Ingest-Webhooks?#

Rufe client.v2.get_webhook_secret auf, was das Signier-Secret des Projekts bei der ersten Nutzung erzeugt und es zusammen mit signature_header, timestamp_header, signature_scheme und replay_tolerance_seconds zurückgibt. Berechne den HMAC über den zugestellten Body und vergleiche ihn mit dem Signatur-Header. Nutze rotate_webhook_secret, um das alte Secret ungültig zu machen, und retry_ingest_webhook(job_id), um die Benachrichtigung eines abgeschlossenen Jobs erneut zuzustellen.