---
title: "Job scraping"
description: "Job scraping for postings, salaries and hiring signals from Indeed, LinkedIn, Glassdoor and Greenhouse, Lever or Workday career pages, as structured JSON."
canonical: https://www.zenrows.com/use-cases/job-scraping
updated: 2026-10-18
---

# Job scraping

Job scraping collects job postings from job boards and company career pages: title, employer, location, salary range, posting date and the full description. Zenrows fetches protected boards like Indeed and LinkedIn, crawls ATS career pages on Greenhouse, Lever and Workday, and returns each posting as JSON or Markdown for your job dataset.

## Sample output: one job posting

```json
{
  "url": "https://job-boards.example-ats.com/acme/jobs/4821930",
  "source": "employer_ats",
  "title": "Senior Data Engineer",
  "company": "Acme Analytics",
  "department": "Data Platform",
  "location": "Austin, TX",
  "workplace_type": "hybrid",
  "employment_type": "full_time",
  "salary": { "min": 155000, "max": 190000, "currency": "USD", "period": "year" },
  "posted_at": "2026-10-14",
  "skills": ["Python", "Spark", "Airflow", "dbt", "AWS"],
  "seniority": "senior",
  "description_markdown": "## About the role\nYou will own the batch pipelines that feed ...",
  "apply_url": "https://job-boards.example-ats.com/acme/jobs/4821930/apply",
  "first_seen": "2026-10-15",
  "last_seen": "2026-10-17",
  "is_live": true
}
```

An illustrative posting as structured JSON; `first_seen`, `last_seen` and `is_live` are fields your pipeline adds when it re-checks the URL on each run.

## What job data teams collect

Two kinds of source cover most job data projects. Job boards give breadth across employers, and the employer's own ATS career page is the most complete and current copy of each posting.

| Job | Data you collect | Typical sources |
|---|---|---|
| Job aggregation | Title, employer, location, description, apply URL | Indeed, LinkedIn Jobs, ZipRecruiter, StepStone, InfoJobs, Pracuj.pl |
| Career page monitoring | Every open role at a list of employers, with team and location | Greenhouse, Lever, Workday, SmartRecruiters, Workable and Teamtailor boards |
| Salary benchmarking | Posted pay ranges by title, level and city | Postings in pay-transparency states, Glassdoor, Indeed, government wage tables |
| Hiring signals for sales and investing | Open roles per company and function, tracked week over week | Company career pages, LinkedIn Jobs, Built In, Wellfound |
| Posting verification | Whether a listing is still live, moved or closed | The original posting URLs, re-fetched on a schedule |
| Labor market research | Required skills, seniority mix and remote share by region | Job descriptions across boards, converted to Markdown for NLP |

## Why job board scraping breaks

The largest boards protect their listings hard, and the long tail of employer career sites is the opposite problem: thousands of small sites, each built a little differently.

| What breaks | What it looks like | How Zenrows handles it |
|---|---|---|
| Bot protection on the big boards | 403s and challenge pages from the largest boards once volume picks up | Zenrows' stealth gets through Cloudflare, DataDome and similar protections, and Adaptive Stealth Mode renders the results when they need a browser |
| Challenge pages served with a 200 | Your parser stores a CAPTCHA page as an empty posting | Requests go out as real browser traffic; check a required field such as the title before you store a record |
| JavaScript career sites | Workday and many custom career pages load the job list after the page renders | Browser rendering waits for the job list element to load before the page is returned |
| Thousands of ATS layouts | One set of selectors per employer, and they drift | Extract returns the posting's fields without per-site selectors |
| Country redirects | Glassdoor and Indeed send you to a different country site or result set | Geotargeting fetches each market from inside its country |
| The same role on five boards | Duplicate postings inflate counts and skew salary data | Crawl the employer's ATS board as the canonical record and match board postings to it |

## How to scrape job postings with Zenrows

1. **Pick canonical sources first.** List the employers you care about and find their ATS board (Greenhouse, Lever, Workday). Use job boards for discovery beyond that list.
2. **Discover posting URLs.** Run Crawl on each career board with an include pattern such as `/jobs/`, or fetch board search pages for a title and location.
3. **Fetch each posting.** Name the fields with CSS selectors when you know the board's markup, or use automatic structured extraction for career pages you have never seen.
4. **Re-check on a schedule.** Submit every known posting URL to Batch daily or weekly, mark postings that disappear as closed, and store first-seen and last-seen dates.

```bash cURL
# Crawl one employer's ATS board and keep only posting pages
curl -X POST "https://api.zenrows.com/v1/crawls" \
  -H "X-API-Key: YOUR_ZENROWS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "CAREER_BOARD_URL",
    "max_items": 500,
    "max_pages": 700,
    "include_patterns": ["/jobs/"],
    "output_format": "markdown"
  }'
# Then read the postings: GET https://api.zenrows.com/v1/crawls/{crawl_id}
```

```python Python
# pip install requests
import json
import requests

params = {
    "apikey": "YOUR_ZENROWS_API_KEY",
    "url": JOB_SEARCH_URL,  # a board search page for one title and city
    "mode": "auto",  # Adaptive Stealth Mode
    "proxy_country": "us",
    # example selectors: match them to the board's own markup
    "css_extractor": json.dumps({
        "titles": ".job-card .job-title",
        "companies": ".job-card .company-name",
        "links": ".job-card a.job-link @href",
    }),
}
response = requests.get("https://api.zenrows.com/v1/", params=params)
print(response.json())  # titles, companies and links as lists
```

```bash CLI
npx -y @zenrows/cli init
# Fields from one posting on a career site you have never mapped
zenrows extract POSTING_URL --autoparse
# Nightly re-check: postings.jsonl holds one {"url": "..."} object per known posting
zenrows batch create postings.jsonl --output markdown --wait
zenrows batch results JOB_ID --out postings-today.jsonl
```

```text Claude Code (MCP)
# Connect once
claude mcp add zenrows -e ZENROWS_API_KEY=YOUR_ZENROWS_API_KEY -- npx -y @zenrows/mcp

# Then ask:
Use Zenrows to collect every open role on this company's Greenhouse board and return a table with title, team, location, workplace type, salary range where stated, and the posting URL.
```

## Which Zenrows product fits

| If you need to | Use | Why |
|---|---|---|
| Pull a board search page or a single posting | [Fetch](/products/fetch) | Returns the rendered page from Indeed, LinkedIn and Glassdoor through Adaptive Stealth Mode |
| Read postings from career sites you have never mapped | [Extract](/products/extract) | Automatic structured extraction returns title, location, salary and description without selectors |
| List every open role on an employer's ATS board | [Crawl](/products/crawl) | Follows the board's links and keeps only URLs that match `/jobs/` or your pattern |
| Re-verify 100,000 posting URLs every night | [Batch](/products/batch) | One job for the whole list, results by webhook or download |
| Run the same jobs from a terminal or a cron script | [CLI](/cli) | One command submits a JSONL file of URLs, with no client code to write |

## What success looks like

| Measure | What good looks like | How to check it |
|---|---|---|
| Employer coverage | Every employer on your list has its full set of open roles | Compare your role count per employer with the count on their careers page |
| Time to first seen | New postings enter your dataset within one refresh cycle of going live | Compare `first_seen` with the posting date the board shows |
| Closed-posting lag | Removed postings are marked closed by the next run | Count records still marked live whose URL now returns 404 or redirects |
| Salary field fill rate | Pay ranges are captured wherever the posting states one | Share of postings with a parsed salary, per source and per state |
| Duplicate rate | One record per real role, however many boards list it | Count clusters with the same employer, title and location |
| Empty-record rate | Almost no stored posting is missing its title or description | Count records without a title per source after each run |

## Job scraping FAQ

### Is job scraping legal?

Job postings are published so that as many people as possible see them, and collecting them for aggregation, research and analytics is common. Read each board's terms, do not collect data from behind a login, and treat recruiter names and contact details as personal data under GDPR or CCPA. Your legal team should review the specific use, especially if you republish full descriptions.

### What is the difference between a job scraper and a job scraping API?

A job scraper is usually a script or a browser extension that reads one board for one person. A job scraping API takes a URL and returns the page or its fields, and it handles proxies, rendering and protection for you, so the same code can run against Indeed, LinkedIn and thousands of employer career pages on a schedule.

### Can Zenrows scrape Indeed and LinkedIn job postings?

Yes. Adaptive Stealth Mode starts with the lightest setup and escalates to browser rendering and stealth only when a page needs it, and geotargeting picks the market you want. LinkedIn job pages that are visible without signing in can be fetched the same way. See the Indeed and LinkedIn guides for working parameters.

### How do I scrape Greenhouse, Lever and Workday career pages?

Send the board URL with Adaptive Stealth Mode: plain Greenhouse and Lever boards come back from the lightest setup, and boards that answer with a challenge page get Zenrows' stealth. Workday career sites build the job list with JavaScript, so Adaptive Stealth Mode renders them in a browser. Crawl any of them from the board's root URL with an include pattern to collect every posting.

### How do I get salary data from job postings?

Fetch the posting and read the pay range from the description or the structured salary block, which many boards and ATS pages include. Automatic structured extraction returns it as a field when the page states one. Normalize the period (hour, month, year) and currency before you compare titles.

### Is there a job data API for postings across many boards?

Zenrows works as one: send any job board or career page URL and get the posting back as JSON or Markdown. Use Crawl to list every role on an ATS board, Fetch with CSS selectors for boards you know, and Extract for career sites you have never seen. Batch runs the whole posting list nightly, so one pipeline feeds a single job postings dataset.

### How do I detect closed or expired job postings?

Keep every posting URL with a last_seen date and re-fetch the full list with Batch. A 404, a redirect to the board's search page, or a missing title means the role is closed.

### Can I build a job postings dataset for labor market research?

Yes. Fetch descriptions with Markdown output to get clean text for skill and seniority extraction, and store title, location, date and salary as separate fields. Running the same source list on a fixed schedule gives you a time series of postings by role, region and employer.

## Related use cases

- [Alternative data for investors](https://www.zenrows.com/use-cases/alternative-data.md): Hiring, pricing, app ranks, reviews and store counts per ticker, collected weekly as point-in-time series.
- [Web scraping for lead generation](https://www.zenrows.com/use-cases/web-scraping-for-lead-generation.md): Prospect lists built from directories, maps, exhibitor lists and member rosters, filtered to your ideal customer.
- [Firmographic data](https://www.zenrows.com/use-cases/firmographic-data.md): Industry, headcount, HQ, locations and funding per company, built from registries, company sites and directories.
- [Competitive intelligence](https://www.zenrows.com/use-cases/competitive-intelligence.md): Pricing, hiring, reviews, launches and partners across your market, structured for battlecards and strategy.
- [Market research web scraping](https://www.zenrows.com/use-cases/market-research-web-scraping.md): Assortments, prices, reviews and forum talk across a whole category, for sizing studies and recurring trackers.

Start free: https://app.zenrows.com/register. Docs: https://docs.zenrows.com
