v1.8.91-d84675c
ProxiesRecruitmentHow-to

Proxies for Recruitment: Scraping Job Boards Ethically and at Scale

11 min read

By Hex Proxies Engineering Team

Proxies for Recruitment: Scraping Job Boards Ethically and at Scale

The labor market generates enormous volumes of structured data every day. Job postings, salary ranges, company reviews, skill requirements, location distributions, and hiring velocity are all publicly visible on job boards. For recruitment firms, HR tech companies, labor economists, and workforce planning teams, this data is a strategic asset -- if you can collect it reliably.

The challenge is that every major job board has invested heavily in anti-bot technology. Indeed, LinkedIn, Glassdoor, and ZipRecruiter all deploy sophisticated detection systems that identify and block automated access. A scraper that worked six months ago may be completely blocked today because these platforms continuously update their defenses.

Proxies are the infrastructure layer that sustains job data collection at scale. This guide covers the technical architecture, ethical considerations, proxy configuration by platform, and the operational patterns that keep data pipelines running reliably. For proxy fundamentals, see our job board monitoring use case and recruiting industry page.

The Job Board Data Landscape

Platform Protection Levels

PlatformProtection TechnologyDetection SophisticationSuccess Rate (Residential)
LinkedInCustom + CloudflareVery high75-85%
IndeedPerimeterXHigh88-93%
GlassdoorCloudflare EnterpriseHigh85-90%
ZipRecruiterModerate CloudflareMedium92-96%
DiceBasic rate limitingLow97-99%
AngelList/WellfoundBasic CloudflareLow-Medium94-97%
Government portals (USAJobs, etc.)MinimalLow99%+
Niche industry boardsMinimal to basicLow97-99%
Success rates from Hex Proxies internal testing, Q1 2026. Residential proxies with per-request rotation, 3-5 second delays between requests.

LinkedIn stands out as the most challenging target. Its custom anti-bot system combines IP analysis, behavioral scoring, browser fingerprinting, and account-level tracking into a multi-layered defense that achieves detection rates above 90% against naive scraping attempts.

What Data Is Collectible

Job postings: Title, description, company, location, salary range (when listed), posting date, application URL, required skills, experience level, employment type.

Company data: Company size, industry, headquarters location, benefits, employee reviews, salary surveys, interview process details.

Market intelligence: Job volume trends by skill/role/location, salary range distributions, time-to-fill estimates (inferred from posting duration), demand signals for emerging skills.

Proxy Configuration by Platform

Indeed

Indeed uses PerimeterX with moderate JavaScript challenges. The key to sustained access:

Proxy type: Residential proxies with per-request rotation. ISP proxies work for low-volume queries but get flagged at scale.

Request pacing: 2-4 seconds between requests. Indeed's rate limiting is per-IP but also tracks patterns across IPs from the same subnet.

Geographic targeting: Indeed localizes results by IP. To see jobs in Chicago, use a US residential IP. To see jobs in London, use a UK IP. Use Hex Proxies' geo-targeting to match the target market.

import requests
import random
import time

def search_indeed(
    query: str,
    location: str,
    country: str = "us",
    max_pages: int = 20,
) -> list:
    """
    Search Indeed for job listings with residential proxy rotation.
    Each page load uses a fresh IP to avoid rate limiting.
    """
    proxy = {
        "http": f"http://USER-country-{country}:PASS@gate.hexproxies.com:8080",
        "https": f"http://USER-country-{country}:PASS@gate.hexproxies.com:8080",
    }
    
    all_jobs = []
    
    for page in range(max_pages):
        params = {
            "q": query,
            "l": location,
            "start": page * 10,
            "fromage": 14,  # Last 14 days
        }
        
        headers = {
            "User-Agent": random.choice(BROWSER_USER_AGENTS),
            "Accept-Language": "en-US,en;q=0.9",
        }
        
        response = requests.get(
            "https://www.indeed.com/jobs",
            params=params,
            proxies=proxy,
            headers=headers,
            timeout=20,
        )
        
        if response.status_code != 200:
            break
        
        jobs = parse_indeed_results(response.text)
        if not jobs:
            break
        
        all_jobs.extend(jobs)
        time.sleep(random.uniform(2, 5))
    
    return all_jobs

LinkedIn

LinkedIn requires the most careful approach. Their anti-bot system evaluates:

  • IP reputation (datacenter and ISP IPs are immediately suspicious)
  • Login state (many data points require authentication)
  • Browser fingerprint consistency
  • Navigation patterns (directly accessing a search results page without first visiting linkedin.com is suspicious)
Proxy type: Residential proxies only. ISP proxies produce success rates below 40% on LinkedIn.

Session strategy: Use sticky sessions with a 10-15 minute duration for each browsing "session." Within a session, maintain cookies and navigate naturally (homepage, then search, then results).

Authentication consideration: LinkedIn's richest data (full job descriptions, company insights, salary data) requires a logged-in session. Scraping authenticated LinkedIn at scale carries risk of account bans. For many use cases, public LinkedIn data (job titles, company names, locations from search results) is sufficient.

import requests
import uuid

def create_linkedin_session(country: str = "us") -> requests.Session:
    """
    Create a LinkedIn-optimized session with sticky residential IP.
    The session ID ensures the same IP persists for the session duration.
    """
    session_id = uuid.uuid4().hex[:12]
    
    session = requests.Session()
    session.proxies = {
        "http": f"http://USER-session-{session_id}-country-{country}:PASS@gate.hexproxies.com:8080",
        "https": f"http://USER-session-{session_id}-country-{country}:PASS@gate.hexproxies.com:8080",
    }
    session.headers.update({
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
                      "AppleWebKit/537.36 (KHTML, like Gecko) "
                      "Chrome/124.0.0.0 Safari/537.36",
        "Accept-Language": "en-US,en;q=0.9",
    })
    
    # Warm the session by visiting the homepage first
    session.get("https://www.linkedin.com/", timeout=15)
    time.sleep(random.uniform(2, 4))
    
    return session

Glassdoor

Glassdoor uses Cloudflare Enterprise and requires JavaScript rendering for most of its content (reviews, salary data, interview questions).

Proxy type: Residential proxies. Glassdoor actively blocks ISP ranges.

Browser rendering: Required for most Glassdoor data. Use Playwright with residential proxy configuration.

Rate limiting: Glassdoor is aggressive about rate limiting. Keep requests to 1-2 per minute per session. Use many concurrent sessions with different IPs rather than fast sequential requests.

Government and Niche Boards

USAJobs, state job portals, and niche industry boards (Dice for tech, HealthcareJobSite for medical, etc.) typically have minimal anti-bot protection.

Proxy type: ISP proxies for speed and cost efficiency. Unlimited bandwidth means you can collect data aggressively without per-GB cost concerns.

Rate limiting: Respect the sites' robots.txt and implement 1-2 second delays as a courtesy, even if the sites do not enforce rate limits.

Ethical Scraping: A Framework

Job data collection raises ethical questions that are worth addressing directly, not as legal disclaimers but as engineering principles.

Principle 1: Collect Only Public Data

If a data point requires a login to access, it is not public data. Scraping behind authentication walls (logging in with fake accounts, bypassing paywalls) is ethically distinct from collecting data that is freely visible to any visitor.

For recruitment data collection, most valuable data points (job titles, companies, locations, posting dates, and often salary ranges) are available on public search result pages without authentication.

Principle 2: Respect Rate Limits

Just because your proxy infrastructure can sustain 10,000 requests per minute does not mean you should. Excessive request rates impose real costs on the target sites (server load, bandwidth, CDN charges).

A reasonable rate: 3-5 requests per minute to any single domain, distributed across IPs via rotation. This mimics the traffic pattern of a few dozen real users and imposes negligible server load.

Principle 3: Honor robots.txt When Reasonable

Check the target site's robots.txt for explicit disallow rules. While robots.txt is advisory (not legally binding in most jurisdictions), respecting it demonstrates good faith.

Exception: some sites use overly broad robots.txt rules that disallow all automated access, including search engine crawlers. In these cases, practical judgment applies. If Google can crawl the data, it is public.

Principle 4: Do Not Republish Raw Data

Collecting data for analysis (market trends, salary benchmarks, demand signals) is different from republishing the data verbatim. Aggregated, transformed, and analyzed data creates new value. A direct copy of Indeed's listings does not.

Architecture: Production Job Data Pipeline

Pipeline Components

Scheduler → Job Board Crawlers → Proxy Layer → Raw Data Store → 
Deduplication → Enrichment → Analytics Database → API/Dashboard

Deduplication Strategy

The same job posting often appears on multiple boards. A software engineer role at Google might be listed on LinkedIn, Indeed, Glassdoor, Google Careers, and multiple aggregator sites. Without deduplication, your dataset inflates by 3-5x with duplicates.

Deduplication keys: company name (normalized) + job title (normalized) + location + posting date. Hash these four fields to create a unique identifier:

import hashlib

def generate_listing_id(company: str, title: str, location: str, date: str) -> str:
    """Generate a deterministic unique ID for deduplication."""
    normalized = f"{company.lower().strip()}|{title.lower().strip()}|{location.lower().strip()}|{date}"
    return hashlib.sha256(normalized.encode()).hexdigest()[:16]

Data Freshness and Refresh Strategy

Job postings are ephemeral. The average job posting is active for 30-45 days. A data pipeline must both discover new postings and detect removed postings.

Discovery: Run search result crawls daily for each target market and role category. New listings appear in search results automatically.

Removal detection: Re-check known listing URLs every 3-7 days. If a listing returns 404 or redirects to a "this job is no longer available" page, mark it as closed with the detection date.

Cost-efficient refresh: Only re-check listings that are still active. A listing confirmed active yesterday does not need re-checking today. Focus refresh bandwidth on listings last checked 3+ days ago.

Cost Analysis by Use Case

Recruitment Agency: Competitive Intelligence

A recruitment agency monitoring job openings across 500 target companies in a specific vertical (e.g., fintech), tracking 10 job boards, refreshing weekly.

  • ~15,000 job listings to monitor
  • 10 source sites × weekly refresh = 150,000 requests/month
  • Average 100 KB per request = 15 GB/month
  • Split: 10 GB residential (protected sites), 5 GB ISP (unprotected sites)
  • Residential cost: $42.50-$47.50/month
  • ISP cost: $20.80-$24.70/month (10 IPs, unlimited bandwidth)
  • Total: $63.30-$72.20/month

HR Tech Company: Market Analytics Platform

An HR tech platform providing labor market analytics across all US markets, tracking 50 job categories, refreshing daily.

  • ~500,000 active listings to track
  • 5 major source sites × daily refresh = 2,500,000 requests/month
  • Average 80 KB per request (API-targeted) = 200 GB/month
  • Primarily residential (major portals dominate volume)
  • Total: $850-$950/month

Labor Economist: Research Data Set

An academic or research team building a longitudinal job market dataset, sampling 100 markets monthly.

  • ~200,000 listings per monthly sample
  • 3 source sites per market = 600,000 requests/month
  • Average 100 KB per request = 60 GB/month
  • Total: $255-$285/month

Monitoring and Reliability

Success Rate Tracking

Track HTTP status codes by source platform:

# Track success rates per platform
metrics = {
    "indeed": {"success": 0, "blocked": 0, "error": 0},
    "linkedin": {"success": 0, "blocked": 0, "error": 0},
    "glassdoor": {"success": 0, "blocked": 0, "error": 0},
}

def record_result(platform: str, status_code: int):
    if status_code == 200:
        metrics[platform]["success"] += 1
    elif status_code in (403, 429):
        metrics[platform]["blocked"] += 1
    else:
        metrics[platform]["error"] += 1

Alert thresholds: If any platform's success rate drops below 85% over a 24-hour window, investigate. Common causes: platform updated their anti-bot system, your request headers are outdated, or you are hitting rate limits too aggressively.

Data Quality Checks

Beyond HTTP success rates, validate that the data you collect is actually correct:

  • Price/salary sanity checks: If a parsed salary is $0 or $10,000,000, the parser is likely broken.
  • Location validation: Cross-check parsed locations against a known city/state database.
  • Duplicate rate monitoring: If your deduplication rate suddenly spikes above 80%, a source may be serving cached or stale results.

Frequently Asked Questions

Is scraping job boards legal?

Publicly accessible job listing data is generally collectible under US law, particularly after the hiQ v. LinkedIn decision. However, scraping behind authentication, violating explicit terms of service, or republishing raw data may cross legal lines. Consult legal counsel for your specific use case.

Should I use headless browsers or raw HTTP requests?

Use raw HTTP requests wherever possible -- they are 10-50x more bandwidth-efficient than headless browsers. Only use headless browsers for platforms that render job data entirely in JavaScript (Glassdoor is the main example). For Indeed and most other platforms, raw HTTP requests with proper headers return complete data.

How do I handle LinkedIn's rate limiting?

LinkedIn's rate limiting is the most aggressive in the industry. Keep to 1-2 requests per minute per session, use sticky sessions with 10-15 minute durations, and warm sessions by visiting the homepage before search queries. Accept that LinkedIn data collection will be slower than other platforms.

What about using official APIs instead of scraping?

Indeed, LinkedIn, and others offer official APIs with various levels of access. These APIs are often limited in scope (fewer data points), require approval processes, and have usage caps. For comprehensive market data, web collection through proxies provides fuller coverage. Many organizations use both: official APIs for baseline data and proxy-based collection for supplementary intelligence.


Build your recruitment data pipeline with Hex Proxies. Residential proxies at $4.25-$4.75/GB handle protected platforms like Indeed and LinkedIn. ISP proxies at $2.08-$2.47/IP with unlimited bandwidth cover government boards and niche platforms. Visit our job board monitoring use case for more architecture patterns.

Cookie Preferences

We use cookies to ensure the best experience. You can customize your preferences below. Learn more