Website zu PDF API#
Der Endpunkt POST /v1/convert/website-to-pdf crawlt eine ganze Website (per Sitemap-Parsing oder einem vollständigen Breadth-First-Crawl), konvertiert jede gefundene Seite in ein hochwertiges PDF und bündelt die Ergebnisse in einem einzigen ZIP-Archiv. Jobs laufen immer asynchron: Die API liefert sofort HTTP 202 mit einer batch_id, der Abschluss wird über Batch-Status-Polling, einen Webhook-Callback oder eine E-Mail-Benachrichtigung signalisiert, und die Batch-Status-Antwort enthält eine vorsignierte Download-URL für das fertige ZIP. Erfordert einen kostenpflichtigen Plan und einen privaten API-Key.
Endpunkt#
POST /v1/convert/website-to-pdf
Content-Type: application/json
Ausgabeformat: ZIP-Archiv mit einer PDF-Datei pro gefundener Seite.
Modus: Immer asynchron. Liefert sofort HTTP 202.
Authentifizierung#
Dieser Endpunkt erfordert einen privaten API-Key. Öffentliche Keys werden für die Website-Erfassung nicht unterstützt.
X-API-Key: sk_your_private_key
Request-Parameter#
Website-Erkennungsparameter#
| Parameter | Typ | Erforderlich | Standard | Beschreibung | Plan-Freigabe |
|---|---|---|---|---|---|
url |
string |
Ja | -- | Die Basis-URL der Website (z. B. https://example.com). Wird als Wurzel für die Seitenerkennung verwendet. |
-- |
crawl_mode |
string |
Nein | "auto" |
Methode zur URL-Erkennung. Einer von "auto", "sitemap" oder "full". Siehe Crawl-Modi weiter unten. |
Sitemap erfordert Indie+, Full erfordert Studio+ |
include_patterns |
string[] |
Nein | null |
Regex-Muster zum Whitelisten gefundener URLs. Wird nur im full-Crawl-Modus verwendet. |
-- |
exclude_patterns |
string[] |
Nein | Systemstandards | Regex-Muster zum Blacklisten von URLs. Wird nur im full-Crawl-Modus verwendet. Wenn weggelassen, werden integrierte Standardwerte verwendet, die statische Assets, Login-/Admin-/Warenkorb-Seiten und tiefe Paginierung ausschließen. |
-- |
Benachrichtigungsparameter#
| Parameter | Typ | Erforderlich | Standard | Beschreibung | Plan-Freigabe |
|---|---|---|---|---|---|
output_filename |
string |
Nein | Automatisch generiert | Benutzerdefinierter Basisname für die ausgegebene ZIP-Datei. Der Zeitstempel wird automatisch angehängt. | -- |
notification_email |
string |
Nein | E-Mail des Projektinhabers | E-Mail-Adresse, die bei Abschluss des Jobs benachrichtigt wird. | -- |
callback_url |
string |
Nein | -- | Webhook-URL, die bei Abschluss einen POST-Request erhält. | Erfordert Webhook-Zugriff |
Browser- und Rendering-Parameter#
Diese Einstellungen gelten für die Konvertierung jeder einzelnen Seite innerhalb der Website.
| Parameter | Typ | Erforderlich | Standard | Beschreibung | Plan-Freigabe |
|---|---|---|---|---|---|
viewport_width |
integer |
Nein | 1920 |
Breite des Browser-Viewports in Pixeln. | -- |
viewport_height |
integer |
Nein | 1080 |
Höhe des Browser-Viewports in Pixeln. | -- |
single_page |
boolean |
Nein | true |
true rendert jede Seite als eine durchgehende PDF-Seite. false erzeugt paginierte Ausgabe anhand der Seitengröße aus pdf_options. |
-- |
load_media |
boolean |
Nein | true |
Wartet vor der Konvertierung, bis alle Bilder und Videos vollständig geladen sind. | -- |
enable_scroll |
boolean |
Nein | true |
Scrollt jede Seite, um Lazy-Loading-Inhalte auszulösen. | -- |
handle_sticky_header |
boolean |
Nein | true |
Erkennt Sticky-/Fixed-Header und behandelt sie vor der Erfassung. | -- |
handle_cookies |
boolean |
Nein | true |
Blendet Cookie-Consent-Banner automatisch aus. | -- |
wait_for_images |
boolean |
Nein | true |
Wartet, bis alle <img>-Elemente fertig geladen sind. |
-- |
wait_for_selector |
string |
Nein | null |
CSS-Selektor, auf den vor der Aufnahme gewartet wird, angewendet auf jede Seite. Gibt 422 zurück, wenn er nicht innerhalb von wait_for_selector_timeout erscheint. Nützlich für SPAs, die Inhalte nach dem Laden hydrieren. |
-- |
wait_for_selector_timeout |
integer |
Nein | 10000 |
Millisekunden, die auf wait_for_selector gewartet wird (maximal 60000). |
-- |
block_ads |
boolean |
Nein | false |
Bricht Anfragen an bekannte Werbe-/Tracker-Domains ab, sodass diese nie geladen, gerendert werden oder die Aufnahme verlangsamen. | -- |
block_media |
boolean |
Nein | false |
Bricht Bild- und Audio-/Videoanfragen vollständig ab, für ein schnelleres, leichteres Rendering. Anders als load_media (das nur das Warten steuert), verhindert dies das Herunterladen von Medien vollständig. |
-- |
Authentifizierung und benutzerdefinierte Requests#
| Parameter | Typ | Erforderlich | Standard | Beschreibung | Plan-Freigabe |
|---|---|---|---|---|---|
auth |
object |
Nein | null |
HTTP-Basic-Auth-Zugangsdaten, die auf jede Seite angewendet werden. Format: {"username": "...", "password": "..."}. |
Erfordert Basic-Auth-Zugriff |
cookies |
array |
Nein | null |
Array von Cookie-Objekten, die vor jedem Seitenaufruf injiziert werden. Maximal 50 Cookies. | Erfordert Basic-Auth-Zugriff |
headers |
object |
Nein | null |
Benutzerdefinierte HTTP-Header, die mit jedem Request gesendet werden. Maximal 20 Header. | Erfordert Basic-Auth-Zugriff |
PDF-Optionen#
Übergib diese innerhalb eines pdf_options-Objekts. Sie gelten für jede Seite der Website.
| Parameter | Typ | Standard | Beschreibung |
|---|---|---|---|
page_size |
string |
"A4" |
Benannte Seitengröße. Wird ignoriert, wenn sowohl page_width als auch page_height gesetzt sind. |
page_width |
float |
null |
Benutzerdefinierte Seitenbreite in Millimetern. page_width und page_height müssen gemeinsam gesetzt werden. |
page_height |
float |
null |
Benutzerdefinierte Seitenhöhe in Millimetern. |
orientation |
string |
"portrait" |
"portrait" oder "landscape". |
margins |
object |
{"top": 10, "bottom": 10, "left": 10, "right": 10} |
Seitenränder in Millimetern. |
scale |
float |
1.0 |
Skalierungsfaktor für den Inhalt. Bereich: 0.1 bis 2.0. Nur im paginierten Modus. |
grayscale |
boolean |
false |
Konvertiert jede PDF-Seite in Graustufen. |
header |
object |
null |
Seitenkopf für den paginierten Modus. Format: {"content": "<html>", "height": 15}. Unterstützt Template-Variablen: {{page}}, {{total_pages}}, {{date}}, {{title}}, {{url}}. |
footer |
object |
null |
Seitenfuß für den paginierten Modus. Gleiches Format wie beim Header. |
Unterstützte Seitengrößen: A0, A1, A2, A3, A4, A5, A6, B0, B1, B2, B3, B4, B5, Letter, Legal, Tabloid, Ledger
Crawl-Modi#
"auto" (Standard)#
Verwendet den höchsten Crawl-Modus, den dein Plan erlaubt. Wenn dein Plan vollständiges Crawling unterstützt, wird ein vollständiger Crawl ausgeführt. Wenn dein Plan nur Sitemap unterstützt, wird die Sitemap-Erkennung ausgeführt.
"sitemap"#
Erkennt Seiten, indem die sitemap.xml der Website geparst wird:
- Ruft
{base_url}/sitemap.xmlab (30-Sekunden-Timeout) - Wenn das Root-Element
<sitemapindex>ist, werden alle untergeordneten Sitemaps rekursiv abgerufen - Extrahiert alle
<url><loc>-Einträge aus<urlset>-Elementen - Gibt die vollständige Liste der gefundenen URLs zurück
Gibt einen Fehler zurück, wenn die Sitemap fehlt, einen Nicht-200-Status liefert, ungültiges XML enthält oder keine URLs enthält.
"full"#
Führt einen umfassenden zweiphasigen Crawl durch:
Phase 1 -- Seed-Erkennung:
- Parst
robots.txtnach Sitemap-Direktiven und Crawl-Regeln - Prüft Standard-Sitemap-Pfade (
/sitemap.xml,/wp-sitemap.xml,/sitemap_index.xmlusw.) - Findet RSS/Atom-Feeds über
<link>-Tags und gängige Feed-Pfade - Extrahiert Seed-URLs aus allen gefundenen Quellen
Phase 2 -- Breadth-First-Link-Crawl:
- Startet bei der Basis-URL sowie allen Seed-URLs
- Besucht jede Seite und reiht Links derselben Domain in die Warteschlange ein
- Wendet
include_patternsundexclude_patternsan, um Links zu filtern - Beachtet die Regeln von
robots.txt - Erkennt und vermeidet unendliche URL-Fallen (Kalenderseiten, facettierte Filter usw.)
- Dedupliziert URLs durch Normalisieren von Schema, Host und Query-Parametern sowie Entfernen von Tracking-Parametern (
utm_*,fbclid,gclidusw.)
Standard-Exclude-Muster (wenn exclude_patterns nicht angegeben wird):
- Statische Assets:
*.pdf,*.zip,*.jpg,*.png,*.gif,*.svg,*.css,*.js,*.xml,*.json,*.mp4,*.webm,*.woff,*.woff2 - Geschützte Pfade:
/login,/admin,/cart,/checkout - Tiefe Paginierung: URLs mit
page=-Parametern mit mehr als 3 Stellen
Antwort#
202 Accepted (sofort)#
{
"status": "processing",
"batch_id": "550e8400-e29b-41d4-a716-446655440000",
"url_count": 42,
"total_discovered": 42,
"discovery_method": "sitemap",
"output_format": "zip"
}
| Feld | Beschreibung |
|---|---|
batch_id |
UUID zur Nachverfolgung des Jobs über Batch-Status-Polling oder Webhook. |
url_count |
Anzahl der Seiten, die konvertiert werden. |
total_discovered |
Gesamtzahl der vom Crawl gefundenen Seiten. |
discovery_method |
"sitemap" oder "full_crawl", abhängig vom effektiven Crawl-Modus. |
Batch-Status-Polling#
Fragt mit der batch_id aus der 202-Antwort ab:
GET /v1/convert/batch/{batch_id}
X-API-Key: sk_your_private_key
Liefert den Gesamtstatus, Status pro URL sowie bei Abschluss eine vorsignierte Download-URL für das ZIP. Das vollständige Antwortschema findest du unter Batch-Status-Polling.
Webhook-Callback-Payload#
Wenn callback_url angegeben ist, sendet EnConvert bei Abschluss einen POST-Request:
{
"job_id": "batch-uuid",
"status": "success",
"batch_id": "550e8400-e29b-41d4-a716-446655440000",
"gcs_uri": "env/files/{project_id}/url-to-pdf/website_20260405_123456789.zip",
"filename": "website_20260405_123456789.zip",
"file_size": 12345678,
"total_tasks": 42,
"successful_tasks": 40,
"failed_tasks": 2,
"tasks": [
{"url": "https://example.com/", "status": "success", "filename": "example_20260405_001.pdf"},
{"url": "https://example.com/about", "status": "success", "filename": "example_20260405_002.pdf"},
{"url": "https://example.com/broken", "status": "failed", "error": "Timeout"}
]
}
E-Mail-Benachrichtigung#
Bei Abschluss des Jobs wird eine E-Mail an notification_email gesendet (standardmäßig an die E-Mail-Adresse des Projektinhabers), unabhängig davon, ob der Job erfolgreich war oder fehlgeschlagen ist.
Freigabe nach Abo-Plan#
| Funktion | Founding | Indie | Studio | Enterprise |
|---|---|---|---|---|
| Website-Erfassung | Nein | Ja | Ja | Ja |
| Sitemap-Crawl-Modus | Nein | Ja | Ja | Ja |
| Full-Crawl-Modus | Nein | Nein | Ja | Ja |
| Webhook-Callbacks | Nein | Nein | Ja | Ja |
| HTTP Basic Auth | Nein | Ja | Ja | Ja |
| Cookie-Injektion | Nein | Ja | Ja | Ja |
| Benutzerdefinierte Header | Nein | Ja | Ja | Ja |
| Batch-Größenlimit | 0 | Planabhängig | Planabhängig | Unbegrenzt |
| Monatliche Konvertierungen | 100 | Planabhängig | Planabhängig | Unbegrenzt |
403 Forbidden.
Code-Beispiele#
Python (Privater API-Key)#
import requests
import time
# Start the website capture
response = requests.post(
"https://api.enconvert.com/v1/convert/website-to-pdf",
headers={"X-API-Key": "sk_your_private_key"},
json={
"url": "https://example.com",
"crawl_mode": "sitemap",
"output_filename": "example-website",
"pdf_options": {
"page_size": "A4",
"orientation": "portrait"
}
}
)
data = response.json()
print(f"Batch ID: {data['batch_id']}")
print(f"Pages found: {data['url_count']}")
# Poll for completion
batch_id = data["batch_id"]
while True:
status = requests.get(
f"https://api.enconvert.com/v1/convert/batch/{batch_id}",
headers={"X-API-Key": "sk_your_private_key"}
).json()
print(f"Status: {status['status']} ({status['completed']}/{status['total']})")
if status["status"] in ("completed", "partial", "failed"):
if status.get("zip_download_url"):
print(f"Download: {status['zip_download_url']}")
break
time.sleep(5)
PHP (Privater API-Key)#
$ch = curl_init("https://api.enconvert.com/v1/convert/website-to-pdf");
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-API-Key: sk_your_private_key"
],
CURLOPT_POSTFIELDS => json_encode([
"url" => "https://example.com",
"crawl_mode" => "sitemap",
"output_filename" => "example-website",
"pdf_options" => [
"page_size" => "A4",
"orientation" => "portrait"
]
])
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
echo "Batch ID: " . $response["batch_id"] . "\n";
echo "Pages found: " . $response["url_count"] . "\n";
Node.js (Privater API-Key)#
const response = await fetch("https://api.enconvert.com/v1/convert/website-to-pdf", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-Key": "sk_your_private_key"
},
body: JSON.stringify({
url: "https://example.com",
crawl_mode: "sitemap",
output_filename: "example-website",
pdf_options: {
page_size: "A4",
orientation: "portrait"
}
})
});
const data = await response.json();
console.log(`Batch ID: ${data.batch_id}`);
console.log(`Pages found: ${data.url_count}`);
// Poll for completion
const poll = async () => {
const status = await fetch(
`https://api.enconvert.com/v1/convert/batch/${data.batch_id}`,
{ headers: { "X-API-Key": "sk_your_private_key" } }
).then(r => r.json());
console.log(`Status: ${status.status} (${status.completed}/${status.total})`);
if (["completed", "partial", "failed"].includes(status.status)) {
if (status.zip_download_url) console.log(`Download: ${status.zip_download_url}`);
return;
}
setTimeout(poll, 5000);
};
poll();
Go (Privater API-Key)#
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
)
func main() {
body, _ := json.Marshal(map[string]interface{}{
"url": "https://example.com",
"crawl_mode": "sitemap",
"output_filename": "example-website",
"pdf_options": map[string]interface{}{
"page_size": "A4",
"orientation": "portrait",
},
})
req, _ := http.NewRequest("POST", "https://api.enconvert.com/v1/convert/website-to-pdf", bytes.NewBuffer(body))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-API-Key", "sk_your_private_key")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
respBody, _ := io.ReadAll(resp.Body)
fmt.Println(string(respBody))
}
Mit Webhook-Callback#
{
"url": "https://example.com",
"crawl_mode": "full",
"callback_url": "https://your-server.com/webhook/enconvert",
"output_filename": "example-full-site",
"include_patterns": [".*\\/blog\\/.*", ".*\\/docs\\/.*"],
"pdf_options": {
"page_size": "Letter",
"margins": {"top": 20, "bottom": 20, "left": 15, "right": 15}
}
}
Mit Authentifizierung (passwortgeschützte Website)#
{
"url": "https://staging.example.com",
"crawl_mode": "sitemap",
"auth": {
"username": "admin",
"password": "staging-password"
},
"cookies": [
{"name": "session_token", "value": "abc123", "domain": "staging.example.com"}
]
}
Fehlerantworten#
| Status | Bedingung |
|---|---|
400 Bad Request |
Fehlender oder leerer url-Parameter |
400 Bad Request |
Keine URLs in der Sitemap gefunden |
400 Bad Request |
Timeout beim Abrufen der Sitemap (30-Sekunden-Limit) |
400 Bad Request |
Nicht-200-Antwort von der Sitemap-URL |
400 Bad Request |
Ungültiges XML in der Sitemap |
400 Bad Request |
Nicht erkanntes Sitemap-Format |
400 Bad Request |
Keine Seiten gefunden (Full Crawl hat null URLs gefunden) |
400 Bad Request |
Ungültige Struktur von auth, cookies oder headers |
402 Payment Required |
Monatliches Ops-Kontingent durch Anzahl gefundener Seiten überschritten |
402 Payment Required |
Speicherlimit erreicht |
403 Forbidden |
Website-Crawling im aktuellen Plan nicht verfügbar (Founding-Plan) |
403 Forbidden |
Full-Crawl-Modus erfordert Studio-Plan oder höher |
403 Forbidden |
Anzahl gefundener Seiten überschreitet das Batch-Größenlimit |
403 Forbidden |
Funktion im Plan nicht verfügbar (Webhook, Basic Auth) |
500 Internal Server Error |
Fehler beim Crawlen oder Konvertieren |
Limits#
| Limit | Wert |
|---|---|
| Sitemap-Abruf-Timeout | 30 Sekunden |
| Globales Crawl-Timeout (Full-Modus) | 10 Minuten |
| Maximale Crawl-Tiefe (Full-Modus) | 10 Ebenen |
| Crawl-Timeout pro Seite (Full-Modus) | 30 Sekunden |
| Speicherlimit des Crawlers | 512 MB |
| Schwellenwert für Endlos-Fallen | 20 URLs pro URL-Muster |
| Robots.txt-Abruf-Timeout | 10 Sekunden |
| Maximale Seiten pro Crawl | Batch-Größenlimit des Plans |
| Maximale Cookies pro Request | 50 |
| Maximale benutzerdefinierte Header pro Request | 20 |
| Webhook-Zustellungs-Timeout | 30 Sekunden |
| Monatliche Konvertierungen | Planabhängig |
| Aufbewahrungsdauer der Dateien | Planabhängig |
Häufig gestellte Fragen#
Wie konvertiere ich eine ganze Website mit einer API in PDF?#
Sende einen POST-Request an /v1/convert/website-to-pdf mit der Basis-url der Website und deinem privaten Key im X-API-Key-Header. Die API findet jede Seite (Sitemap oder Full Crawl), konvertiert jede davon in PDF, bündelt sie in einem ZIP und liefert HTTP 202 mit einer batch_id, die du für den Download-Link abfragen kannst.
Was ist der Unterschied zwischen Sitemap- und Full-Crawl-Modus?#
crawl_mode: "sitemap" parst die sitemap.xml der Website (einschließlich verschachtelter Sitemap-Indizes) und ist ab dem Indie-Plan verfügbar. crawl_mode: "full" führt einen zweiphasigen Crawl aus: zuerst Seed-Erkennung über robots.txt, Sitemaps und RSS/Atom-Feeds, danach ein Breadth-First-Link-Crawl innerhalb derselben Domain mit Filterung durch include_patterns/exclude_patterns. Dieser Modus erfordert Studio oder höher. Der Standard "auto" verwendet den höchsten Modus, den dein Plan erlaubt.
Woran erkenne ich, dass mein Website-zu-PDF-Job abgeschlossen ist?#
Frage GET /v1/convert/batch/{batch_id} mit deinem privaten Key ab, um den Gesamtstatus, Status pro URL und eine vorsignierte ZIP-Download-URL zu erhalten, oder übergib eine callback_url, um bei Abschluss einen Webhook-POST zu erhalten. Zusätzlich wird bei Abschluss eine E-Mail an notification_email (standardmäßig an den Projektinhaber) gesendet, unabhängig davon, ob der Job erfolgreich war oder fehlgeschlagen ist.
Kann ich eine passwortgeschützte Website oder eine Staging-Website als PDF archivieren?#
Ja, auf Plänen mit Basic-Auth-Zugriff: Übergib auth mit username und password für HTTP Basic Auth, das auf jede Seite angewendet wird, injiziere bis zu 50 Sitzungs-cookies oder sende bis zu 20 benutzerdefinierte headers.
Warum liefert der Website-zu-PDF-Endpunkt 403 Forbidden?#
Die häufigsten Ursachen: Die Website-Erfassung ist im Founding-Plan nicht verfügbar, crawl_mode: "full" erfordert Studio oder höher, die Anzahl gefundener Seiten überschreitet das Batch-Größenlimit deines Plans, oder eine angeforderte Funktion (Webhook, Basic Auth) ist in deinem Plan nicht enthalten.