Wie man Indeed mit Python scrapt (und 403-Fehler vermeidet)

Zuletzt aktualisiert am April 16, 2026

Nach etwa dem fünfzigsten Copy-and-Paste eines Jobtitels von Indeed in eine Tabelle habe ich meine Berufswahl infrage gestellt. Wenn du schon einmal versucht hast, strukturierte Daten programmgesteuert aus Indeed zu ziehen, kennst du den Witz bereits: Der 403-Fehler ist kein Bug – sondern ein Teil von Indeeds Verteidigungssystem.

Indeed ist das weltweit größte Jobportal mit rund , zu jedem beliebigen Zeitpunkt und Aktivitäten in . Damit gehört es zu den ergiebigsten Quellen für Arbeitsmarktdaten überhaupt – und zu den schwierigsten Zielen fürs Scraping. Das Open-Source-Scraper-Projekt JobFunnel (mit Tausenden GitHub-Stars) wurde im Dezember 2025 vom Maintainer , nachdem es jahrelang den Anti-Bot-Krieg verloren hatte. In den Worten des Maintainers: „Alle Nutzer können einige Jobs scrapen, werden aber schnell von Captchas getroffen, und das Scraping schlägt fehl, sodass keine Jobs zurückkommen.“ Ein weiterer Mitwirkender berichtete, bereits ein CAPTCHA erhalten zu haben. Kurz gesagt: Das ist definitiv kein triviales Scraping-Ziel. In diesem Leitfaden zeige ich dir alle praxisrelevanten Methoden, um Indeed mit Python zu scrapen, wie du die 403-Hürde tatsächlich überwindest – und für alle, die sich das Debugging lieber sparen möchten, eine No-Code-Alternative mit .

Was bedeutet es, Indeed mit Python zu scrapen?

Web Scraping ist im Kern die automatisierte Extraktion strukturierter Daten aus Webseiten. Wenn wir davon sprechen, Indeed mit Python zu scrapen, meinen wir ein Skript, das die Suchergebnisse und Detailseiten von Indeed aufruft, den zugrunde liegenden HTML-Code (oder eingebettete Daten) ausliest und Felder wie Jobtitel, Unternehmen, Ort, Gehalt und Beschreibung in ein nutzbares Format überführt – etwa CSV, eine Datenbank oder Google Sheets.

Typische Python-Bibliotheken dafür sind Requests (für HTTP-Aufrufe), BeautifulSoup (für die HTML-Analyse) sowie Selenium oder Playwright (für Browser-Automatisierung). Doch Indeed ist keine einfache statische Website. Es handelt sich um eine Mischform: serverseitig gerendertes HTML mit eingebettetem JSON-State-Blob, abgesichert durch Cloudflare Bot Management. Das heißt: Dein Scraper muss mit JavaScript-gerenderten Inhalten, wechselnden CSS-Klassennamen und aggressivem Bot-Schutz umgehen können – und das alles, bevor du überhaupt einen einzigen Jobtitel ausliest.

Außerdem gibt es im Jahr 2026 keine offizielle, kostenlose Nur-Lese-API von Indeed. Die frühere Publisher Jobs API wurde um 2020 eingestellt, und was geblieben ist, richtet sich an Arbeitgeber (Job Sync, Sponsored Jobs). Daher bleiben Scraping oder ein kostenpflichtiger Drittanbieter für Daten realistisch die einzigen Optionen.

Warum Indeed-Jobdaten scrapen?

Der Business-Case ist klar: Tausende Anzeigen manuell zu durchsuchen ist unpraktisch, und die Daten in diesen Anzeigen sind wirklich wertvoll.

indeed_stats_dca2a43cec.png

AnwendungsfallWer profitiertBeispiel
Lead-GenerierungSales- und Recruiting-TeamsListen von einstellenden Unternehmen mit Kontaktdaten aufbauen
ArbeitsmarktanalyseAnalysten, HR-TeamsTrend-Skills und Gehaltsbenchmarks nach Region erkennen
WettbewerbsanalyseArbeitgeber, PersonaldienstleisterEinstellungsaktivität und Gehaltsangebote der Konkurrenz beobachten
Automatisierte JobsucheJobsuchendeAnzeigen nach eigenen Kriterien über mehrere Standorte hinweg bündeln
Trainingsdaten für ML-ModelleData ScientistsGehaltsprognosemodelle aus historischen Stellendaten erstellen

Die Forschung des Indeed Hiring Lab , dass Stellendaten eng mit BLS JOLTS korrelieren und als nahezu Echtzeit-Indikator für den US-Arbeitsmarkt dienen können. Hedgefonds nutzen die Geschwindigkeit von Jobpostings als alternativen Datensignal. HR-Teams benchmarken Gehälter anhand gescrapter Gehaltsspannen. Und Recruiter erstellen Prospect-Listen aus Unternehmen, die gerade aktiv einstellen.

Ein wichtiger Hinweis: Gehaltsdaten auf Indeed werden zwar besser, sind aber noch immer lückenhaft. Mitte 2025 enthalten rund Gehaltsinformationen, aber nur etwa nennen einen exakten Wert – der Rest sind Spannen. Jede Gehaltsanalyse auf Basis von Indeed-Daten sollte diese Lücken berücksichtigen.

Die richtige Methode zum Scrapen von Indeed mit Python wählen

Es gibt nicht den einen „richtigen“ Weg, Indeed zu scrapen. Die beste Methode hängt von deinem Kenntnisstand, der Datenmenge und deiner Bereitschaft ab, Wartungsaufwand zu akzeptieren. Ich habe alle vier wichtigen Ansätze getestet – hier ist der Vergleich:

KriteriumBS4 + RequestsSeleniumVerstecktes JSON (window.mosaic)No-Code (Thunderbit)
SchwierigkeitsgradEinsteigerMittelMittel bis fortgeschrittenKeiner (2 Klicks)
GeschwindigkeitSchnellLangsam (Browser-Rendering)SchnellSchnell (Cloud-Scraping)
JavaScript-gerenderte InhalteNeinJaJa (eingebettete Daten)Ja
Widerstand gegen Bot-SchutzNiedrigMittel (erkennbar)Mittel bis hochHoch (automatisch gehandhabt)
Wartung bei HTML-ÄnderungenHoch (Selektoren brechen)HochMittel (JSON-Struktur stabiler)Keine (KI passt sich an)
Am besten geeignet fürSchnelle PrototypenDynamische Seiten, Login-geschützte InhalteStrukturierte Daten in großen MengenNicht-Entwickler, schnelle Ergebnisse

Dieser Leitfaden geht alle Methoden durch. Wenn du Python entwickelst, sind die Abschnitte zu BS4, Hidden JSON und Selenium relevant. Wenn du kein Code-Profi bist – oder einfach genug von 403-Debugging hast –, spring direkt zum Thunderbit-Abschnitt.

Bevor du startest

  • Schwierigkeitsgrad: Einsteiger bis Mittelstufe (Python-Teile); keiner (Thunderbit-Teil)
  • Benötigte Zeit: ca. 20–60 Minuten für Python-Setup und ersten Scrape; ca. 2 Minuten mit Thunderbit
  • Was du brauchst: Python 3.9+, einen Code-Editor, den Chrome-Browser und für den No-Code-Weg die

Deine Python-Umgebung für Indeed-Scraping einrichten

Bevor du irgendeinen Scraping-Code schreibst, richte zuerst deine Umgebung ein.

Benötigte Bibliotheken installieren

Erstelle ein virtuelles Umfeld und installiere die Pakete, die du brauchst:

1python -m venv indeed_env
2source indeed_env/bin/activate  # Unter Windows: indeed_env\Scripts\activate
3# Für den HTTP- + Parsing-Ansatz
4pip install requests beautifulsoup4 lxml httpx
5# Für den Hidden-JSON-Ansatz (empfohlen)
6pip install curl_cffi parsel tenacity
7# Für den Browser-Automatisierungs-Ansatz
8pip install selenium

Ein paar Hinweise:

  • curl_cffi ist 2026 der Standard für das Scraping von Cloudflare-geschützten Websites. Es imitiert echte Browser-TLS-Fingerprints, was einfache requests- und httpx-Aufrufe nicht können. Mehr dazu im Abschnitt zum Bot-Schutz.
  • Selenium 4.6+ bringt den Selenium Manager mit, sodass du ChromeDriver nicht mehr manuell herunterladen musst – die Browser-Binärdatei wird automatisch verwaltet.
  • Nutze lxml als Parser-Backend für BeautifulSoup. Es ist ungefähr als der Standard-Parser html.parser.

Die Projektstruktur anlegen

Halte es einfach:

1indeed_scraper/
2├── scraper.py
3├── requirements.txt
4└── output/

Alle Codebeispiele unten bauen auf scraper.py auf.

Indeed mit Python und BeautifulSoup scrapen

Das ist der einsteigerfreundliche Ansatz: Mit requests die Seite abrufen und mit BeautifulSoup das HTML parsen. Das ist schnell eingerichtet, aber bei Indeed auch am anfälligsten.

Schritt 1: Die Indeed-Such-URL bauen

Die Such-URLs von Indeed folgen einem gut vorhersagbaren Muster:

1https://www.indeed.com/jobs?q=<query>&l=<location>&start=<offset>

Zum Beispiel für die Suche nach „data analyst“ in „Austin, TX“ ab der ersten Seite:

1from urllib.parse import urlencode
2params = {
3    "q": "data analyst",
4    "l": "Austin, TX",
5    "start": 0,
6}
7url = f"https://www.indeed.com/jobs?{urlencode(params)}"
8print(url)
9# https://www.indeed.com/jobs?q=data+analyst&l=Austin%2C+TX&start=0

Indeed paginiert in Schritten von 10 und hat eine harte Obergrenze von 1.000 Ergebnissen (start <= 990). Jeder Offset über 990 liefert stillschweigend dieselbe Seite zurück.

Schritt 2: Eine HTTP-Anfrage mit passenden Headern senden

Indeed blockiert Requests mit dem Standard-User-Agent von Python sofort. Du brauchst realistische Header:

1import requests
2headers = {
3    "User-Agent": (
4        "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
5        "(KHTML, like Gecko) Chrome/145.0.0.0 Safari/537.36"
6    ),
7    "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
8    "Accept-Language": "en-US,en;q=0.9",
9    "Accept-Encoding": "gzip, deflate, br",
10    "Referer": "https://www.indeed.com/",
11}
12response = requests.get(url, headers=headers, timeout=30)
13print(response.status_code)

Wenn du 200 erhältst, bist du vorerst drin. Wenn du 403 bekommst, hat Cloudflare dich erkannt. Wie du das überlebst, kommt weiter unten.

Schritt 3: Stellenanzeigen aus dem HTML parsen

Nutze BeautifulSoup, um Jobkarten zu selektieren. Ziel sollten data-testid-Attribute sein – sie sind stabiler als Indeeds zufällig generierte CSS-Klassennamen:

1from bs4 import BeautifulSoup
2soup = BeautifulSoup(response.text, "lxml")
3cards = soup.find_all("div", attrs={"data-testid": "slider_item"})
4jobs = []
5for card in cards:
6    title_el = card.find("h2", class_="jobTitle")
7    title = title_el.get_text(strip=True) if title_el else None
8    company = card.find(attrs={"data-testid": "company-name"})
9    location = card.find(attrs={"data-testid": "text-location"})
10    link = title_el.find("a")["href"] if title_el and title_el.find("a") else None
11    jobs.append({
12        "title": title,
13        "company": company.get_text(strip=True) if company else None,
14        "location": location.get_text(strip=True) if location else None,
15        "url": f"https://www.indeed.com{link}" if link else None,
16    })
17print(f"Gefunden: {len(jobs)} Jobs")

Schritt 4: Pagination behandeln

Schleife durch die Seiten, indem du den start-Parameter erhöhst:

1import time, random
2all_jobs = []
3for page in range(0, 50, 10):  # Erste 5 Seiten
4    params["start"] = page
5    url = f"https://www.indeed.com/jobs?{urlencode(params)}"
6    response = requests.get(url, headers=headers, timeout=30)
7    # ... wie oben parsen ...
8    all_jobs.extend(jobs)
9    time.sleep(random.uniform(3, 6))

Grenzen dieses Ansatzes

Ich sage es offen: BS4 + Requests ist 2026 die schwächste Methode für Indeed. Einfaches requests nutzt die TLS-Bibliothek aus der Python-Standardbibliothek, die einen erzeugt, den Cloudflare sofort als „kein Browser“ erkennt. Außerdem unterstützt es kein HTTP/2, obwohl Indeed genau das ausliefert. Wahrscheinlich wirst du nach wenigen Seiten blockiert. Und die CSS-Selektoren? Indeed wechselt Klassennamen wie css-1m4cuuf und jobsearch-JobComponent-embeddedBody-1n0gh5s – jeder Selektor darauf ist also eine tickende Zeitbombe.

Nutze diese Methode nur für schnelle Prototypen auf einer einzelnen Seite. Für alles, was skalieren soll, ist der Hidden-JSON-Ansatz besser.

Indeed mit Python und versteckten JSON-Daten scrapen

Das ist die Methode, die ich den meisten Python-Entwicklern empfehle. Statt fragile HTML-Elemente zu parsen, extrahierst du strukturierte Daten aus einer JavaScript-Variable im Seitenquelltext von Indeed: window.mosaic.providerData["mosaic-provider-jobcards"].

Jedes Feld, das dich interessiert – Jobtitel, Unternehmen, Ort, Gehalt, Job-Key, Veröffentlichungsdatum, Remote-Flag – steckt bereits in diesem JSON-Blob. Kein JavaScript-Ausführen nötig. Das Schema ist , also deutlich robuster als DOM-Selektoren.

Schritt 1: Den HTML-Quellcode abrufen

Nutze curl_cffi statt requests – damit werden echte Browser-TLS-Fingerprints imitiert, was für das Überleben gegen Cloudflare entscheidend ist:

1from curl_cffi import requests as cffi_requests
2response = cffi_requests.get(
3    "https://www.indeed.com/jobs?q=python+developer&l=Remote&start=0",
4    impersonate="chrome124",
5    headers={
6        "Accept-Language": "en-US,en;q=0.9",
7        "Referer": "https://www.indeed.com/",
8    },
9    timeout=30,
10)
11print(response.status_code, len(response.text))

Warum curl_cffi? Es basiert auf curl-impersonate und reproduziert das exakte TLS ClientHello, den HTTP/2-SETTINGS-Frame und die Header-Reihenfolge echter Browser. Es ist derzeit der einzige aktiv gepflegte Python-HTTP-Client, der in einem einzigen Aufruf umgeht. Unterstützte Profile sind unter anderem chrome120, chrome124, chrome131, Safari- und Edge-Varianten.

Schritt 2: Das JSON per Regex herausziehen

Der JSON-Blob steckt in einem <script>-Tag. Mit einer Regex holst du ihn heraus:

1import re, json
2MOSAIC_RE = re.compile(
3    r'window\.mosaic\.providerData\["mosaic-provider-jobcards"\]=(\{.+?\});',
4    re.DOTALL,
5)
6match = MOSAIC_RE.search(response.text)
7if match:
8    data = json.loads(match.group(1))
9    results = data["metaData"]["mosaicProviderJobCardsModel"]["results"]
10    print(f"Gefunden: {len(results)} Jobs im versteckten JSON")
11else:
12    print("Verstecktes JSON nicht gefunden – möglicher Block oder Seitenänderung")

Schritt 3: Job-Felder aus dem JSON auslesen

Jeder Eintrag in results enthält mehr Informationen als das, was sichtbar auf der Seite steht:

1jobs = []
2for job in results:
3    jobs.append({
4        "jobkey": job["jobkey"],
5        "title": job["title"],
6        "company": job.get("company"),
7        "location": job.get("formattedLocation"),
8        "remote": job.get("remoteLocation"),
9        "salary": (job.get("salarySnippet") or {}).get("text"),
10        "posted": job.get("formattedRelativeTime"),
11        "job_type": job.get("jobTypes"),
12        "easy_apply": job.get("indeedApplyEnabled"),
13        "url": f"https://www.indeed.com/viewjob?jk={job['jobkey']}",
14    })

Das JSON enthält oft Gehaltsschätzungen, Taxonomie-Attribute (Skill-Tags) und Unternehmensbewertungen, die im gerenderten HTML nicht immer sichtbar sind.

Schritt 4: Mehrere Seiten scrapen

Nutze tierSummaries im JSON, um die Gesamtzahl der Treffer zu verstehen, und gehe dann in einer Schleife durch die Seiten:

1import time, random
2all_jobs = []
3for start in range(0, 50, 10):  # Erste 5 Seiten
4    url = f"https://www.indeed.com/jobs?q=python+developer&l=Remote&start={start}&sort=date"
5    response = cffi_requests.get(
6        url,
7        impersonate="chrome124",
8        headers={"Accept-Language": "en-US,en;q=0.9", "Referer": "https://www.indeed.com/"},
9        timeout=30,
10    )
11    match = MOSAIC_RE.search(response.text)
12    if match:
13        data = json.loads(match.group(1))
14        results = data["metaData"]["mosaicProviderJobCardsModel"]["results"]
15        all_jobs.extend([{
16            "jobkey": j["jobkey"],
17            "title": j["title"],
18            "company": j.get("company"),
19            "location": j.get("formattedLocation"),
20            "salary": (j.get("salarySnippet") or {}).get("text"),
21            "url": f"https://www.indeed.com/viewjob?jk={j['jobkey']}",
22        } for j in results])
23    time.sleep(random.uniform(3, 7))
24print(f"Insgesamt: {len(all_jobs)} Jobs gescrapt")

Warum Hidden JSON robuster ist

Die Struktur window.mosaic.providerData ändert sich deutlich seltener als CSS-Klassennamen. Du erhältst saubere, strukturierte Daten, ohne verschachteltes HTML zerlegen zu müssen. Dennoch brauchst du weiterhin Maßnahmen gegen Bots (Header, Pausen, Proxies) – darauf gehen wir als Nächstes ein.

Indeed mit Python und Selenium scrapen

Selenium ist der Ansatz über Browser-Automatisierung. Er ist nützlich, wenn du mit der Seite interagieren musst – etwa um in Job-Detailpanels zu klicken, login-geschützte Inhalte zu handhaben oder dynamisch geladene Beschreibungen zu erfassen, die im ersten HTML noch nicht enthalten sind.

Wann Selenium statt HTTP-Clients sinnvoll ist

  • Indeed lädt einige Inhalte dynamisch nach (vollständige Jobbeschreibungen im rechten Panel)
  • Du musst Seiten scrapen, die Session-Zustand oder Login erfordern
  • Du arbeitest in kleinem Umfang und Geschwindigkeit ist nicht kritisch

Kurzer Überblick

1from selenium import webdriver
2from selenium.webdriver.common.by import By
3from selenium.webdriver.chrome.options import Options
4import time
5options = Options()
6options.add_argument("--disable-blink-features=AutomationControlled")
7# options.add_argument("--headless=new")  # Headless ist leichter erkennbar – mit Vorsicht verwenden
8driver = webdriver.Chrome(options=options)
9driver.get("https://www.indeed.com/jobs?q=data+engineer&l=New+York")
10time.sleep(3)
11cards = driver.find_elements(By.CSS_SELECTOR, "[data-testid='slider_item']")
12for card in cards:
13    try:
14        title = card.find_element(By.CSS_SELECTOR, "h2.jobTitle").text
15        company = card.find_element(By.CSS_SELECTOR, "[data-testid='company-name']").text
16        location = card.find_element(By.CSS_SELECTOR, "[data-testid='text-location']").text
17        print(f"{title} | {company} | {location}")
18    except Exception:
19        continue
20driver.quit()

Grenzen

Selenium ist langsam – jede Seite muss komplett im Browser gerendert werden. Headless Chrome ist (Cloudflare prüft unter anderem navigator.webdriver, WebGL-Vendor-Strings, Plugin-Anzahl und mehr). Selbst undetected-chromedriver verzögert die Erkennung nur; er verhindert sie nicht dauerhaft. Und wie bei BS4 brechen deine Selektoren, sobald Indeed die Oberfläche ändert.

Für die meisten Anwendungsfälle liefert der Hidden-JSON-Ansatz dieselben Daten schneller und mit weniger Wartungsaufwand. Selenium solltest du für Sonderfälle reservieren, in denen du wirklich einen Browser brauchst.

403-Fehler beim Scrapen von Indeed mit Python vermeiden

Das ist der wichtigste Abschnitt. Wenn du nach einer frustrierenden Google-Suche hier gelandet bist, bist du genau richtig.

indeed_antibot_374d080ff4.png

Warum Indeed deinen Scraper blockiert

Indeed nutzt – nicht DataDome, nicht PerimeterX. Das bestätigen die Response-Header: server: cloudflare, cf-ray und das Bot-Management-Cookie __cf_bm. Cloudflare prüft deinen TLS-Fingerprint (JA3/JA4), die Reihenfolge der HTTP/2-Header, dein Request-Muster und Browser-Verhaltenssignale. Wenn etwas davon nicht menschlich wirkt, bekommst du einen 403, 429, 503 oder – der heimtückischste Fall – einen 200 OK mit einer Turnstile-Challenge statt echter Jobdaten.

User-Agent und Request-Header rotieren

Ein einziger statischer User-Agent ist der schnellste Weg ins Blockieren. Rotieren solltest du aus einem Pool aktueller, realistischer Strings. Wichtig: Die Minor-Versionen in Chromes User-Agent sind seit der User-Agent-Reduktion auf – erfinde keine nicht-null Minor-Versionen, sonst schlagen Anti-Bots Alarm.

1import random
2USER_AGENTS = [
3    "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
4    "(KHTML, like Gecko) Chrome/145.0.0.0 Safari/537.36",
5    "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 "
6    "(KHTML, like Gecko) Chrome/145.0.0.0 Safari/537.36",
7    "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
8    "(KHTML, like Gecko) Chrome/145.0.0.0 Safari/537.36 Edg/145.0.3800.97",
9    "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:128.0) Gecko/20100101 Firefox/128.0",
10    "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 "
11    "(KHTML, like Gecko) Version/17.4 Safari/605.1.15",
12]
13headers = {
14    "User-Agent": random.choice(USER_AGENTS),
15    "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
16    "Accept-Language": "en-US,en;q=0.9",
17    "Accept-Encoding": "gzip, deflate, br, zstd",
18    "Referer": "https://www.indeed.com/",
19    "Sec-Fetch-Dest": "document",
20    "Sec-Fetch-Mode": "navigate",
21    "Sec-Fetch-Site": "same-origin",
22}

Achte außerdem darauf, dass deine sec-ch-ua-Client-Hints zur UA-Version passen. Ein sec-ch-ua: "Chrome";v="131" neben einem User-Agent, der Chrome 145 behauptet, ist sofort verdächtig.

Zwischen den Requests zufällige Pausen einbauen

Feste Intervalle werden durch Mustererkennung auffällig. Nutze zufällige Jitter:

1import time, random
2# Zwischen zwei Requests
3time.sleep(random.uniform(3, 6))
4# Bei Wiederholungen nach einem Block
5def backoff_sleep(attempt):
6    base = 4
7    sleep_time = base * (2 ** attempt) + random.uniform(0, 2)
8    time.sleep(min(sleep_time, 60))

Die übereinstimmende Einschätzung von und liegt bei 3–6 Sekunden zwischen Requests pro IP, mit einer harten Grenze von ungefähr 100 Requests pro IP und Session, bevor rotiert werden sollte.

Proxy-Rotation einsetzen

Das ist der größte Erfolgsfaktor. Datacenter-Proxies aus AWS-/GCP-Netzen kommen auf Cloudflare-Enterprise-Zielen nur auf etwa 5–15 % Erfolgsquote – für Indeed praktisch unbrauchbar. Residential Proxies plus korrekte TLS-Fingerprints liegen eher bei 80–95 %.

1PROXIES = [
2    "http://user:pass@us.residential.example:7777",
3    "http://user:pass@us.residential.example:7778",
4    "http://user:pass@us.residential.example:7779",
5]
6proxy = random.choice(PROXIES)
7response = cffi_requests.get(
8    url,
9    impersonate="chrome124",
10    headers=headers,
11    proxies={"https": proxy},
12    timeout=30,
13)

Die Preise für Residential Proxies liegen 2026 je nach Anbieter und Vertragsmodell grob bei . Für Indeed solltest du mit einem kleinen Pool starten und bei Bedarf erweitern.

403-, 429- und 503-Statuscodes sauber behandeln

Nicht blind neu versuchen. Unterschiedliche Statuscodes bedeuten Unterschiedliches:

1def fetch_with_retry(url, proxy_pool, max_retries=5):
2    for attempt in range(max_retries):
3        proxy = random.choice(proxy_pool)
4        headers["User-Agent"] = random.choice(USER_AGENTS)
5        try:
6            r = cffi_requests.get(
7                url,
8                impersonate=random.choice(["chrome124", "chrome120", "edge101"]),
9                headers=headers,
10                proxies={"https": proxy},
11                timeout=30,
12            )
13            # Den heimtückischen Fall „200 mit Challenge“ prüfen
14            if r.status_code == 200 and "cf-turnstile" not in r.text and "Just a moment" not in r.text:
15                return r
16            if r.status_code == 403:
17                print(f"403 – blockiert. Proxy wird rotiert, Versuch {attempt + 1}")
18            elif r.status_code == 429:
19                print(f"429 – Rate-Limit erreicht. Langsamer machen.")
20            elif r.status_code == 503:
21                print(f"503 – Server überlastet oder JS-Challenge.")
22            backoff_sleep(attempt)
23        except Exception as e:
24            print(f"Request-Fehler: {e}")
25            backoff_sleep(attempt)
26    raise RuntimeError(f"Nach {max_retries} Versuchen fehlgeschlagen: {url}")

Der Fall „200 mit Challenge“ ist der kniffligste. Prüfe den Response-Body immer auf Marker wie cf-turnstile oder Just a moment, bevor du einen 200er als Erfolg behandelst.

Die einfachere Alternative: Thunderbit übernimmt den Bot-Schutz für dich

Für Nutzer, die keine Proxy-Pools, Header-Rotation und TLS-Fingerprint-Imitation aufbauen und pflegen möchten, übernimmt das Cloud-Scraping von CAPTCHAs, Proxy-Rotation und Bot-Schutz automatisch. Kein Proxy-Setup, keine curl_cffi-Konfiguration, keine CAPTCHA-Lösungsbibliotheken. Das ist der Weg mit dem geringsten Widerstand, wenn du einfach nur die Daten brauchst.

Warum dein Indeed-Scraper immer wieder kaputtgeht – und wie du das löst

Die 403-Sperre ist der akute Schmerz. Der chronische Schmerz ist Wartung – Scraper, die heute funktionieren, brechen nächste Woche still und heimlich und liefern leere oder veraltete Ergebnisse.

Wie Indeed deine Selektoren bricht

Indeed rotiert CSS-Klassennamen sehr aggressiv. In Bright Datas Leitfaden wird , dass Klassen wie css-1m4cuuf und css-1rqpxry „zufällig generiert zu sein scheinen – vermutlich zur Build-Zeit“. A/B-Tests sorgen außerdem dafür, dass verschiedene Sitzungen unterschiedliche Layouts sehen. Und DOM-Strukturen werden ohne Vorwarnung umgebaut.

Die JobFunnel-Geschichte ist lehrreich. Ein Mitwirkender schrieb: „CaptchaBuster hat das CAPTCHA erfolgreich abgefangen, und der Grund, warum das Scraping weiterhin fehlschlägt, liegt an veralteten BeautifulSoup-Selektoren.“ Der Scraper war also nicht blockiert – er griff nur die falschen Elemente ab.

Strategie: Hidden JSON statt DOM-Parsing bevorzugen

Der window.mosaic.providerData-Blob ist seit mindestens 2023 schema-stabil. Der Pfad metaData.mosaicProviderJobCardsModel.results[] ist Weg. DOM-Selektoren brechen monatlich. JSON-Extraktion bricht, wenn überhaupt, eher jährlich.

Strategie: Datenattribute statt Klassennamen nutzen

Wenn du doch ins DOM greifen musst, verwende funktionale Attribute:

SelektorZweck
[data-testid="slider_item"]Container jeder Jobkarte
[data-testid="job-title"] oder h2.jobTitle > aLink zum Jobtitel
[data-testid="company-name"]Name des Arbeitgebers
[data-testid="text-location"]Standorttext
data-jk="<jobkey>" auf jeder KarteDer stabilste Anker überhaupt – unverändert seit 2019

Assertion-Checks einbauen, um veraltete Selektoren zu erkennen

Lass deinen Scraper niemals stillschweigend mit null Ergebnissen durchlaufen. Füge nach jedem Abruf eine Prüfung ein:

1results = parse_hidden_json(html)
2assert len(results) &gt; 0, (
3    f"Indeed hat bei start={start} ein leeres Ergebnis zurückgegeben – "
4    "möglicher Block, CAPTCHA oder Selektor-Verschiebung. "
5    f"Erste 500 Zeichen der Antwort: {html[:500]}"
6)

Logge bei Fehlern die ersten 500 bis 2000 Zeichen der Rohantwort. So erkennst du sofort, ob du eine Turnstile-Challenge, eine Login-Sperre oder eine Schemaänderung bekommen hast. Führe täglich einen Smoke Test auf CI-Ebene gegen eine feste Suchanfrage aus, z. B. q=python&l=remote, und prüfe auf nicht-leere Ergebnisse.

Die KI-Alternative: Scraper, die nicht ständig kaputtgehen

Thunderbits KI liest die Seitenstruktur jedes Mal neu – sie hängt nicht an hart codierten Selektoren oder Regex-Mustern. Wenn Indeed sein HTML ändert, passt sich Thunderbit automatisch an. Genau das adressiert den Wartungsaufwand, den Nutzer in Foren immer wieder als größtes Problem nennen. Wenn du schon einmal morgens eine Slack-Nachricht gesehen hast wie „der Scraper liefert schon wieder leere Zeilen“, weißt du, wie wertvoll es ist, das nicht selbst reparieren zu müssen.

Indeed scrapen, ohne Python zu schreiben: die No-Code-Alternative

Jeder Konkurrenzleitfaden setzt voraus, dass du Python-Code schreibst. Doch die Forenberichte erzählen eine andere Geschichte. Nutzer sagen Dinge wie „es ist einfach so schwierig mit ständigen Bugs und Fehlern“ und manche empfehlen, jemanden auf Fiverr zu beauftragen, nur um an die Daten zu kommen. Wenn dir das bekannt vorkommt, ist dieser Abschnitt dein Ausweg.

Indeed mit Thunderbit scrapen – Schritt für Schritt

Schritt 1: Installiere die aus dem Chrome Web Store. Der Einstieg ist kostenlos.

Schritt 2: Öffne eine Indeed-Suchergebnisseite in deinem Browser – zum Beispiel https://www.indeed.com/jobs?q=data+analyst&l=Austin%2C+TX.

Schritt 3: Klicke auf das Thunderbit-Symbol in deiner Browserleiste und dann auf „AI-Felder vorschlagen“. Thunderbits KI scannt die Seite und erkennt automatisch Spalten wie Jobtitel, Unternehmen, Ort, Gehalt, Job-URL und Veröffentlichungsdatum. Du kannst die vorgeschlagenen Felder prüfen und anpassen – Spalten entfernen, die du nicht brauchst, oder eigene Felder hinzufügen, indem du einfach beschreibst, was du willst.

Schritt 4: Klicke auf „Scrapen“. Thunderbit extrahiert die Daten von der Seite und zeigt sie in einer strukturierten Tabelle an. Du solltest Zeilen mit Stellenanzeigen und den von dir konfigurierten Feldern sehen.

Mit Subpage-Scraping anreichern

Nach dem Scrapen der Listing-Seite klickst du auf „Subpages scrapen“, damit Thunderbit jede einzelne Job-Detailseite besucht. Es zieht vollständige Stellenbeschreibungen, Anforderungen, Benefits und Bewerbungslinks – ohne zusätzliche Einrichtung. Das ist das Äquivalent dazu, einen zweiten Python-Scraper zu schreiben, der jede /viewjob?jk=<jobkey>-URL besucht – nur mit einem Klick.

Pagination automatisch verarbeiten

Thunderbit verarbeitet Indeeds klickbasierte Pagination automatisch. Du musst keine Offset-URLs manuell bauen und keine Schleifen für die Seitennavigation schreiben. Es klickt sich durch die Seiten und aggregiert die Ergebnisse.

In deine Lieblings-Tools exportieren

Exportiere die gescrapten Daten kostenlos nach CSV, Excel, Google Sheets, Airtable oder Notion. Du musst dafür keinen csv.writer()- oder pandas.to_csv()-Code schreiben.

Wann Python und wann Thunderbit?

SzenarioBestes Tool
Eigene Datenpipelines, geplante Automatisierung über cron/AirflowPython
Integration in eine größere CodebasisPython
Stark angepasste Parsing-LogikPython
Einmalige Recherche oder MarktanalyseThunderbit
Nicht-technische Teammitglieder brauchen DatenThunderbit
Daten sofort, ohne 403-DebuggingThunderbit
Anreicherung von Unterseiten ohne SetupThunderbit

Zeitvergleich: Python-Setup plus Anti-Bot-Debugging = Stunden bis Tage, besonders beim ersten Mal. Thunderbit = unter 2 Minuten für dieselben Daten. Ich sage nicht, dass Python falsch ist – ich sage nur, dass es von deinem Ziel abhängt.

Keine der führenden Indeed-Scraping-Guides spricht die Rechtslage an – erstaunlich, wenn man bedenkt, wie oft in Foren die Frage „Ist das Scraping von Indeed legal?“ auftaucht. Das hier ist keine Rechtsberatung, aber hier ist der Überblick.

Indeeds Nutzungsbedingungen

In den Indeed-AGB () gibt es keine pauschale Klausel „kein Scraping“. Das einzige ausdrückliche Verbot von Automatisierung steht in Abschnitt A.3.5 und untersagt die „Verwendung jeglicher Automatisierung, Skripte oder Bots zur Automatisierung des Indeed-Apply-Prozesses“. Das ist eng auf den Bewerbungs-Flow zugeschnitten, nicht auf das passive Auslesen öffentlicher Stellenanzeigen. Indeeeds Hauptdurchsetzung ist technischer Natur – Cloudflare-Challenges, IP-Bans, Device Fingerprinting – nicht ein Gerichtsverfahren.

Relevante Rechtsprechung

Der meistzitierte US-Fall ist hiQ Labs v. LinkedIn. Das 9th Circuit Court , dass das Scrapen öffentlich zugänglicher Daten „wahrscheinlich nicht gegen den CFAA“ (Computer Fraud and Abuse Act) verstößt. Allerdings wurde hiQ später wegen , weil Mitarbeiter Fake-LinkedIn-Profile angelegt und den AGB zugestimmt hatten.

Noch jüngst führte Meta v. Bright Data (N.D. Cal., Jan. 2024) zu einer noch klareren Entscheidung. Richter Chen , dass Facebooks und Instagrams AGB das Scraping öffentlicher Daten im ausgeloggten Zustand nicht verbieten. Meta zog die verbleibenden Ansprüche im folgenden Monat freiwillig zurück.

Indeeds robots.txt

Indeeeds untersagt für den Standard-User-agent: * im Allgemeinen /jobs/ und /job/, erlaubt Googlebot und Bingbot aber ausdrücklich den Zugriff auf /viewjob? – also auf die einzelnen Job-Detailseiten. AI-Trainings-Crawler (GPTBot, CCBot, anthropic-ai) sind stark eingeschränkt. robots.txt ist in den USA nicht rechtsverbindlich, aber die Beachtung gilt als Best Practice und als Zeichen guten Willens.

Praktische Leitlinien für verantwortungsvolles Scraping

  • Nur öffentlich verfügbare Daten scrapen – niemals einloggen, niemals Fake-Accounts anlegen
  • Rate Limits respektieren: 1 Request alle 3–6 Sekunden pro IP, nur geringe Parallelität
  • Gescrapte Daten nicht als eigenes Jobportal neu veröffentlichen
  • Daten nur für persönliche oder interne Recherche nutzen, nicht für kommerziellen Weiterverkauf ohne Erlaubnis
  • Nicht benötigte personenbezogene Daten löschen oder hashen; für personennahe Daten eine Aufbewahrungsgrenze setzen
  • Bei großem Umfang oder in der EU/UK einen Anwalt konsultieren – für gescrapte personenbezogene Daten gelten die Transparenzpflichten aus Artikel 14 DSGVO

Die Risikospanne: Automatisierte persönliche Jobsuche liegt am unteren Ende. Der groß angelegte kommerzielle Weiterverkauf von Indeed-Daten liegt am oberen Ende.

Fazit und wichtigste Erkenntnisse

Indeed mit Python zu scrapen ist machbar, aber kein Wochenendprojekt, das man einmal aufsetzt und dann vergisst. Indeeds Cloudflare-Schutz, wechselnde Selektoren und aggressive Anti-Bot-Maßnahmen bedeuten, dass du mit den richtigen Werkzeugen und realistischen Erwartungen herangehen musst.

Das würde ich aus allem mitnehmen:

  • Indeed ist die reichste Quelle für Arbeitsmarktdaten im Web – 350 Millionen monatliche Besucher, 130 Millionen Anzeigen – aber die Plattform wehrt sich heftig gegen Scraper.
  • Die Extraktion von Hidden JSON (window.mosaic.providerData) ist der robusteste Python-Ansatz. Das Schema ist seit Jahren stabil, während CSS-Selektoren monatlich brechen.
  • curl_cffi mit Browser-Imitation ist 2026 der Standard-HTTP-Client für Cloudflare-geschützte Seiten. Einfache requests- und httpx-Aufrufe werden allein wegen des TLS-Fingerprints geblockt.
  • Nutze immer rotierende Header, zufällige Pausen und Residential Proxies, um 403-Fehler zu vermeiden. Datacenter-Proxies sind gegen Cloudflare Enterprise fast nutzlos.
  • Baue Assertion-Checks ein, damit du sofort merkst, wenn Selektoren brechen oder du statt echter Jobdaten eine Challenge-Seite bekommst.
  • Für Nicht-Techniker oder alle, die einfach schnell Ergebnisse wollen, bietet einen No-Code-, KI-gestützten Weg, der sich automatisch an Seitenänderungen anpasst – ohne Proxies, ohne Debugging, ohne Wartung.

Wenn du den No-Code-Weg ausprobieren möchtest, bietet , sodass du es ohne Verpflichtung an Indeed testen kannst. Und wenn du den Python-Weg gehst, sind die obigen Codebeispiele ein solider Startpunkt – behandle Bot-Schutz aber unbedingt als zentrales Thema, nicht als nachträgliche Nebensache.

Mehr zu Web-Scraping-Ansätzen und Tools findest du in unseren Anleitungen zu , und . Außerdem kannst du Tutorials auf dem ansehen.

Thunderbit ausprobieren, um Indeed-Daten schneller zu scrapen

FAQs

Welche Python-Bibliotheken sind am besten für das Scrapen von Indeed?

Für HTTP-Requests ist curl_cffi im Jahr 2026 die stärkste Wahl – es imitiert echte Browser-TLS-Fingerprints, was für das Umgehen von Cloudflare entscheidend ist. httpx mit HTTP/2 ist eine brauchbare Alternative für weniger geschützte Ziele. Für das HTML-Parsing bleibt BeautifulSoup4 mit lxml der Standard. Für Browser-Automatisierung funktionieren Playwright (mit playwright-stealth) oder undetected-chromedriver, auch wenn beide zunehmend erkennbar sind. Der Hidden-JSON-Regex-Ansatz (window.mosaic.providerData) erspart dir schwere Parsing-Logik fast vollständig.

Warum bekomme ich beim Scrapen von Indeed ständig 403-Fehler?

Indeed nutzt Cloudflare Bot Management, das deinen TLS-Fingerprint (JA3/JA4), die Reihenfolge der HTTP/2-Header, Request-Muster und Browser-Verhalten auswertet. Wenn du einfach requests verwendest, identifiziert dich der TLS-Fingerprint sofort als Python-Skript – der 403 kommt, noch bevor deine Header gelesen werden. Abhilfe: curl_cffi mit Browser-Imitation, rotierende realistische User-Agent-Strings, zufällige Pausen von 3–6 Sekunden und Residential Proxies. Prüfe außerdem den Fall „200 mit Turnstile-Challenge“ – durchsucht den Response-Body nach cf-turnstile.

Kann ich Indeed auch ohne Programmierung scrapen?

Ja. Tools wie ermöglichen es dir, Indeed-Stellenanzeigen mit wenigen Klicks zu extrahieren – Chrome-Erweiterung installieren, Indeed-Suchseite öffnen, auf „AI-Felder vorschlagen“ klicken und dann auf „Scrapen“. Thunderbits KI erkennt Felder wie Jobtitel, Unternehmen, Ort und Gehalt automatisch. Pagination, Subpage-Anreicherung (vollständige Jobbeschreibungen) und Bot-Schutz werden ebenfalls automatisch behandelt. Export nach CSV, Google Sheets, Airtable oder Notion ist kostenlos.

Wie oft ändert Indeed seine HTML-Struktur?

Indeed rotiert CSS-Klassennamen regelmäßig (z. B. css-1m4cuuf, zufällig generierte Hash-Strings) und baut DOM-Elemente ohne Vorwarnung um. A/B-Tests bedeuten, dass unterschiedliche Nutzer gleichzeitig verschiedene Layouts sehen können. Der Hidden-JSON-Ansatz (window.mosaic.providerData) ist deutlich stabiler – das Schema ist seit mindestens 2023 konsistent. Wenn du DOM-Selektoren verwenden musst, greife lieber auf data-testid-Attribute und data-jk (Job-Key) zu als auf CSS-Klassen.

Das Scrapen öffentlich zugänglicher Indeed-Jobseiten im ausgeloggten Zustand dürfte in den USA nach der 9th-Circuit-Entscheidung hiQ v. LinkedIn (2022) und dem Urteil Meta v. Bright Data (2024) wahrscheinlich keine CFAA-Haftung auslösen. In den Indeed-AGB ist speziell die Automatisierung des Apply-Prozesses verboten, nicht das passive Lesen öffentlicher Anzeigen. Trotzdem gilt: verantwortungsvoll scrapen, nicht einloggen, keine Fake-Accounts anlegen, Rate Limits beachten, Daten nicht als eigenes Jobportal weiterveröffentlichen und personenbezogene Daten wie Recruiter-Namen oder E-Mails unter DSGVO/CCPA sorgfältig behandeln. Für kommerzielle Großprojekte solltest du einen Anwalt konsultieren.

Mehr erfahren

Fawad Khan
Fawad Khan
Fawad writes for a living, and honestly, he kind of loves it. He's spent years figuring out what makes a line of copy stick — and what makes readers scroll past. Ask him about marketing, and he'll talk for hours. Ask him about carbonara, and he'll talk longer.
Inhaltsverzeichnis

Teste Thunderbit

Leads und andere Daten in nur 2 Klicks scrapen. Unterstützt durch KI.

Thunderbit holen Es ist kostenlos
Daten mit KI extrahieren
Daten einfach zu Google Sheets, Airtable oder Notion übertragen
Chrome Store Rating
PRODUCT HUNT#1 Product of the Week