Job Board Aggregator — Multi-Platform Job Listing Scraper avatar

Job Board Aggregator — Multi-Platform Job Listing Scraper

Pricing

from $50.00 / 1,000 job postings

Go to Apify Store
Job Board Aggregator — Multi-Platform Job Listing Scraper

Job Board Aggregator — Multi-Platform Job Listing Scraper

One feed for the whole hiring market. Aggregate live job listings across LinkedIn, Glassdoor, and ZipRecruiter with salary normalization and skill extraction — spot hiring trends and comp benchmarks by role or company. Pay per result.

Pricing

from $50.00 / 1,000 job postings

Rating

0.0

(0)

Developer

Creator Fusion

Creator Fusion

Maintained by Community

Actor stats

0

Bookmarked

98

Total users

21

Monthly active users

5 days ago

Last modified

Categories

Share

Job Board Aggregator — Indeed, LinkedIn, Glassdoor & ZipRecruiter API for Recruiting & Lead-Gen Agents

Job-market data API that scrapes Indeed, Glassdoor, ZipRecruiter, and LinkedIn in one call and returns typed JSON rows — schema below. Every listing is normalized to a single 35-field contract (salary range + annualized estimate, skills, experience level, education requirement, benefits) and deduplicated across boards. Built for recruiting, lead-gen, and market-research agents that need multi-board coverage without stitching four scrapers together.

Why agents use this actor

  • Deterministic typed output — every row follows the dataset schema below; stable id hash per posting, consistent field names regardless of source board.
  • Multi-board aggregation in one call — one input, four job boards, cross-board deduplication. No fan-out orchestration on your side.
  • Parsed intelligence, not raw HTML — salary min/max/period/currency plus estimatedAnnualSalary, technicalSkills/softSkills extraction, experienceLevel, yearsExperienceRequired, educationRequired.
  • Clear error semantics — invalid input or zero results exits non-zero with a status message; per-site failures are reported in the SUMMARY key-value record, never as billable dataset rows.
  • No auth or cookies needed — public listings only; per-site resilience built in (scrapeIndividually isolates board failures).

Input schema

FieldTypeRequiredDefaultDescription
searchTermstringyes— (prefill: software engineer)Job title or keywords to search for
locationstringyes— (prefill: Austin, TX)City, state, or country for job search
sitesarrayno["glassdoor", "zip_recruiter", "linkedin"]Boards to scrape. Allowed: indeed, glassdoor, zip_recruiter, linkedin
resultsPerSiteintegerno25Max results per site (1–100)
jobTypestringno""Filter: fulltime, parttime, contract, internship, or "" for all
isRemotebooleannofalseOnly return remote positions
distanceMilesintegerno50Search radius from location (5–100)
hoursOldintegerno168Only jobs posted within this many hours (1–720)
countryIndeedstringnousaIndeed country: usa, uk, canada, germany, france, india, australia
scrapeIndividuallybooleannotrueScrape each board separately so one board's failure doesn't kill the run
enableDeduplicationbooleannotrueRemove duplicate listings across boards (title+company+location hash)
extractSkillsbooleannotrueExtract technical and soft skills from descriptions
proxyConfigurationobjectnoApify residential proxyProxy settings. Residential is the default because Glassdoor and LinkedIn block datacenter IPs; residential traffic is billed to your Apify account

Output schema

Single mode: every dataset row is one normalized job listing with all fields below. Fields the source board doesn't provide are null (or empty string / empty array), never omitted.

FieldTypeNullableDescription
idstringnoStable 16-char hash of title+company+location
sourcestringnoindeed, glassdoor, zip_recruiter, or linkedin
titlestringnoJob title
companystringnoCompany name
locationstringnoLocation as posted
jobTypestringyesfulltime, parttime, contract, internship, or null
isRemotebooleannoBoard-flagged remote position
experienceLevelstringyesentry, mid, senior, executive, or null (inferred)
yearsExperienceRequiredintegeryesMin years parsed from description
educationRequiredstringyesHighSchool, Associates, Bachelors, Masters, PhD, or null
salaryMinnumberyesPosted salary lower bound
salaryMaxnumberyesPosted salary upper bound
salaryPeriodstringyeshourly, weekly, monthly, yearly, or null
salaryCurrencystringnoCurrency code (default USD)
estimatedAnnualSalarynumberyesRange midpoint annualized from salaryPeriod
technicalSkillsarray<string>noDetected tech skills (empty if extractSkills false)
softSkillsarray<string>noDetected soft skills
allSkillsarray<string>notechnicalSkills + softSkills
skillsCountintegernoTotal skills detected
descriptionstringnoDescription text, truncated to 5000 chars (often empty on LinkedIn)
descriptionLengthintegernoFull description length in chars
requirementsarray<string>noParsed requirement phrases (max 10)
benefitsarray<string>noDetected benefit keywords (401k, pto, equity, ...)
companyLogostringyesLogo URL
companyRatingnumberyesCompany rating (mainly Glassdoor)
companyIndustrystringyesIndustry
companySizestringyesHeadcount range
companyUrlstringyesCompany profile URL on the source board
postedDatestringnoYYYY-MM-DD (empty string when unknown)
scrapedAtstringnoISO 8601 UTC scrape timestamp
applyUrlstringnoDirect URL to the posting
sourceUrlstringnoSame as applyUrl
hasRemoteOptionbooleannoDescription mentions remote / work from home
hasVisaSponsorshipbooleannoDescription mentions visa / sponsorship
isEasyApplybooleannoBoard-flagged one-click apply

Real example row (from a live run):

{
"id": "98ffae5ccb0ec729",
"source": "linkedin",
"title": "Software Engineer, Realtime",
"company": "Cloudflare",
"location": "Austin, TX",
"jobType": null,
"isRemote": false,
"experienceLevel": null,
"yearsExperienceRequired": null,
"educationRequired": null,
"salaryMin": null,
"salaryMax": null,
"salaryPeriod": null,
"salaryCurrency": "USD",
"estimatedAnnualSalary": null,
"technicalSkills": [],
"softSkills": [],
"allSkills": [],
"skillsCount": 0,
"description": "",
"descriptionLength": 0,
"requirements": [],
"benefits": [],
"companyLogo": null,
"companyRating": null,
"companyIndustry": null,
"companySize": null,
"companyUrl": "https://www.linkedin.com/company/cloudflare",
"postedDate": "2026-08-13",
"scrapedAt": "2026-08-15T01:34:22.114155",
"applyUrl": "https://www.linkedin.com/jobs/view/4441070330",
"sourceUrl": "https://www.linkedin.com/jobs/view/4441070330",
"hasRemoteOption": false,
"hasVisaSponsorship": false,
"isEasyApply": false
}

Error semantics

  • Invalid input (empty searchTerm/location, empty sites): the run exits with code 1 and a status message explaining what's missing. No dataset items.
  • Zero results (all boards blocked, errored, or no matching jobs): the run exits with code 1 and a per-site status message — never a silent SUCCEEDED with 0 items. Retry with different boards, a broader search, or proxies.
  • Partial failure (some boards succeed, some fail): the run SUCCEEDS with the rows it got. Per-site outcomes (success / empty / error + error text) are in the SUMMARY key-value record (also stored as summary for backward compatibility) under siteStatus. Failed sites never produce dataset rows, so you're never billed for errors.
  • Retry guidance for agents: check SUMMARY.siteStatus; a 403 on Glassdoor means enable residential proxy; LinkedIn empty/401 is rate limiting — retry later or lower resultsPerSite.

Use from AI agents (MCP)

{
"mcpServers": {
"apify": {
"url": "https://mcp.apify.com/?tools=apricot_blackberry/job-board-aggregator",
"headers": { "Authorization": "Bearer <YOUR_APIFY_TOKEN>" }
}
}
}

Works in Claude, Cursor, ChatGPT deep research connectors, and any MCP client; the input schema above is the tool's parameter schema.

Use from code

curl:

curl -X POST "https://api.apify.com/v2/acts/apricot_blackberry~job-board-aggregator/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
-H "Content-Type: application/json" \
-d '{"searchTerm": "software engineer", "location": "Austin, TX", "sites": ["glassdoor", "zip_recruiter", "linkedin"], "resultsPerSite": 10}'

JavaScript (apify-client):

import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('apricot_blackberry/job-board-aggregator').call({
searchTerm: 'software engineer',
location: 'Austin, TX',
sites: ['glassdoor', 'zip_recruiter', 'linkedin'],
resultsPerSite: 10,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();

Python (apify_client):

from apify_client import ApifyClient
client = ApifyClient(os.environ["APIFY_TOKEN"])
run = client.actor("apricot_blackberry/job-board-aggregator").call(run_input={
"searchTerm": "software engineer",
"location": "Austin, TX",
"sites": ["glassdoor", "zip_recruiter", "linkedin"],
"resultsPerSite": 10,
})
items = client.dataset(run["defaultDatasetId"]).list_items().items

Use from automation platforms

  • n8n / Make / Zapier: use the native Apify integration and choose job-board-aggregator by name.
  • LangChain / LlamaIndex: use the Apify tool wrappers (ApifyWrapper / ApifyActor) with the same input JSON.
  • Webhooks: configure an Apify webhook to fire on run completion (ACTOR.RUN.SUCCEEDED/FAILED) to push new listings into your ATS or CRM pipeline without polling.

Supported job sites

  • Indeed — largest job board, best coverage, most reliable (7+ countries via countryIndeed)
  • Glassdoor — includes company ratings; aggressive anti-bot, needs residential proxy
  • ZipRecruiter — good for SMB jobs
  • LinkedIn — professional network jobs; strictest rate limits (~10 pages per IP), list endpoint returns no descriptions

Rate limits & best practices

  • LinkedIn has the strictest rate limits; Indeed is the most reliable with minimal rate limiting.
  • Recommended: 25–50 results per site per run; use scheduled runs for continuous monitoring.
  • Keep scrapeIndividually on so one blocked board doesn't sink the run.

Pricing

$0.10 start + $0.05 per job listing delivered (PAY_PER_EVENT). A default run (3 sites × 25 results/site, deduplicated) typically lands around $2–4; residential proxy usage (the default) is billed to your Apify account on top of this.

FAQ

Why are some fields null/empty? Boards report different data. LinkedIn's list endpoint returns no descriptions, so description-derived fields (skills, requirements, education) are empty for LinkedIn rows.

How does deduplication work? MD5 hash of lowercased title+company+location(first 20 chars); first occurrence wins. Duplicate count is in the SUMMARY record.

Do I need an account on the job boards? No — public listings only, no login or cookies.

Changelog

v2.1 (2026-08)

  • Output dataset schema published (.actor/dataset_schema.json) — typed, agent-readable contract for all 35 fields.
  • Agent integration docs: MCP server config, curl/JS/Python snippets.
  • indeed added to the sites input enum (was documented but not selectable).
  • Fail-loud semantics: invalid input or zero total results now exits with code 1 and a descriptive status message instead of silently succeeding with 0 items.
  • Run summary now also stored under the standard SUMMARY key-value record (existing summary record kept for backward compatibility).

v2.0

  • Per-site individual scraping (scrapeIndividually) with per-site success/failure status.
  • Proxy rotation support; skill/requirement/benefit extraction; cross-board deduplication.