Multi Job Board Scraper — Career Pages, Listings Aggregator
Pricing
from $1.80 / 1,000 job listings
Multi Job Board Scraper — Career Pages, Listings Aggregator
Job board jobs: one keyword across Indeed and the company boards you name, merged and de-duplicated into one feed — the same role on two boards is one row and one charge. One price for the whole feed, no add-on events, from $1.80 per 1,000 job listings; a scheduled run pays only for new jobs.
Pricing
from $1.80 / 1,000 job listings
Rating
0.0
(0)
Developer
Steadyfetch Team
Maintained by CommunityActor stats
0
Bookmarked
1
Total users
1
Monthly active users
2 hours ago
Last modified
Categories
Share
Never pay for a job we didn't deliver. One keyword across Indeed and the company boards you name, merged and de-duplicated into one feed. The same role found on two boards is one row — and one charge.
Using an AI agent? Pin this actor in Apify's MCP server with one link: https://mcp.apify.com?tools=steadyfetch/multi-job-board-scraper
- Actor id:
steadyfetch/multi-job-board-scraper - Input:
{ "query": "backend engineer" }— the one field you have to set. - Cap the bill: set
maxTotalChargeUsdon the run (a run option, not Actor input), e.g.0.50— the run stops when it reaches it.
From $1.80 per 1,000 job listings — all-inclusive pay per event, nothing charged for starting a run, charged only on delivery. One price for the whole feed: no per-source fee, no per-search fee.
Just want to see it work? Run it with no input at all ({} from the API or an AI agent) and
you get a real 5-job sample — "software engineer" on Indeed US plus vercel.com's own board,
merged — charged like any run, at most $0.04 on the Apify free plan and less on paid plans.
Set only the filters — a country, a sort
order, a row cap — and nothing to search for, and that same sample runs under them, charged like
any run; a limit larger than the sample's own is capped at it. In the console, Start with the
example keyword already in the form and you get that search for real, up to your row cap.
Running the same search again? A job already delivered to your account is never charged a second time (a run started with a scoped API token needs key-value store permission for that) — see Never pay for the same job twice.

Unofficial. This actor is not affiliated with, endorsed by, or sponsored by Indeed or any hiring-board provider. "Indeed" is a trademark of Indeed, Inc.; Greenhouse, Lever, Ashby, Workday, Workable, SmartRecruiters, Rippling, Personio and Teamtailor are trademarks of their respective owners. It reads only the public search and hiring pages an ordinary browser sees.
Every row carries charged and missReason, so you can reconcile the invoice from the
dataset itself without opening the console. Only rows with charged: true were billed.
Output — a merged row
Five merged jobs from a real run, in the Apify console's dataset table — every row shows which boards it came from, how many duplicates it absorbed, and the department, type and posted date:

And one merged row in full:
{"title": "Senior Backend Engineer","company": "Acme","location": { "raw": "Berlin, Germany", "city": "Berlin", "region": null, "postcode": null, "country": "DE" },"salary": { "min": 85000, "max": 115000, "currency": "EUR", "period": "year" },"descriptionText": "…full description from the company's own board…","source": "greenhouse","sources": [{ "board": "greenhouse", "jobUrl": "https://job-boards.greenhouse.io/acme/jobs/123", "applyUrl": "…", "postedAt": "2026-08-24T09:00:00.000Z", "salary": null },{ "board": "indeed", "jobUrl": "https://www.indeed.com/viewjob?jk=…", "applyUrl": null, "postedAt": null, "salary": { "min": 85000, "max": 115000, "currency": "EUR", "period": "year" } }],"merged": true,"duplicateCount": 1,"dedupeConfidence": "high","dedupeKey": "mb:…","charged": true,"missReason": null}
Plus one coverage row per source and per company board (source, board, status,
delivered, resolved, missReason) and one summary row with delivered, uniqueFound,
duplicatesMerged, stoppedBy, datasetId (paste it into resumeFromDatasetId to continue this run
later), carriedOver and chargedEvents.
See a real run's full output →
— eight live jobs pulled from four different company boards in one run, plus the coverage row for
each board and the summary. Every charged row is marked charged: true; nothing else is.
Agent / API paste-block
Actor: steadyfetch/multi-job-board-scraperRequired: query (job title/keyword, or a full Indeed search URL)Optional: companies (array of company domains / careers URLs / board URLs / names)sources (array: ["indeed","ats"], default both)countries (array of ISO-2, default ["US"] — each is its own Indeed search)location (string, e.g. "Austin, TX")radiusKm (integer, needs a location — Indeed only; up to 160 km,Indeed's own radius selector stops at 100 miles)dedupe (boolean, default true — merge the same role across boards)postedWithinDays (integer — up to 30, Indeed's own window)sortBy (relevance | date, default relevance — Indeed only; theoption's own name works too, e.g. "Date posted")remoteOnly (boolean, default false — filtered rows are never charged)maxItems (integer, default 100 — hard cap on UNIQUE jobs;up to 10,000 per run)maxItemsPerCompany (integer, default 200 — up to 5,000 per board)maxRunSeconds (integer, default 1200 — clean stop, never a timeout kill;30 to 3,600 seconds)includeSeen (boolean, default false — OFF: a job already delivered to youraccount is skipped, uncharged. ON: it comes back markedrepeat:true with firstSeenRunId, still uncharged)resumeFromDatasetId (string — an earlier run's dataset ID; every job already in itis skipped and never charged again)Charges: job-listing once per UNIQUE job after de-duplicationNote: sending null (or an empty value) for an optional field is the same as leavingit out — the default is used, so templated callers need no special-casing.Every number above falls back to its default if it arrives empty — never toits smallest value.Token: https://console.apify.com/settings/integrations
curl -X POST "https://api.apify.com/v2/acts/steadyfetch~multi-job-board-scraper/run-sync-get-dataset-items?token=$APIFY_TOKEN" \-H 'Content-Type: application/json' \-d '{"query":"backend engineer","companies":["stripe.com","https://jobs.lever.co/leverdemo"],"countries":["US"],"maxItems":50}'
What it does
You give one keyword and, optionally, the companies you care about. The actor:
- searches Indeed for your keyword (across the countries you pick), and
- pulls each company's own hiring board — Greenhouse, Lever, Ashby, Workday, Workable, SmartRecruiters, Rippling, Personio or Teamtailor — filtered to roles that match your keyword, then
- merges the same role found on more than one board into a single row, keeping every source link, and charges you once.
Company boards are how you reach roles that never appear on a job aggregator. About half of companies resolve from a bare domain (mid-market best; large enterprises mostly run their own Workday/Oracle, so paste a board URL for those). You are never charged for a company we cannot resolve.
The de-duplication, in plain terms
A "duplicate" is the same role — same company, same title, in an overlapping location — found
on two different sources. When we find one, you get one row with a sources array listing
every board it was on, duplicateCount, and a dedupeConfidence of high (matched on a
shared city) or medium (matched on a country-wide or remote posting).
We tuned this on a corpus of real jobs to never delete a distinct job. When two roles at
one company genuinely can't be told apart from an aggregator's single row, we keep them
separate — tagged with a shared dedupeGroupId — rather than merge them and silently drop
one. A wrong merge is a job you never see, so we always err on the side of showing you more.
Turn dedupe off and each board's copy is its own row (the identical posting fetched twice is
still never charged twice).
Sources
- Indeed — needs a keyword. United States, United Kingdom and Germany are verified end to end; any listing that comes back outside the country you asked for is reported, never charged.
- Company boards (ATS) — need company domains. Greenhouse, Lever, Ashby, Workday, Workable, SmartRecruiters, Rippling, Personio, Teamtailor — each the company's own documented public board.
Narrow with sources (for example ["ats"] to skip Indeed). Google Jobs is deliberately not a
source — its jobs tab returns only ~10 rows per query with no way to page deeper, so it would
add cost without adding coverage.
What you are never charged for
| situation | run status |
|---|---|
| A board answered with an access check or rate limit | SUCCEEDED — re-runnable, the coverage row says so |
| A company we could not resolve to a board | SUCCEEDED — uncharged, lists what was tried |
| A source ran and matched nothing | SUCCEEDED — a real answer |
| A duplicate merged into another row | SUCCEEDED — charged once, not per board |
| A job your account already has from an earlier run | SUCCEEDED — skipped, never charged twice |
| An input we could not read | SUCCEEDED — the row names the fix |
| The note on a run where you set filters but nothing to search for, so the sample ran under them | SUCCEEDED — the row names which settings were yours |
Your own maxItems / maxRunSeconds / cost cap | SUCCEEDED — summary row names the knob and the datasetId to continue from |
| A limit you set past what this actor can do | SUCCEEDED — the run continues at the limit and one row says what you asked for |
Ask for more than this actor can do and the run still starts. One run delivers up to
10,000 unique jobs, pulls up to 5,000 roles from any one company board, and runs 30 to
3,600 seconds; radiusKm reaches 160 km and postedWithinDays 30 days, both of them
Indeed's own ceilings rather than ours. A larger (or smaller) number is not an error: the
run continues at the nearest limit and writes one extra uncharged row saying what you asked
for and what bound it — you are never left with no run at all.
We never guess. An access check is reported as an access check, not as "no jobs found" — a temporary problem is never dressed up as a permanent verdict, and it is never charged.
This actor may fail when a board changes things. The two you are most likely to see are
a board answering with an access check or rate limit, and a company we could not resolve to
a hiring board. Both ship as a coverage row with charged: false, the run still
SUCCEEDS, and the row says whether re-running helps.
When Indeed turns every connection away and the run still has time, it does not stop at the first refusal: it varies the connection type and device shape, reaches a rendered fallback, and then waits a couple of minutes and tries the whole ladder again — up to three times. How much of the run's remaining time one blocked search may spend waiting follows the size of what you asked for: a third of it while there are still other searches and company boards to fetch, so a wide block leaves them their share, and up to all of it when this search is the only thing left to fetch. Only then does the uncharged coverage row ship, and it says what was tried: how many connections, over how many minutes, whether it waited, and how much time was left. A run with a short time limit makes one attempt and reports at once; a "not found" answer is answered at once and never waited on.
How often this data changes, and what a second run costs
A company board is the employer's own record: a role appears on it the hour it opens and comes off it the hour it closes, so the boards in a run are the freshest listing anyone can read for that company. Indeed is a keyword search rather than a record, and it re-ranks continuously — the same query an hour later returns an overlapping but differently ordered slice.
We have not measured a turnover rate across nine boards and will not invent one. What the shape of the data says is this: a daily run is what catches a role while it is still open on a fast-moving keyword search, and a weekly run is enough for a fixed list of companies you are watching. Either cadence costs you the same money over the same week, because there is no fee for starting a run and the memory below charges you only for roles you did not already have — so a tighter schedule buys you speed, not a bigger bill. The outer bound is that memory's own 90 days: a schedule pointed at the same companies pays for new roles only for three months, and a role still live after that is collected and charged again.
Never pay for the same job twice
Every run remembers the jobs it delivered for your account, in a key-value store called
multi-job-watch-account in your own Apify account — under every identity a job carries, one
per board that saw it. Run the same search again and what you already have is skipped before it
takes a row slot: no row, no charge, and no bite out of your row cap. The store lives in your
own Apify account, so a run started with a scoped API token in restricted-access mode needs
key-value store Read, Write and Create permission (or Actor runs set to Full access) —
without it the run says so on its status line and on an uncharged first row, and it cannot skip
what you already have (Write is what lets the run remember what it delivered; without it every run
pays again). The status line says how many (18 already in your account, skipped, not
charged) and OUTPUT.skippedSeen holds the number.
Storing every identity is what makes it work here: a role you first got from Indeed alone is still recognised next week when a company board sighting merges with it, and the other way round — so a merged row can never read as new and bill you again.
Want one complete dataset per run instead of only what changed? Turn on Include jobs you already
have (includeSeen: true) and those rows come back anyway — marked repeat: true with
firstSeenAt and firstSeenRunId naming the run that first delivered them, charged: false.
- The store lives in your account, one record (
SEEN). Open it in Storage → Key-value stores to see everything the actor has delivered you, or delete the record to start over. This actor runs with limited permissions and touches nothing else in your account. - Entries older than 90 days count as new again, so a job you collected last quarter is collected (and charged) again if it is still live.
- Only delivered rows are remembered. A blocked board, an unresolved company or a source that matched nothing leaves nothing behind, so the next run tries it again.
- If the store cannot be read on some run, the run still runs: it delivers and charges as usual and the status line says the repeat check was unavailable, so you know a repeat may have been charged that once. If the reason is the token's own permissions, the status line says that instead and names the permission to grant — set it under Settings → API & Integrations on the token, or start the run from the console.
- The no-input sample is outside all of this. Pressing Start with nothing filled in reads both boards live every time and neither checks nor updates this list, so you always see a real result rather than a hand-back of the last one — and those few sample rows are charged like any run's, each time you press it.
FAQ
I hit my cap — how do I get the rest without paying twice? Re-run the same input with a higher maxItems and resumeFromDatasetId set to that run's
dataset ID (it is on the summary row as datasetId): every job already in that dataset is
skipped — never fetched, never charged — and the new summary's carriedOver says how many.
A dataset ID that cannot be read stops the run before it fetches anything, uncharged.
Under a small cap, delivery rotates across boards, so Indeed plus two company boards returns rows
from all three rather than the first few Indeed cards. A time limit ends the collecting, never
the delivering: every job already collected when the clock runs out is still delivered, so a run
never pays for jobs it does not hand you.
Does {} charge anything? Yes — like any run, and at most $0.04 on the Apify free plan (less on paid plans). With no input at all
the run is a real 5-job sample — "software engineer" on Indeed US plus vercel.com's own board,
merged — and you pay for the jobs it delivers. Set only filters and nothing to search for and the
same sample runs under them, charged the same way, with one extra uncharged row naming which
settings were yours. A source that does not answer gets an uncharged
coverage row saying so, never an empty result.
Can I just scrape a company's whole board? Yes — pass the companies with sources: ["ats"].
For deep, unfiltered board pulls by domain, the sibling Career Page Scraper — Company Jobs by Domain, 9 ATS Boards
is purpose-built for exactly that.
Why did a company return nothing? Either it has no live openings matching your keyword, or its board could not be positively identified from the domain — the coverage row tells you which, and neither is charged. Paste the board URL directly if you know it.
Run it from an AI agent (MCP)
Works out of the box from any MCP client (Claude, Cursor, …) via Apify's MCP server — ask for "remote data engineer jobs across Indeed and company career pages, de-duplicated" and point it at this actor.
The one-link pin is at the top of this page.
Steadyfetch jobs suite
One actor per source, one job each, the same row shape — so a pipeline built on one source takes the next without a rewrite. All-inclusive pay per event, nothing charged for starting a run, charged only on delivery. Google Jobs adds one search fee of $0.004 per search that returns listings; a search that returns nothing pays nothing.
| Source | Actor |
|---|---|
| Indeed | Indeed Jobs Scraper — Indeed API Alternative, Salary, Vacancies |
| A company's own career site (paste a domain) | Career Page Scraper — Company Jobs by Domain, 9 ATS Boards |
| Glassdoor, with the employer rating | Glassdoor Jobs Scraper — Glassdoor API Alternative + Ratings |
| Several boards at once, de-duplicated | this actor |
| Google Jobs, with the full panel and apply links | Google Jobs Scraper — Google for Jobs Listings, Apply Links |
The rest of the steadyfetch shelf — same contract everywhere: all-inclusive pay per event, nothing charged for starting a run, charged only on delivery (three actors add a small delivery-conditional fee: keyword volume's fresh lookup, profile posts' profile lookup, Google Jobs' search fee — each README states it).
| Family | Actors |
|---|---|
| Ad creative intelligence | Facebook · Google Ads video · TikTok · LinkedIn · Google Ads text & OCR |
| Trends & keywords | Google Trends · Trends Now · Breakout keywords · Autocomplete keywords · Keyword volume & CPC · Social trends |
| YouTube transcripts | YouTube videos · YouTube channels |
| Reel transcripts · Profile posts | |
| Jobs | Indeed · Career sites by domain · Glassdoor · Multi-board · Google Jobs |
| Amazon | Products · Search · Bestsellers · Sellers |
| Any media file | Speech to Text · any link or file |
Feedback & support
Found an issue? Open it on the Issues tab — we usually reply within a couple of hours, always within a day. Feature requests welcome.
If it earned its keep, a rating helps other buyers find it, and saving the actor keeps it one click away.