AI & ML Engineer Jobs Scraper — 8 Boards in One avatar

AI & ML Engineer Jobs Scraper — 8 Boards in One

Pricing

from $1.50 / 1,000 job results

Go to Apify Store
AI & ML Engineer Jobs Scraper — 8 Boards in One

AI & ML Engineer Jobs Scraper — 8 Boards in One

AI & ML jobs aggregator: one run merges 8 boards (aijobs.net, LinkedIn, Hacker News Who-is-Hiring, Y Combinator, Built In, RemoteOK/Remotive/WeWorkRemotely, WTTJ, JustJoin.it) into a URL-deduped dataset with structured salary, remote flag and seniority. Delta mode for daily alerts.

Pricing

from $1.50 / 1,000 job results

Rating

0.0

(0)

Developer

Nomad Dev

Nomad Dev

Maintained by Community

Actor stats

0

Bookmarked

10

Total users

1

Monthly active users

2 days ago

Last modified

Categories

Share

Claude / Codex skill to describe and setup this actor: SKILL.md

One call, eight sources, one bill. This AI/ML jobs aggregator runs 8 job-board scrapers tuned for AI/ML roles inside a single Actor, merges and dedupes into one dataset, and normalizes structured salary, remote flag and seniority uniformly across every board.

What machine learning jobs data does this scraper extract?

Published postings follow nomad-agent-job-row-v1. The shared fields carry source identity, title and company, parsed locations, dates, the complete plain-text body when available, source markup when usable, and work type. Unavailable scalar facts are null; no parsed location is []. Source-only facts appear in versioned custom.data only when mapped there. See the Output example section for the exact dataset fields and diagnostic rows.

Structured salary and the remote/seniority/type fields are populated from each source's own structured data where available: ai_jobs_net, builtin, remote_boards, wttj and justjoinit expose salaryMin/Max/Currency/Period directly; for ycombinator_was and hackernews the bundle parses the numbers out of the free-text pay line when the poster includes one. linkedin doesn't quote pay on its listings, so its salary fields stay empty/null; a hackernews post that names no figure is left null too. Any field a source doesn't provide is "" (strings) or null (numbers/booleans) rather than fabricated.

How the bundle works

This is a bundle Actor: one endpoint that runs every job-board scraper listed below in-process — their code ships inside this Actor, so no child actor runs are launched and no per-source fees stack on top. You pay this bundle's pay-per-event pricing only. Sources run concurrently, every record is mapped onto one flat schema with salary/remote/seniority normalized uniformly, and results are deduped by URL across boards. You can restrict the run to a subset with the sources input. Each source fails open independently: if one board errors or times out, the others still return.

How to scrape machine learning jobs with this Actor

  1. Click Try for free / Run — no login to the target site, no cookies, no proxies to configure.
  2. Adjust the input (keyword, filters, maxItems) or keep the defaults.
  3. Run it and export the dataset as JSON, CSV or Excel, or read it over the API.

Run it from your own code:

from apify_client import ApifyClient
client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("nomad-agent/ml-ai-dev-bundle").call(run_input={"maxItems": 50})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
print(item["title"], "—", item["company"], item["url"])

Or a single HTTP call that runs the Actor and returns items in one response:

curl -X POST \
"https://api.apify.com/v2/acts/nomad-agent~ml-ai-dev-bundle/run-sync-get-dataset-items?token=<YOUR_APIFY_TOKEN>" \
-H "Content-Type: application/json" \
-d '{"maxItems": 50}'

Input

FieldTypeDefaultNotes
sourcesarray["linkedin", "ai_jobs_net", "hackernews", "ycombinator_was", "builtin", "remote_boards", "wttj", "justjoinit"]Which boards to include. Leave empty to run the full default set. All sources run in-process — no per-source fees.
keywordstring""Optional free-text filter forwarded to sources that support it (others ignore it).
maxItemsPerSourceinteger36Cap on items fetched from EACH board before merge.
maxItemsinteger288Hard cap on the merged, deduped output. Default is sources × maxItemsPerSource (the zero-config ceiling). Set 0 for no cap.
cacheTtlSecondsinteger1800How long to reuse results already fetched from a source instead of re-fetching. 0 = always fetch fresh.
concurrencyinteger6How many boards to fetch in parallel. (Advanced)
runTimeoutSecsinteger240How long to give each source before returning what it has collected so far. Sources run in parallel, so this is a per-source ceiling, not a budget shared across them. (Advanced)
apifyTokenstring (secret)""Leave empty — injected automatically on the Apify platform. Only set for local runs outside the platform. (Advanced)

Output example

Every row follows nomad-agent-job-row-v1, the one shape shared by all of this fleet's job Actors. A row carries every field; null means the source did not publish that fact, and locations: [] means no usable location was parsed from the posting. Nothing is guessed.

{
"schemaVersion": "nomad-agent-job-row-v1",
"recordType": "posting",
"source": "linkedin",
"id": "a1b2c3",
"url": "https://example.com/linkedin/jobs/a1b2c3",
"title": "Senior Backend Engineer",
"company": "Example Company",
"locations": [
"Bilbao",
"Spain"
],
"postedAt": "2026-09-02T00:00:00Z",
"deadline": null,
"description": "The complete posting body as plain text, exactly as the source published it — never truncated.",
"descriptionHtml": null,
"workType": "remote",
"custom": {
"schemaId": "nomad-agent-job-custom-ml-ai-dev-bundle-v1",
"data": {
"hiringContactName": "…",
"hiringContactTitle": "…",
"hiringContactUrl": "…"
}
},
"employmentType": null,
"hiringContactName": null,
"hiringContactTitle": null,
"hiringContactUrl": null,
"isRemote": null,
"location": null,
"remoteType": null,
"salary": null,
"salaryCurrency": null,
"salaryMax": null,
"salaryMin": null,
"salaryPeriod": null,
"seniority": null,
"snippet": null
}
FieldMeaning
schemaVersionAlways "nomad-agent-job-row-v1".
recordType"posting" for a job, "diagnostic" for a row reporting something about the run itself.
sourceWhich job source the posting came from, from the collector registry's vocabulary — not the Actor name.
idStable identifier for the posting within source.
urlCanonical public URL of the posting on the source site.
titleJob title exactly as the source publishes it, untruncated.
companyEmployer name as published.
locationsPlaces the role is based, most specific first — e.g. ["Bilbao", "Spain"].
postedAtWhen the source published the posting, ISO-8601 UTC (YYYY-MM-DDTHH:MM:SSZ).
deadlineClosing date for applications as an ISO-8601 calendar date (YYYY-MM-DD).
descriptionThe complete posting body as plain text — never truncated, never summarised.
descriptionHtmlThe posting body as the source's own markup, preserving lists, headings and links.
workTypeWorking arrangement: "remote", "hybrid" or "onsite".
customFacts only this source publishes, as {"schemaId", "data"}.

Deprecated fields

This Actor is on a paid integration contract, so the fields it published before this shape are still emitted alongside the ones above during a transition window, unchanged:

employmentType hiringContactName hiringContactTitle hiringContactUrl isRemote location remoteType salary salaryCurrency salaryMax salaryMin salaryPeriod seniority snippet

They will be removed once the integrations reading them have moved. Nothing above changes when they go.

A run also emits diagnostic rows — recordType: "diagnostic" with a warnings array — when it has something to report, such as a source returning nothing. They are never billed and are easy to filter out on recordType.

Pricing

Pay per event: $0.01 per Actor start and $0.003 per job returned ($3 per 1,000 jobs). That is the whole bill — every source runs inside this Actor, so there are no child-actor fees on top.

Zero-config run estimate (defaults, all 8 sources): up to ~288 merged items for roughly $0.88 all-in ($0.01 start + 288 × $0.003). Real runs usually cost less — not every board returns the full cap, and cross-board duplicates are billed once.

Use cases

  • AI-specialist job boards
  • ML-engineer alert bots
  • AI-talent market research
  • Recruiting pipelines for data/ML teams

FAQ

Is it legal to scrape machine learning jobs? This Actor reads only publicly available job postings — data any visitor can see without logging in. No personal data behind authentication is touched. Review the target site's terms and your local regulations for your specific use case.

Do I need an account on the target site? No. Postings are fetched from public pages/APIs — no login, cookies or session tokens.

How fresh is the data? Every run fetches live listings. Results are cached for cacheTtlSeconds (default 30 min, set 0 to always hit the source live).

How many jobs can I get? maxItems caps the run (set 0 for no cap). Most sources paginate from newest to oldest.

Something broken or missing? Open an issue on the Actor's Issues tab — it is monitored and reliability fixes ship fast.

Integrations

Export the dataset as JSON, CSV or Excel, or read it straight from the Apify API. Works out of the box with Make, Zapier and n8n via their Apify integrations, can be called synchronously with run-sync-get-dataset-items from any backend, and is usable by AI agents through the Apify MCP server.

Is this Actor useful to you? A quick ⭐ review on the Actor's Reviews tab helps other AI/ML and developer job seekers find it — and tells us what to build next.


From the maker of Oink — an open-source, AI-powered job-search bot for Telegram that runs on these Actors. Try the free bot, get a managed instance at oinkjobsearch.com, or browse the full catalog of 50+ Actors.