Hoe je Indeed scrapeert met Python (en 403-fouten voorkomt)

Laatst bijgewerkt op April 16, 2026

Rond mijn vijftigste keer dat ik een functietitel van Indeed naar een spreadsheet zat te kopiëren, begon ik me serieus af te vragen of ik wel de juiste carrièrekeuzes maakte. Als je ooit geprobeerd hebt om programmatic gestructureerde data van Indeed te halen, ken je de clou al: de 403-fout is geen bug — het is een onderdeel van Indeed’s verdedigingssysteem.

Indeed is ’s werelds grootste vacaturesite, met ongeveer , op elk moment en activiteiten in . Daarmee is het een van de rijkste bronnen van arbeidsmarktdata op aarde — én een van de lastigste om te scrapen. De open-source scraper JobFunnel (duizenden GitHub-stars) werd in december 2025 zelfs , na jaren verlies in de anti-bot-wapenwedloop. De eigen woorden van de maintainer: "Alle gebruikers kunnen wat vacatures scrapen, maar worden al snel geraakt door captcha, waarna het scrapen faalt en er geen vacatures overblijven." Een andere bijdrager meldde een CAPTCHA . Kortom: dit is geen simpel scrape-doelwit. In deze gids laat ik je alle praktische manieren zien om Indeed met Python te scrapen, hoe je de 403-muur daadwerkelijk omzeilt, en — voor wie liever helemaal niet debugt — een no-code alternatief met .

Wat betekent het om Indeed te scrapen met Python?

Webscraping is in de kern het automatisch ophalen van gestructureerde data uit webpagina’s. Als we het hebben over Indeed scrapen met Python, bedoelen we een script schrijven dat Indeed’s zoekresultaten en vacaturepagina’s bezoekt, de onderliggende HTML (of ingebedde data) uitleest en velden zoals functietitel, bedrijf, locatie, salaris en beschrijving verzamelt in een bruikbaar formaat — een CSV, database of Google Sheet.

De gebruikelijke Python-bibliotheken zijn Requests (voor HTTP-calls), BeautifulSoup (voor HTML-parsing) en Selenium of Playwright (voor browserautomatisering). Maar Indeed is geen eenvoudige statische site. Het is een hybride: server-renderde HTML met een ingebedde JSON-state blob, beschermd door Cloudflare Bot Management. Dat betekent dat je scraper moet omgaan met JavaScript-gerenderde content, wisselende CSS-classnamen en agressieve anti-botbescherming — nog vóór je ook maar één functietitel hebt geparsed.

Er is in 2026 ook geen officiële, gratis, read-only Indeed API. De oude Publisher Jobs API is rond 2020 uitgefaseerd, en wat overblijft is alleen beschikbaar voor werkgevers (Job Sync, Sponsored Jobs). Scrapen of betalen voor een externe dataprovider zijn dus de enige realistische opties.

Waarom zou je Indeed-vacaturedata scrapen?

De business case voor Indeed scrapen is simpel: handmatig duizenden vacatures doorzoeken is onpraktisch, en de data in die vacatures is echt waardevol.

indeed_stats_dca2a43cec.png

Use caseWie profiteertVoorbeeld
LeadgeneratieSales- en recruitmentteamsLijsten opbouwen van bedrijven die aannemen, inclusief contactgegevens
Onderzoek naar de arbeidsmarktAnalisten, HR-teamsTrends in vaardigheden en salarisbenchmarks per regio identificeren
ConcurrentieanalyseWerkgevers, detacheerdersWervingspatronen en salarisaanbiedingen van concurrenten monitoren
Persoonlijke baanzoek-automatiseringWerkzoekendenVacatures verzamelen die aan je criteria voldoen, over meerdere locaties
Trainingsdata voor ML-modellenData scientistsSalarisvoorspellingsmodellen bouwen op basis van historische vacaturedata

Indeed Hiring Lab’s eigen onderzoek dat vacaturedata nauw samenhangt met BLS JOLTS en bijna realtime inzicht kan geven in de Amerikaanse arbeidsmarkt. Hedgefondsen gebruiken vacature-activiteit als alternatieve datasignaal. HR-teams benchmarken salarissen op basis van gescrapete salarisranges. En recruiters bouwen prospectlijsten van bedrijven die actief aan het werven zijn.

Eén praktische kanttekening: salarisdata op Indeed wordt beter, maar is nog steeds onvolledig. Medio 2025 bevat ongeveer salarisinformatie, maar slechts zo’n geeft een exact bedrag; de rest werkt met ranges. Elke salarisanalyse op basis van Indeed-data moet met die onvolledigheid rekening houden.

Je methode kiezen om Indeed te scrapen met Python

Er is niet één “juiste” manier om Indeed te scrapen. De beste aanpak hangt af van je niveau, hoeveel data je nodig hebt en hoeveel onderhoud je bereid bent te accepteren. Ik heb de vier belangrijkste aanpakken getest, en dit is hoe ze zich tot elkaar verhouden:

CriteriaBS4 + RequestsSeleniumVerborgen JSON (window.mosaic)No-code (Thunderbit)
MoeilijkheidsgraadBeginnerGemiddeldGemiddeld-gevorderdGeen (2 klikken)
SnelheidSnelTraag (browser-rendering)SnelSnel (cloudscraping)
JS-gerenderde contentNeeJaJa (ingebedde data)Ja
Anti-bot-bestendigheidLaagGemiddeld (detecteerbaar)Gemiddeld-hoogHoog (automatisch afgehandeld)
Onderhoud bij HTML-wijzigingenHoog (selectors breken)HoogGemiddeld (JSON-structuur stabieler)Geen (AI past zich aan)
Beste voorSnelle prototypesDynamische pagina’s, login-afgeschermde contentGrote hoeveelheden gestructureerde dataNiet-developers, snelle resultaten

Deze gids loopt door elke methode heen. Ben je Python-developer, dan zijn vooral de secties over BS4, verborgen JSON en Selenium nuttig. Ben je geen codeerder — of ben je klaar met 403-debuggen — sla dan door naar het Thunderbit-gedeelte.

Voor je begint

  • Moeilijkheidsgraad: Beginner tot gemiddeld (Python-gedeelte); geen (Thunderbit-gedeelte)
  • Benodigde tijd: ~20–60 minuten voor Python-setup en eerste scrape; ~2 minuten met Thunderbit
  • Wat je nodig hebt: Python 3.9+, een code-editor, Chrome en (voor de no-code route) de

Je Python-omgeving voorbereiden voor Indeed scraping

Voordat je scraping-code schrijft, moet je omgeving klaarstaan.

Installeer de benodigde libraries

Maak een virtuele omgeving aan en installeer de pakketten die je nodig hebt:

1python -m venv indeed_env
2source indeed_env/bin/activate  # Op Windows: indeed_env\Scripts\activate
3# Voor de HTTP + parsing-aanpak
4pip install requests beautifulsoup4 lxml httpx
5# Voor de verborgen JSON-aanpak (aanbevolen)
6pip install curl_cffi parsel tenacity
7# Voor browserautomatisering
8pip install selenium

Een paar opmerkingen:

  • curl_cffi is in 2026 de standaard voor het scrapen van Cloudflare-beveiligde sites. Het bootst echte browser-TLS-fingerprints na, iets wat gewone requests en httpx niet kunnen. Meer hierover in de anti-botsectie.
  • Selenium 4.6+ wordt geleverd met Selenium Manager, dus je hoeft ChromeDriver niet meer handmatig te downloaden — de browserbinary wordt automatisch beheerd.
  • Gebruik lxml als parser-backend voor BeautifulSoup. Die is ongeveer dan de stdlib html.parser.

Maak je projectstructuur aan

Hou het simpel:

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

Alle codevoorbeelden hieronder bouwen voort op scraper.py.

Hoe je Indeed scrapt met Python met BeautifulSoup

Dit is de beginnersvriendelijke aanpak: gebruik requests om de pagina op te halen en BeautifulSoup om de HTML te parsen. Het is het snelst om op te zetten, maar op Indeed ook het meest kwetsbaar.

Stap 1: Bouw de Indeed-zoek-URL

Indeed’s zoek-URL’s volgen een voorspelbaar patroon:

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

Bijvoorbeeld, zoeken op “data analyst” in “Austin, TX”, vanaf de eerste pagina:

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 pagineert in stappen van 10, met een harde bovengrens van 1.000 resultaten (start <= 990). Elke offset boven 990 geeft stilletjes dezelfde pagina terug.

Stap 2: Stuur een HTTP-verzoek met de juiste headers

Indeed blokkeert requests met de standaard Python user-agent direct. Je hebt realistische headers nodig:

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)

Krijg je 200, dan zit je voorlopig goed. Krijg je 403, dan heeft Cloudflare je gepakt. (Hoe je dat overleeft lees je hieronder.)

Stap 3: Parse vacatures uit de HTML

Gebruik BeautifulSoup om vacaturekaarten te selecteren. Richt je op data-testid-attributen — die zijn stabieler dan de willekeurige CSS-classnamen van Indeed:

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"{len(jobs)} vacatures gevonden")

Stap 4: Pagina’s doorlopen

Loop door de pagina’s door de start-parameter op te hogen:

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

Beperkingen van deze aanpak

Ik ben er eerlijk over: BS4 + Requests is in 2026 de zwakste methode voor Indeed. Gewone requests gebruikt de TLS-bibliotheek uit de Python-stdlib, waardoor een ontstaat die Cloudflare meteen herkent als “geen browser.” Bovendien ondersteunt het geen HTTP/2, terwijl Indeed dat wel gebruikt. Grote kans dat je na een paar pagina’s al wordt geblokkeerd. En die CSS-selectors? Indeed roteert classnamen zoals css-1m4cuuf en jobsearch-JobComponent-embeddedBody-1n0gh5s — dus elke selector die daarop leunt is een tikkende tijdbom.

Gebruik deze methode alleen voor snelle prototypes op één pagina. Voor alles wat groter is, gebruik je beter de verborgen JSON-aanpak.

Hoe je Indeed scrapt met Python via verborgen JSON-data

Dit is de methode die ik de meeste Python-developers zou aanraden. In plaats van fragiele HTML-elementen te parsen, haal je gestructureerde data uit een JavaScript-variabele die in de broncode van Indeed zit: window.mosaic.providerData["mosaic-provider-jobcards"].

Elk veld dat je nodig hebt — functietitel, bedrijf, locatie, salaris, job key, plaatsingsdatum, remote-flag — zit al in die JSON-blob. JavaScript uitvoeren is niet nodig. De schema-structuur is , waardoor dit veel robuuster is dan DOM-selectors.

Stap 1: Haal de HTML van de pagina op

Gebruik curl_cffi in plaats van requests — het bootst echte browser-TLS-fingerprints na, wat cruciaal is om Cloudflare te overleven:

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))

Waarom curl_cffi? Het is een Python-binding rond curl-impersonate, die de exacte TLS ClientHello, HTTP/2 SETTINGS-frame en header-volgorde van echte browsers nabootst. Het is de enige actief onderhouden Python HTTP-client die in één call kan omzeilen. Ondersteunde impersonaties zijn onder meer chrome120, chrome124, chrome131, Safari en Edge-varianten.

Stap 2: Extraheer de JSON met een regex

De JSON-blob zit ingebed in een <script>-tag. Haal hem eruit met een regex:

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"{len(results)} vacatures gevonden in verborgen JSON")
11else:
12    print("Verborgen JSON niet gevonden — mogelijk blokkade of wijziging op de pagina")

Stap 3: Parse vacaturevelden uit de JSON

Elk item in results bevat meer data dan wat je op de pagina ziet:

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    })

De JSON bevat vaak salarisinschattingen, taxonomie-attributen (skill-tags) en bedrijfsbeoordelingen die niet altijd zichtbaar zijn in de gerenderde HTML.

Stap 4: Scrape meerdere pagina’s

Gebruik tierSummaries in de JSON om het totaal aantal resultaten te begrijpen en loop vervolgens door de pagina’s:

1import time, random
2all_jobs = []
3for start in range(0, 50, 10):  # Eerste 5 pagina’s
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"Totaal: {len(all_jobs)} vacatures gescrapet")

Waarom verborgen JSON robuuster is

De window.mosaic.providerData-structuur verandert minder vaak dan CSS-classnamen. Je krijgt nette, gestructureerde data zonder rommelige HTML te hoeven parsen. Dat gezegd hebbende, je hebt nog steeds anti-botmaatregelen nodig (headers, vertragingen, proxies) — daar komen we zo op terug.

Hoe je Indeed scrapt met Python via Selenium

Selenium is de browserautomatiseringsaanpak. Handig als je met de pagina moet interacteren — bijvoorbeeld door in vacaturepanelen te klikken, login-afgeschermde content te behandelen of dynamisch geladen beschrijvingen te scrapen die niet in de initiële HTML staan.

Wanneer Selenium gebruiken in plaats van HTTP-clients

  • Indeed laadt sommige content dynamisch in (volledige vacaturebeschrijvingen in het rechterpaneel)
  • Je moet pagina’s scrapen waarvoor sessiestatus of login nodig is
  • Je doet kleinschalige scraping waarbij snelheid minder belangrijk is

Snelle walkthrough

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 is makkelijker te detecteren — voorzichtig gebruiken
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()

Beperkingen

Selenium is traag — elke pagina moet volledig in de browser worden gerenderd. Headless Chrome is (Cloudflare controleert navigator.webdriver, WebGL-vendor strings, pluginaantallen en meer). Zelfs undetected-chromedriver stelt detectie alleen uit; het voorkomt die niet permanent. En net als BS4 breken je selectors zodra Indeed zijn UI aanpast.

Voor de meeste use-cases geeft de verborgen JSON-aanpak je dezelfde data, sneller en met minder onderhoud. Bewaar Selenium voor uitzonderingen waarbij je echt een browser nodig hebt.

Hoe je 403-fouten voorkomt bij het scrapen van Indeed met Python

Dit is het onderdeel dat het meest telt. Als je hier bent beland via een gefrustreerde Google-zoekopdracht, zit je goed.

indeed_antibot_374d080ff4.png

Waarom Indeed je scraper blokkeert

Indeed gebruikt — niet DataDome, niet PerimeterX. De response-headers verraden het: server: cloudflare, cf-ray en de __cf_bm bot-management cookie. Cloudflare controleert je TLS-fingerprint (JA3/JA4), de volgorde van HTTP/2-headers, requestpatronen en browsergedrag. Als daar iets niet menselijk uitziet, krijg je een 403, 429, 503 — of, het lastigste geval, een 200 OK met een Turnstile-uitdaging in plaats van echte vacaturedata.

Roteer User-Agent en requestheaders

Een vaste User-Agent is de snelste manier om geblokkeerd te worden. Gebruik liever een pool met actuele, realistische strings. Belangrijk: Chrome’s minor-versionvelden zijn sinds User-Agent Reduction — verzin dus geen niet-nul minor versions, want anti-bots flaggen dat meteen.

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}

Zorg er ook voor dat je sec-ch-ua Client Hints overeenkomen met de UA-versie. Een sec-ch-ua: "Chrome";v="131" naast een User-Agent die Chrome 145 claimt, is een direct alarmsignaal.

Voeg willekeurige vertragingen tussen requests toe

Vaste intervallen worden op patroonherkenning gepakt. Gebruik daarom random jitter:

1import time, random
2# Tussen requests
3time.sleep(random.uniform(3, 6))
4# Bij een retry na een blokkade
5def backoff_sleep(attempt):
6    base = 4
7    sleep_time = base * (2 ** attempt) + random.uniform(0, 2)
8    time.sleep(min(sleep_time, 60))

De praktische consensus van en is 3–6 seconden tussen requests per IP, met een harde grens van ongeveer 100 requests per IP per sessie vóór rotatie.

Gebruik proxyrotatie

Dit is de grootste succesfactor. Datacenter-proxy’s uit AWS/GCP-ranges halen op Cloudflare Enterprise-doelen ongeveer 5–15% succes — praktisch onbruikbaar op Indeed. Residential proxies in combinatie met de juiste TLS-fingerprinting brengen dat op 80–95% succes.

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)

Residential proxyprijzen liggen in 2026 grofweg op , afhankelijk van provider en contract. Begin voor Indeed met een kleine pool en schaal pas op als dat nodig is.

Handel 403-, 429- en 503-statuscodes netjes af

Ga niet blind blijven retry’en. Verschillende statuscodes betekenen verschillende dingen:

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            # Check ook het verraderlijke geval van "200 met challenge"
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 — geblokkeerd. Proxy roteren, poging {attempt + 1}")
18            elif r.status_code == 429:
19                print(f"429 — rate limit bereikt. Vertragen.")
20            elif r.status_code == 503:
21                print(f"503 — server overbelast of JS-challenge.")
22            backoff_sleep(attempt)
23        except Exception as e:
24            print(f"Requestfout: {e}")
25            backoff_sleep(attempt)
26    raise RuntimeError(f"Mislukt na {max_retries} retries: {url}")

De 200-met-challenge-case is de lastigste. Controleer daarom altijd de response-body op cf-turnstile of Just a moment vóór je een 200 als succes beschouwt.

Het eenvoudigere alternatief: laat Thunderbit de anti-botlaag voor je afhandelen

Voor gebruikers die geen proxy-pools, headerrotatie en TLS-fingerprint-impersonatie willen bouwen en onderhouden, regelt cloudscraping CAPTCHA’s, proxyrotatie en anti-botbescherming automatisch. Geen proxy-setup, geen curl_cffi-configuratie, geen CAPTCHA-oplossers. Het is de weg van de minste weerstand als je gewoon data nodig hebt.

Waarom je Indeed-scraper steeds stukgaat (en hoe je dat oplost)

De 403-muur is de acute pijn. De chronische pijn is onderhoud — scrapers die vandaag werken en volgende week stilletjes lege of verouderde data teruggeven.

Hoe Indeed je selectors breekt

Indeed roteert CSS-classnamen agressief. Bright Data waarschuwt in zijn gids dat klassen als css-1m4cuuf en css-1rqpxry “willekeurig gegenereerd lijken — waarschijnlijk tijdens build time.” A/B-testen zorgen ervoor dat verschillende sessies verschillende layouts zien. En DOM-herstructurering gebeurt zonder waarschuwing.

De JobFunnel-geschiedenis is leerzaam. Een bijdrager meldde: "CaptchaBuster heeft de captcha succesvol gemitigeerd, en de reden dat het paginaparsen nog steeds niet lukt [is] verouderde BeautifulSoup-selectors." De scraper werd dus niet geblokkeerd — hij parseerde simpelweg de verkeerde elementen.

Strategie: geef prioriteit aan verborgen JSON boven DOM-parsing

De window.mosaic.providerData-blob is schema-technisch al stabiel sinds minstens 2023. Het pad metaData.mosaicProviderJobCardsModel.results[] is in . DOM-selectors breken maandelijks. JSON-extractie breekt hooguit jaarlijks, als het al gebeurt.

Strategie: gebruik data-attributen in plaats van classnamen

Als je toch de DOM nodig hebt, richt je dan op functionele attributen:

SelectorDoel
[data-testid="slider_item"]Container van elke vacaturekaart
[data-testid="job-title"] of h2.jobTitle > aLink met functietitel
[data-testid="company-name"]Naam van de werkgever
[data-testid="text-location"]Locatietekst
data-jk="<jobkey>" op elke kaartDe stabielste hook — onveranderd sinds 2019

Voeg assertion-checks toe om verouderde selectors te detecteren

Laat je scraper nooit stilletjes met nul resultaten doorlopen. Voeg na elke fetch een check toe:

1results = parse_hidden_json(html)
2assert len(results) &gt; 0, (
3    f"Indeed gaf een lege set terug bij start={start} — "
4    "mogelijke blokkade, CAPTCHA of selector-drift. "
5    f"Eerste 500 tekens van de response: {html[:500]}"
6)

Log bij fouten de eerste 500–2000 tekens van de ruwe response. Zo zie je meteen of je een Turnstile-uitdaging, een inlogmuur of een schemawijziging kreeg. Draai dagelijks een kleine CI-smoketest op een vaste query (bijv. q=python&l=remote) die controleert of er resultaten zijn.

Het AI-alternatief: scrapers die niet stukgaan

Thunderbit’s AI leest de pagina-structuur telkens opnieuw — het leunt niet op hardcoded selectors of regexpatronen. Als Indeed zijn HTML aanpast, past Thunderbit automatisch mee aan. Daarmee pak je precies de onderhoudslast aan die forumgebruikers steeds als grootste frustratie noemen. Als je ooit wakker werd met een Slack-bericht dat “de scraper geeft weer lege rijen terug”, dan weet je hoe waardevol het is om dat niet steeds handmatig te hoeven fixen.

Indeed scrapen zonder Python te schrijven: het no-code alternatief

Elke concurrerende gids gaat ervan uit dat je Python-code gaat schrijven. Maar forumdata vertelt iets anders. Gebruikers zeggen dingen als "het is gewoon zo lastig met constante bugs en fouten" en sommigen stellen zelfs voor om iemand op Fiverr in te huren om de data ĂĽberhaupt uit de site te krijgen. Klinkt dat herkenbaar? Dan is dit je uitweg.

Hoe je Indeed scrapt met Thunderbit (stap voor stap)

Stap 1: Installeer de vanuit de Chrome Web Store. Je kunt gratis beginnen.

Stap 2: Ga in je browser naar een Indeed-zoekresultatenpagina — bijvoorbeeld https://www.indeed.com/jobs?q=data+analyst&l=Austin%2C+TX.

Stap 3: Klik op het Thunderbit-icoon in je werkbalk en klik vervolgens op "AI Suggest Fields". Thunderbit’s AI scant de pagina en herkent automatisch kolommen zoals Functietitel, Bedrijf, Locatie, Salaris, Vacature-URL en Publicatiedatum. Je kunt de voorgestelde velden bekijken en aanpassen — kolommen verwijderen die je niet nodig hebt, of aangepaste velden toevoegen door gewoon in normale taal te beschrijven wat je wilt.

Stap 4: Klik op "Scrape". Thunderbit haalt de data van de pagina en toont die in een gestructureerde tabel. Je zou rijen met vacatures moeten zien, inclusief de velden die je hebt ingesteld.

Verrijk met subpage-scraping

Na het scrapen van de overzichtspagina klik je op "Scrape Subpages" zodat Thunderbit elke individuele vacaturepagina bezoekt. Het haalt volledige functiebeschrijvingen, vereisten, voordelen en sollicitatielinks op — zonder extra setup. Dat is het equivalent van een tweede Python-scraper die elke /viewjob?jk=<jobkey>-URL bezoekt, alleen kost het je één klik.

Paginering automatisch afhandelen

Thunderbit handelt Indeed’s klik-gebaseerde paginering automatisch af. Je hoeft geen offset-URL’s te bouwen of paginaloops te schrijven. Het klikt door de pagina’s heen en voegt de resultaten samen.

Exporteer naar je favoriete tools

Exporteer gescrapete data gratis naar CSV, Excel, Google Sheets, Airtable of Notion . Je hoeft geen csv.writer() of pandas.to_csv()-code te schrijven.

Wanneer kies je Python en wanneer Thunderbit?

ScenarioBeste tool
Aangepaste datapijplijnen, geplande automatisering via cron/AirflowPython
Integratie in een grotere codebasePython
Sterk aangepaste parsinglogicaPython
Eenmalig onderzoek of marktanalyseThunderbit
Niet-technische teamleden hebben data nodigThunderbit
Direct data ophalen zonder 403-debuggingThunderbit
Verrijking van subpagina’s zonder setupThunderbit

Tijdsvergelijking: Python-setup + anti-bot-debuggen = uren tot dagen (zeker de eerste keer). Thunderbit = minder dan 2 minuten voor dezelfde data. Ik zeg niet dat Python fout is — ik zeg dat het afhangt van wat je nodig hebt.

Is het legaal om Indeed te scrapen? Wat je moet weten

Geen enkele top-ranked Indeed-scrapinggids bespreekt de legaliteit, wat opvallend is gezien hoe vaak "Is scraping van Indeed legaal?" in forums opduikt. Dit is geen juridisch advies, maar dit is het speelveld.

Indeed’s gebruiksvoorwaarden

Indeed’s ToS () bevat geen algemene “geen scraping”-clausule. De enige expliciete verbodsbepaling over automatisering staat in sectie A.3.5, waarin "het gebruik van automatisering, scripts of bots om het Indeed Apply-proces te automatiseren" wordt verboden. Dat is dus beperkt tot de Apply-flow, niet tot het passief lezen van openbare vacatures. Indeed’s belangrijkste handhavingsmiddel is technisch — Cloudflare-uitdagingen, IP-bans, device fingerprinting — niet juridisch.

Relevante juridische precedentvorming

De meest geciteerde Amerikaanse zaak is hiQ Labs v. LinkedIn. Het 9th Circuit dat het scrapen van publiek toegankelijke data “waarschijnlijk niet in strijd is met de CFAA” (Computer Fraud and Abuse Act). Later werd hiQ echter omdat werknemers nep-LinkedIn-profielen hadden aangemaakt en de ToS hadden geaccepteerd.

Recenter kwam Meta v. Bright Data (N.D. Cal., jan. 2024) met een nog duidelijker oordeel. Rechter Chen dat de gebruiksvoorwaarden van Facebook en Instagram “het uitgelogd scrapen van openbare data niet verbieden.” Meta trok de resterende claims de maand erop vrijwillig in.

Indeed’s robots.txt

Indeed’s staat voor de standaard User-agent: * in grote lijnen /jobs/ en /job/ niet toe, maar geeft Googlebot en Bingbot expliciet toegang tot /viewjob? — de afzonderlijke vacaturepagina’s. AI-trainingscrawlers (GPTBot, CCBot, anthropic-ai) zijn zwaar beperkt. robots.txt is in de VS niet juridisch bindend, maar het respecteren ervan is wel een best practice en een teken van goede trouw.

Praktische richtlijnen voor verantwoord scrapen

  • Scrape alleen publiek beschikbare data — nooit inloggen, nooit nepaccounts aanmaken
  • Respecteer rate limits: 1 request per 3–6 seconden per IP, met lage concurrency
  • Publiceer gescrapete data niet opnieuw als je eigen vacaturebank
  • Gebruik data voor persoonlijk of intern onderzoek, niet voor commerciĂ«le doorverkoop zonder toestemming
  • Verwijder of hash PII die je niet nodig hebt; stel een bewaartermijn in voor persoonsgerelateerde data
  • Werk je op schaal of in de EU/VK, raadpleeg dan een jurist — GDPR’s transparantieverplichtingen uit Artikel 14 gelden voor gescrapete persoonsgegevens

De risicoschaal: persoonlijke automatisering van je baanzoektocht zit aan de lage kant. Grootschalige commerciële wederverkoop van Indeed-data zit aan de hoge kant.

Conclusie en belangrijkste lessen

Indeed scrapen met Python kan, maar het is geen weekendproject dat je een keer opzet en daarna vergeet. Indeed’s Cloudflare-beveiliging, wisselende selectors en agressieve anti-botmaatregelen betekenen dat je hier de juiste tools én de juiste verwachtingen voor nodig hebt.

Dit zou ik hieruit meenemen:

  • Indeed is de rijkste bron van arbeidsmarktdata op het web — 350 miljoen maandelijkse bezoekers, 130 miljoen vacatures — maar het vecht hard terug tegen scrapers.
  • Verborgen JSON-extractie (window.mosaic.providerData) is de meest robuuste Python-aanpak. De schema-structuur is al jaren stabiel, terwijl CSS-selectors maandelijks breken.
  • curl_cffi met browser-impersonatie is in 2026 de standaard HTTP-client voor Cloudflare-beveiligde sites. Gewone requests en httpx worden al op TLS-fingerprintniveau geblokkeerd.
  • Gebruik altijd roterende headers, willekeurige vertragingen en residential proxies om 403-fouten te voorkomen. Datacenter-proxy’s zijn bijna nutteloos tegen Cloudflare Enterprise.
  • Voeg assertion-checks toe zodat je direct ziet wanneer selectors breken of je een challenge-pagina krijgt in plaats van vacaturedata.
  • Voor niet-technische gebruikers of iedereen die gewoon snel resultaat wil, biedt een no-code, AI-gedreven aanpak die automatisch meebeweegt met sitewijzigingen — geen proxies, geen debugging, geen onderhoud.

Als je de no-code route wilt proberen, heeft waarmee je het zonder verplichtingen op Indeed kunt testen. En als je voor de Python-route gaat, vormen de codevoorbeelden hierboven een prima startpunt — onthoud alleen dat anti-botbestendigheid een kernonderdeel moet zijn, niet iets voor later.

Voor meer over webscraping-aanpakken en tools, bekijk onze gidsen over , en . Je kunt ook tutorials bekijken op het .

Probeer Thunderbit om Indeed-data sneller te scrapen

FAQs

Welke Python-bibliotheken zijn het beste voor het scrapen van Indeed?

Voor HTTP-requests is curl_cffi in 2026 de sterkste keuze — het bootst echte browser-TLS-fingerprints na, wat essentieel is om Cloudflare te omzeilen. httpx met HTTP/2 is een redelijke fallback voor minder zwaar beveiligde targets. Voor HTML-parsing blijft BeautifulSoup4 met lxml de standaard. Voor browserautomatisering werken Playwright (met playwright-stealth) of undetected-chromedriver, al zijn beide steeds beter detecteerbaar. De verborgen JSON-aanpak (window.mosaic.providerData) maakt zware parsing helemaal overbodig.

Waarom krijg ik steeds 403-fouten bij het scrapen van Indeed?

Indeed gebruikt Cloudflare Bot Management, dat je TLS-fingerprint (JA3/JA4), de volgorde van HTTP/2-headers, requestpatronen en browsergedrag inspecteert. Als je gewone requests gebruikt, verraadt je TLS-fingerprint meteen dat je een Python-script bent — de 403 komt vaak al vóór je headers gelezen worden. Los dit op door over te stappen op curl_cffi met browser-impersonatie, realistische roterende User-Agent strings, willekeurige vertragingen (3–6 seconden) en residential proxies. Controleer ook op het geval van een “200 met Turnstile-challenge” — scan response bodies op cf-turnstile markers.

Kan ik Indeed scrapen zonder code te schrijven?

Ja. Met tools zoals kun je Indeed-vacatures in een paar klikken extraheren — installeer de Chrome-extensie, ga naar een Indeed-zoekpagina, klik op “AI Suggest Fields” en daarna op “Scrape.” Thunderbit’s AI herkent automatisch velden zoals functietitel, bedrijf, locatie en salaris. Het handelt paginering, verrijking van subpagina’s (volledige vacaturebeschrijvingen) en anti-botbescherming automatisch af. Exporteer gratis naar CSV, Google Sheets, Airtable of Notion.

Hoe vaak verandert Indeed de HTML-structuur?

Indeed roteert CSS-classnamen regelmatig (bijv. css-1m4cuuf, random gehashte strings) en wijzigt DOM-elementen zonder waarschuwing. A/B-testen zorgen ervoor dat verschillende gebruikers tegelijk verschillende layouts zien. De verborgen JSON-aanpak (window.mosaic.providerData) is aanzienlijk stabieler — het schema is minstens sinds 2023 consistent. Wanneer je toch DOM-selectors moet gebruiken, richt je dan op data-testid-attributen en data-jk (job key) in plaats van op CSS-classnamen.

Is het legaal om Indeed te scrapen?

Uitgelogd scrapen van publiek toegankelijke Indeed-vacature-URL’s levert volgens de Amerikaanse 9th Circuit-uitspraak in hiQ v. LinkedIn (2022) en de Meta v. Bright Data-zaak (2024) waarschijnlijk geen CFAA-aansprakelijkheid op. Indeed’s ToS verbiedt specifiek het automatiseren van het Apply-proces, niet het passief lezen van openbare listings. Dat gezegd hebbende: scrape altijd verantwoord — log niet in, maak geen nepaccounts aan, respecteer rate limits, publiceer data niet als je eigen vacaturebank en ga zorgvuldig om met persoonsgegevens (zoals namen en e-mails van recruiters) onder GDPR/CCPA. Raadpleeg bij commerciële schaal een jurist.

Meer leren

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.
Inhoudsopgave

Probeer Thunderbit

Scrape leads en andere data in slechts 2 klikken. Aangedreven door AI.

Thunderbit ophalen Het is gratis
Data extraheren met AI
Gegevens eenvoudig overzetten naar Google Sheets, Airtable of Notion
Chrome Store Rating
PRODUCT HUNT#1 Product of the Week