Eine Stellenanzeige von der Karriereseite bis in deinen Feed
7. Mai 2026 · Viktor Shcherbakov · 6 Min. Lesezeit
- how-we-index
- infrastructure
- crawler
Eine neue Stelle erscheint um 10:14 Uhr morgens auf der Karriereseite von . Um 11:32 ist sie in unserem Index. Bei der nächsten Aktualisierung deiner Watchlist landet sie in deinem Feed.
Wenn du die spezifikationsartige Zusammenfassung suchst — exakter User-Agent, Anfragerate pro Host, Retry-Policy, vollständige ATS-Liste — dann steht das auf der Indexing-Policy-Seite. Dieser Beitrag erzählt die Geschichte.
Schritt 1: ein 30-Sekunden-Handshake
Anthropic nutzt Greenhouse; SAP nutzt SuccessFactors; Stripe baut selbst; Workday ist Workday. Es gibt rund 30 verschiedene ATS-Plattformen in der Top-Schicht und einen deutlich längeren Schwanz danach. Der Großteil der Arbeit beim Betrieb von Job Seek besteht darin, jedes davon als leicht anderes Protokoll zu behandeln, während es von außen gleich aussieht.
Pro Unternehmen führen wir eine Zeile in boards.csv, die festlegt: welcher Monitor-Typ verwendet wird, wo das Board liegt, ein optionaler Regex-Filter für Stellenlinks, manchmal ein Flag, das heißt: „Diese steht hinter einer WAF, route die Anfrage über einen Proxy.“ Wenn der Crawler dieses Unternehmen im Tick aufnimmt, steuert die Zeile alles Weitere.
Für Anthropic konkret — ein Greenhouse-Board — schickt der Monitor https://boards-api.greenhouse.io/v1/boards/<token>/jobs eine Anfrage und bekommt eine JSON-Liste zurück. Bei Workday senden wir POST an /wday/cxs/<tenant>/<site>/jobs mit einem JSON-Such-Payload. Bei Boards ohne beides laufen wir die Sitemap ab, parsen __NEXT_DATA__ oder fallen auf Playwright-DOM-Extraktion zurück.
Schritt 2: der höfliche Rhythmus
Zwei Regeln gelten für jede Anfrage des Crawlers:
- Ein paar Sekunden zwischen Anfragen an denselben Host. Standard ist 2s, sinkt auf 0,5s für bekannte freundliche ATS-Domains. Ein vollständiger Board-Recheck läuft höchstens einmal pro Stunde. Selbst bei den größten Unternehmen unserer Liste sind wir nie der lauteste Besucher der Karriereseite. Concurrency-Budget pro IP, exponentielles Backoff bei Fehlern, automatische Deaktivierung nach 5 aufeinanderfolgenden Fehlschlägen, damit ein falsch konfiguriertes oder verschobenes Board laut statt still scheitert.
robots.txtist bindend. Wenn die robots.txt eines Unternehmens das, was wir abrufen wollen, untersagt, hören wir auf. Außerdem respektieren wir den EU-TDM-Reservation-Header (Opt-out für Text und Data Mining) für alle Unternehmen, die ihn emittieren. Unser User-Agent identifiziert uns —Job-Seek-Crawler/X.Y +https://jseek.co/how-we-index— sodass jede:r, der/die Server-Logs liest, genau weiß, wer anfragt.
Das ist keine Großzügigkeit, sondern wie man über Jahre willkommen bleibt. Die Zahl der Unternehmen, die uns gebeten haben, langsamer zu werden oder aufzuhören, ist klein — und das ist die einzige Kennzahl, die hier zählt.
Schritt 3: die eigentliche Stelle extrahieren
Eine neue URL kommt aus dem Monitoring. Der nächste Worker greift sie auf, holt die Seite und versucht, die strukturierten Posting-Daten zu extrahieren.
Der Fast Path: Die meisten modernen ATS-Systeme betten application/ld+json vom Typ JobPosting ein — Titel, Ort, Beschäftigungsart, Gehalt, datePosted, validThrough. Die Greenhouse-API liefert ein normalisiertes JSON; Levers ebenfalls; Ashbys, Ripplings, Workables im Wesentlichen gleich.
Wo strukturierte Daten fehlen oder falsch sind, fallen wir auf schrittweise DOM-Extraktion zurück — kleine Rezepte pro Board, die sagen: „Hol den Titel aus diesem Selektor, den Ort aus jenem, die Beschreibung aus dem Artikel-Body.“ Jedes wurde für ein Board geschrieben, bei dem zuvor alles andere gescheitert ist.
Das Ergebnis ist ein JobContent-Datensatz mit normalisierten Feldern: ein Beschäftigungstyp aus fünf Enums (full_time, part_time, contract, internship, full_or_part), ein Job-Location-Type aus einer Dreiermenge (onsite, remote, hybrid), Orte aufgelöst zu GeoNames-IDs, ein Gehalt geparst zu einem strukturierten Currency-Min-Max mit Frequenz. Was in unserer Datenbank ankommt, ist einheitlicher als die Karriereseite, von der es stammt. Ohne diese Einheitlichkeit ist der Vergleich von Rollen über Unternehmen hinweg aussichtslos.
Schritt 4: wenn Rechenzentren nicht willkommen sind
Manche Karriereseiten wollen keine Bots aus Rechenzentren. Starbucks' eightfold-gehostetes Board liefert auf alles, was wie eine Hetzner-IP aussieht, ein 405-Captcha — egal wie höflich wir fragen. Workday einiger großer Kunden ebenso. Wir respektieren das Signal — wer Datacenter-Traffic aktiv blockt, bittet damit um Tempo runter — aber bei ~5% der Stellen die Sicht zu verlieren, war uns zu viel.
Der Kompromiss: Eine kleine, transparente Menge von Boards entscheidet sich für einen Residential-Proxy-Provider. Boards auf dem Proxy-Pfad werden noch aggressiver rate-limitiert. Wenn eine Proxy-IP von einer Origin geblockt wird, verlieren wir diese IP und diskutieren nicht.
Wir geben uns nie als echte:r Nutzer:in aus. Der User-Agent bleibt ehrlich. Wir routen die Anfrage einfach über einen Residential-Exit, damit der IP-Reputations-Filter der Origin sie nicht ablehnt, bevor sie überhaupt ihren Inhaltsfilter erreicht.
Schritt 5: von „in der Datenbank“ zu „in deinem Feed“
Das lokale Postgres auf unserem Crawler-Server ist die Quelle der Wahrheit. Von dort laufen kontinuierlich zwei Pipelines:
- Ein Change-Data-Capture-Exporter schickt neue und geänderte Posting-Zeilen an Supabase (die öffentliche Lese-Datenbank) und an Typesense (den Suchindex). Zwei Cursor, zwei Ziele, Deduplikation auf Identifier, bedingte Updates, sodass ein erneuter Scrape ohne tatsächliche Inhaltsänderung den
updated_at-Stempel nicht weiterzieht. - Ein R2-Drain schiebt Posting-Beschreibungs-Blobs nach Cloudflare R2 und schreibt den resultierenden Content-Hash zurück ins lokale Postgres; der Exporter trägt den Hash dann nach Supabase. Wir trennen die Beschreibung von der Zeile, weil sie das größte Feld ist, am seltensten genutzt (nur Detail-Ansicht) und sich am seltensten ändert.
Eine Watchlist, die zur neuen Stelle passt, nimmt sie auf, sobald die Seite das nächste Mal gegen Typesense hydratisiert. Für jemanden mit geöffneter App ist das das nächste Refresh — typischerweise innerhalb von Minuten nach der 11:32-Ingestion oben.
Schritt 6: wie wir entscheiden, dass eine Stelle weg ist
Unternehmen veröffentlichen Stellen und vergessen, sie zu entfernen. ATS-Systeme delisten träge. Sitemap-basierte Monitore können abgeschnitten werden. Die Kosten einer fälschlichen Auslistung sind hoch: Eine Watchlist, die eine noch offene Stelle bei jemandes Wunscharbeitgeber fallen lässt, ist der Fehler, der die Beziehung beendet.
Also kalibrieren wir nach der Vertrauenswürdigkeit der Quelle. Bei API-Monitoren mit definitiven Listen-Semantiken — Greenhouse, Lever, Workday-Tenant-Suche etc., wo „nicht in der Antwort“ tatsächlich „nicht im Board“ bedeutet — listen wir beim ersten Fehlen aus. Bei fragilen URL-only-Monitoren (Sitemaps, DOM-Extraktion) warten wir vier aufeinanderfolgende verfehlte Zyklen, plus fleetweite Drop-Guards, die Massen-Delistings unterdrücken, wenn eine Sitemap- oder API-Antwort verdächtig abgeschnitten aussieht. Nach dem Auslisten bewahren wir die Quell-URL (verlinkbar, Archivwert) und zeigen die Stelle nicht mehr in aktiven Feeds. Taucht sie an der Quelle wieder auf, listen wir mit dem ursprünglichen first_seen_at neu — dieses Detail wiegt mehr, als es klingt, weil es verhindert, dass versehentliches Rotieren alte Stellen frisch erscheinen lässt.
Schritt 7: Opt-out
Wenn du eine Karriereseite betreibst und möchtest, dass wir aufhören, sie zu indexieren, schreibe an business@colophon-group.org. Das Unternehmen fällt aus companies.csv heraus und der nächste Sync wischt seine Stellen aus. Opt-out ist absichtlich schneller als Opt-in.
Das ist der Weg von einem Klick auf ein Karriereseiten-Admin-Formular bis zu einer Zeile in deinem Feed. Die Architektur hat mehr Teile als das — ein ws-Workflow-Tool, das Menschen (mittlerweile Agenten) nutzen, um neue Unternehmen aufzunehmen, eine tägliche Error-Review-Routine, einen gelabelten Postings-Datensatz für das Modell, das wir bauen — aber das sind Geschichten für eigene Beiträge.
Wenn dich hier etwas überrascht hat oder seltsam klang: Die Indexing-Policy-Seite hat die Vertragsversion. Der Crawler ist Open Source (github.com/colophon-group/jobseek) und das meiste, was oben beschrieben ist, steht dort, falls du den eigentlichen Code lesen möchtest.