API-Referenz

Website-Import

Übernimmt eine bestehende Website (Webflow, WordPress, Squarespace, statische Seiten …) mit Seitenbaum, HTML, CSS, JS und Medien. Der Import läuft in zwei Schritten als Hintergrund-Jobs: erst analysieren, dann mit der gewünschten Auswahl importieren.

Alle Import-Endpunkte erfordern Schreibrechte ✏️.

Ablauf und Verhalten

  1. Analyse (POST /import/scan): liest robots.txt und die Sitemap(s) (inkl. Sitemap-Index und .xml.gz), folgt optional allen internen Links und wertet aus, von welchen Hosts die Website Dateien lädt – auch Hosts, die erst in CSS-Dateien auftauchen (Fonts).
  2. Import (POST /import/run) mit den ausgewählten Seiten und Hosts:
    • Seitenbaum aus den URL-Pfaden. Die Startseite der Quelle wird auf target_page_id abgebildet. Fehlende Zwischenebenen (z. B. /leistungen bei /leistungen/webdesign) werden als Entwurf angelegt.
    • Pfade werden normalisiert: Kleinschreibung, Umlaute (ä → ae), Sonderzeichen → -, Kollisionen → -2. Reservierte Namen auf oberster Ebene (api, media, cms-admin) bekommen ein -seite. Interne Links werden entsprechend umgeschrieben, Query und Fragment bleiben erhalten.
    • <head>: <title> → title, meta description → description; charset, viewport, generator und <base> entfallen (setzt IllusionCMS selbst), alles andere landet in head.
    • <html>/<body>-Attribute → html_attrs/body_attrs (wichtig für Webflow-Interaktionen).
    • CSS/JS werden zu Assets. Stylesheets am Anfang des <head> und Skripte am Ende von <body>, die auf allen importierten Seiten identisch sind, werden an die Zielseite gehängt und vererbt. Alle übrigen bleiben exakt an ihrer Position und verweisen per {{ asset_url(id) }} auf die Bibliothek – die Ladereihenfolge bleibt so erhalten. Wird die Startseite nicht mit importiert, bekommt jede Seite ihre Dateien selbst (nicht vererbt).
    • integrity/crossorigin werden bei übernommenen Dateien entfernt (sonst würde eine bearbeitete Datei blockiert). preload-Hinweise auf übernommene CSS/JS-Dateien entfallen.
    • Medien (Bilder, Fonts, Videos, PDFs, Favicons, Social-Media-Bilder) kommen in die Dateiverwaltung, Ordner Import <domain>. Erfasst werden u. a. src, srcset, poster, style="…url()…", <style>, url()/@import in CSS, data-src/data-srcset/data-bg (Lazy-Loading), data-video-urls, og:image.
    • Bilder mit srcset: Nur die größte Fassung wird geladen; das srcset wird durch die eigenen Varianten ersetzt. og:image & Co. bekommen absolute URLs auf der ursprünglichen Domain.
    • Template-Syntax im Original ({{, {%, {#) wird per {% verbatim %} geschützt.
    • Dateien der eigenen Domain werden immer übernommen, externe nur von den Hosts in download_hosts; alles andere bleibt extern verlinkt.

Grenzen: Seiten, die erst im Browser per JavaScript aufgebaut werden (React/Vue-SPAs), liefern kaum HTML – die Analyse meldet das als js_rendered. Formulare, Suche, Logins und andere serverseitige Funktionen der Quelle werden nur optisch übernommen. Skripte, die zur Laufzeit weitere Dateien relativ zu ihrer eigenen URL nachladen, müssen ggf. angepasst werden.

Job-Objekt

{
  "id": "9f2c41d0a7b3e815",
  "kind": "import",
  "status": "running",
  "phase": "Loading pages and files",
  "phase_code": "import.phaseLoad",
  "done": 12,
  "total": 48,
  "files": 87,
  "assets": 5,
  "log": [
    { "time": 1791197160, "level": "warn", "message": "file not imported: HTTP 404", "url": "https://cdn.example.com/alt.png",
      "code": "import.fileNotImported", "params": { "error": "HTTP 404" } }
  ],
  "result": null,
  "started_at": 1791197150,
  "finished_at": 0
}
Feld Beschreibung
kind scan oder import
status running, done, failed, cancelled
done / total Fortschritt in Seiten
files / assets Bisher übernommene Dateien bzw. CSS/JS-Dateien (nur Import)
phase / phase_code Aktueller Schritt (englischer Text) und sein Code zum Übersetzen
log Protokoll (info, warn, error), max. 3000 Einträge; Einträge mit code/params (siehe Fehler)
error / error_code / error_params Bei failed: Meldung, Code und Parameter
result Ergebnis, sobald status = done (siehe unten)

Jobs liegen im Arbeitsspeicher; die letzten 20 bleiben abrufbar, ein Neustart löscht sie.

POST /import/scan ✏️

{ "url": "https://www.firma.de", "crawl": false, "respect_robots": true, "max_pages": 500 }
Feld Beschreibung
url Startadresse; ohne Schema wird https:// angenommen. Redirects werden verfolgt, die finale Domain gilt als „eigene Domain“ (www. wird ignoriert)
crawl Zusätzlich allen internen Links folgen. Wird automatisch aktiviert, wenn keine Sitemap gefunden wird
respect_robots Per robots.txt gesperrte Seiten werden trotzdem aufgeführt, aber mit "excluded": "robots" markiert (die Oberfläche wählt sie vorab ab, der Crawler folgt ihren Links nicht). robots.txt richtet sich an Suchmaschinen – beim Umzug der eigenen Website gehören gesperrte Seiten wie Impressum oder Datenschutz meist dazu
max_pages Obergrenze (Standard 500, max. 5000)

Antwort 202 mit dem Job-Objekt. Ergebnis (result) nach Abschluss:

{
  "base": "https://www.firma.de",
  "sitemaps": ["https://www.firma.de/sitemap.xml"],
  "pages": [
    { "url": "https://www.firma.de/", "path": "/", "title": "Firma GmbH", "source": "start" },
    { "url": "https://www.firma.de/ueber-uns", "path": "/ueber-uns", "title": "Über uns", "source": "sitemap" },
    { "url": "https://www.firma.de/rechtliches/impressum", "path": "/rechtliches/impressum", "source": "sitemap", "excluded": "robots" }
  ],
  "hosts": [
    { "host": "www.firma.de", "category": "site", "label": "Own domain", "count": 14, "kinds": { "img": 10, "file": 4 }, "suggested": true },
    { "host": "cdn.prod.website-files.com", "category": "builder", "label": "Site builder CDN", "count": 96, "kinds": { "css": 3, "js": 6, "img": 80, "font": 7 }, "suggested": true },
    { "host": "fonts.gstatic.com", "category": "fonts", "label": "Web fonts", "count": 4, "kinds": { "font": 4 }, "suggested": true },
    { "host": "www.googletagmanager.com", "category": "service", "label": "Service / embed", "count": 1, "kinds": { "js": 1 }, "suggested": false }
  ],
  "js_rendered": false
}

Host-Kategorien und Vorauswahl (suggested). label ist eine englische Bezeichnung der Kategorie; Clients übersetzen über category:

category Beispiele Vorauswahl
site eigene Domain immer übernommen
builder Webflow-, Squarespace-, Wix-, Shopify-, WordPress.com-, Contentful-, Cloudinary-CDNs ja
fonts Google Fonts, Bunny Fonts ja (selbst gehostete Fonts sind DSGVO-freundlicher)
cdn cdnjs, jsDelivr, unpkg, code.jquery.com ja
service Analytics, Tag Manager, YouTube, Maps, Cookie-Banner, Chat, Adobe Fonts nein – müssen live bleiben bzw. dürfen nicht kopiert werden
other alle übrigen ja, außer der Host liefert nur Skripte

Die Analyse lädt neben der Startseite bis zu 5 weitere Seiten und bis zu 10 CSS-Dateien als Stichprobe (beim Crawlen alle gefundenen Seiten).

POST /import/run ✏️

{
  "base": "https://www.firma.de",
  "urls": ["https://www.firma.de/", "https://www.firma.de/ueber-uns"],
  "target_page_id": 1,
  "overwrite": true,
  "publish": true,
  "download_hosts": ["cdn.prod.website-files.com", "fonts.googleapis.com", "fonts.gstatic.com"]
}
Feld Beschreibung
base base aus dem Analyse-Ergebnis
urls Zu importierende Seiten (typischerweise eine Auswahl aus pages[].url)
target_page_id Seite, auf die die Startseite der Quelle abgebildet wird; alle anderen landen darunter
new_site Statt target_page_id: { "title": "…", "domains": "a.de\nwww.a.de" } legt eine neue Website an und importiert dorthin. 409, wenn eine Domain schon vergeben ist (es wird dann nichts angelegt). result.target_page_id enthält die ID der neuen Website
overwrite Vorhandene, unbearbeitete Seiten aktualisieren (Inhalt, Head, Attribute, CSS/JS-Zuordnungen). Sonst werden sie übersprungen
overwrite_modified Auch im CMS bearbeitete Seiten und CSS/JS-Dateien überschreiben (Standard: false – sie bleiben, wie sie sind)
force Auch Seiten neu schreiben, deren Quelle sich seit dem letzten Import nicht geändert hat
publish Neu angelegte Seiten veröffentlichen (Zwischenebenen bleiben Entwürfe). Der Status vorhandener Seiten bleibt erhalten
download_hosts Externe Hosts, deren Dateien übernommen werden

Antwort 202 mit dem Job-Objekt. Ergebnis nach Abschluss:

{
  "target_page_id": 1,
  "created": 46, "updated": 1, "unchanged": 0, "modified": 0, "foreign": 0, "skipped": 0, "failed": 1, "placeholders": 2,
  "files": 214, "files_updated": 0, "files_reused": 0,
  "assets": 7, "assets_updated": 0, "assets_reused": 0, "assets_kept": 0,
  "shared_css": 1, "shared_js": 3,
  "pages": [
    { "id": 1, "path": "/", "source": "https://www.firma.de/", "action": "updated" },
    { "id": 12, "path": "/ueber-uns", "source": "https://www.firma.de/ueber-uns", "action": "created" },
    { "id": 0, "path": "/kaputt", "source": "https://www.firma.de/kaputt", "action": "failed" }
  ]
}

action je Seite: created, updated, unchanged (Quelle unverändert), modified (im CMS bearbeitet, nicht überschrieben), foreign (Pfad gehört einer Seite aus dem Import einer anderen Website – wird nie überschrieben), skipped, failed. files/assets zählen neu angelegte Dateien, *_updated an Ort und Stelle aktualisierte, *_reused wiederverwendete, assets_kept in der Quelle geänderte, aber im CMS bearbeitete (behaltene) CSS/JS-Dateien. shared_css/shared_js: Anzahl der Dateien, die vererbt an der Zielseite hängen.

Wiederholte Importe

Ein Import kann beliebig oft wiederholt werden, ohne Duplikate zu erzeugen:

  • Seiten werden zuerst über ihre Herkunft (source_url + Zielseite) gefunden – auch wenn sie inzwischen verschoben oder umbenannt wurden –, sonst über den Pfad. Ist das Quell-HTML seit dem letzten Import unverändert, wird die Seite übersprungen (unchanged), ohne ihre Dateien erneut zu laden.
  • Im CMS bearbeitete Seiten (local_modified) werden nur mit overwrite_modified überschrieben.
  • Dateien werden über ihre Quell-URL wiedererkannt und per If-None-Match (ETag) angefragt – unveränderte werden nicht erneut heruntergeladen. Geänderte Inhalte werden an Ort und Stelle ersetzt (gleiche ID, gleiche URL, Varianten neu berechnet). Mediendateien werden deshalb nur einen Tag im Browser gecacht.
  • CSS/JS werden über ihre Quell-URL wiedererkannt; unveränderte wiederverwendet, geänderte aktualisiert – außer, sie wurden im CMS bearbeitet (dann bleibt die CMS-Fassung, Warnung im Log).
  • Die Download-Hosts werden pro Website und Quelle gemerkt und von POST /pages/{id}/reimport verwendet – ein Import einer anderen Quelle in dieselbe Website überschreibt sie nicht.
  • Gehört keine der übergebenen URLs zur Quelle (base), schlägt der Job mit einer Fehlermeldung fehl, statt still nichts zu tun.

Verwaiste Dateien früherer Importe werden nicht automatisch gelöscht. Dateien und Assets aus Importen vor dieser Version haben keine gespeicherte Herkunft und werden beim ersten Re-Import einmalig neu angelegt.

GET /import/jobs ✏️

Die letzten Jobs (neueste zuerst), ohne Log und Ergebnis.

GET /import/jobs/{id} ✏️

Zustand eines Jobs. Mit ?log_since=n werden nur Logeinträge ab Index n geliefert – praktisch für Polling:

curl -H "Authorization: Bearer $TOKEN" "$API/import/jobs/9f2c41d0a7b3e815?log_since=120"

DELETE /import/jobs/{id} ✏️

Bricht einen laufenden Job ab (204). Bereits angelegte Dateien und Assets bleiben erhalten; Seiten werden erst am Ende eines Imports geschrieben.

Beispiel: Import per Skript

JOB=$(curl -s -X POST "$API/import/scan" -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{"url": "https://www.firma.de", "respect_robots": true}' | jq -r .id)
until [ "$(curl -s -H "Authorization: Bearer $TOKEN" "$API/import/jobs/$JOB" | jq -r .status)" != running ]; do sleep 1; done
SCAN=$(curl -s -H "Authorization: Bearer $TOKEN" "$API/import/jobs/$JOB" | jq .result)

curl -s -X POST "$API/import/run" -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d "$(jq '{base, urls: [.pages[].url], target_page_id: 1, overwrite: true, publish: true,
            download_hosts: [.hosts[] | select(.suggested and .category != "site") | .host]}' <<< "$SCAN")"
# Hinweis: urls enthält hier auch per robots.txt gesperrte Seiten – zum Ausschließen
# [.pages[] | select(.excluded == null) | .url] verwenden