Google Jobs Scraper: Salary, Apply Links, ATS Detection
Pricing
from $3.15 / 1,000 jobs
Google Jobs Scraper: Salary, Apply Links, ATS Detection
Google Jobs listings (title, company, salary, job type, description, every apply link) by keyword and location; $8.10 per 1,000 jobs. $3.15 on Business plans, 60-150 jobs per search, the company's ATS board detected. SerpApi input names accepted, free error rows.
Pricing
from $3.15 / 1,000 jobs
Rating
0.0
(0)
Developer
Arcadium Data
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
3 hours ago
Last modified
Share
Google Jobs Scraper: Salary, Apply Links, ATS Detection
Give it job searches ("software engineer", "registered nurse") and places ("Seattle, WA", "Berlin"), and it returns one JSON row per Google Jobs result: title, company, location, posted date, salary (as shown and parsed), job type, full description and every apply link Google lists. $8.10 per 1,000 jobs on the Free plan, $3.15 on Business plans and above; you pay only for rows you get.
Why this actor
- The company's own ATS posting is picked as the best apply link, with the ATS and board detected.
- Goes past Google's 10 jobs per page to 60-150 jobs per search, with Google's own filters.
- SerpApi parameter names (
q,date_posted,employment_type,gl,hl,num_pages) and these field names are also accepted:query,location,jobType,maxResults,maxPagesPerQuery,remote. - No login; recruiter names, emails and phones are never output. Empty searches are free rows.
Built by Arcadium Data: public data, dated and agent-ready, never personal data.
Use with AI agents
The actor takes plain JSON and returns one flat row per job, so an AI agent or MCP client (for example through mcp.apify.com) can call arcadium/google-jobs directly. Input example for "Data analyst jobs in Austin posted this week":
{ "queries": ["data analyst"], "locations": ["Austin, TX"], "datePosted": "week" }
"Remote part-time customer support jobs, up to 50, short descriptions for an LLM":
{ "queries": ["customer support"], "remoteOnly": true, "employmentTypes": ["PARTTIME"], "maxResultsPerQuery": 50, "maxDescriptionChars": 600 }
"Nursing jobs in three German cities, in German, with the company's applicant tracking system detected":
{ "queries": ["Pflegefachkraft"], "locations": ["Berlin", "Hamburg", "München"], "country": "DE", "language": "de", "jobsDbExport": true }
Read title, companyName, location, postedAt, salary, employmentTypes and applyUrl; description holds the job text. The same posting found by two searches comes once (jobId). 100 jobs cost $0.27. A search that finds nothing or cannot be read comes back as a free row with rowType: "error", a code (no_match, blocked, unavailable, invalid_input) and a message, never as a failed run, so an agent can tell "no jobs" from "try again later".
What you get
| Field | What it holds |
|---|---|
jobId | Google's id of the posting, stable across searches and runs (use it to dedupe). |
title, companyName, location | Job title, hiring organisation and place as Google shows them. |
via | Site Google read the posting from (LinkedIn, Indeed, the company's career site, ...). |
postedAgo, postedAt | Age as shown ("3 days ago") and the approximate date it gives (2026-10-19). |
salaryText, salary | Salary as shown and parsed: min, max, currency, period (hour, year, ...). |
employmentType, employmentTypes | Job type as shown and normalised (FULLTIME, PARTTIME, CONTRACTOR, INTERN, TEMPORARY). |
qualification, workFromHome | Google's qualification chip ("No degree mentioned") and its work-from-home flag. |
description, descriptionComplete | The job description, and whether it is the full text or Google's preview. |
applyUrl, applyOptions | Best apply link (the company's own ATS posting when listed) and every apply option with site, URL and detected ATS. |
googleJobsUrl | Link that opens the posting in Google Jobs. |
searchText, query, searchLocation, position | The exact text sent to Google, your query and location, and the rank. |
jobsDb | With jobsDbExport: ATS, board, ATS posting id, clean apply URLs, fingerprint (see below). |
Output example:
{"rowType": "job","platform": "google-jobs","jobId": "d6Zuw_TVelot7iRuAAAAAA==","title": "Software Development Engineer, Network Monitoring and Remediation (NMR)","companyName": "Amazon Web Services, Inc.","location": "Seattle, WA","via": "Indeed","postedAgo": "5 days ago","postedAt": "2026-10-17","salaryText": "$144K–$194K a year","salary": { "min": 144000, "max": 194000, "currency": "USD", "period": "year", "source": "structured" },"employmentType": "Full-time","employmentTypes": ["FULLTIME"],"qualification": null,"workFromHome": false,"description": "DESCRIPTION\n\nAmazon's network carries traffic for hundreds of millions of customers ...","descriptionComplete": true,"applyUrl": "https://www.indeed.com/viewjob?jk=917ad7a1f42a4176&utm_campaign=google_jobs_apply&utm_source=google_jobs_apply&utm_medium=organic","applyOptions": [{ "source": "Indeed", "label": "Apply on Indeed", "url": "https://www.indeed.com/viewjob?jk=917ad7a1f42a4176&utm_campaign=google_jobs_apply&utm_source=google_jobs_apply&utm_medium=organic", "domain": "indeed.com", "ats": null },{ "source": "LinkedIn", "label": "Apply on LinkedIn", "url": "https://www.linkedin.com/jobs/view/software-development-engineer-network-monitoring-and-remediation-nmr-at-amazon-web-services-aws-4480587462?utm_campaign=google_jobs_apply&utm_source=google_jobs_apply&utm_medium=organic", "domain": "linkedin.com", "ats": null }],"googleJobsUrl": "https://www.google.com/search?ibp=htl;jobs&q=software+engineer+jobs+in+seattle&htidocid=d6Zuw_TVelot7iRuAAAAAA%3D%3D","searchText": "software engineer in Seattle, WA","query": "software engineer","searchLocation": "Seattle, WA","position": 2,"language": "en","country": "US","scrapedAt": "2026-10-22T08:00:00.000Z"}
Input
{"queries": ["software engineer", "product manager"],"locations": ["Seattle, WA", "New York, NY"],"datePosted": "week","employmentTypes": ["FULLTIME"],"maxResultsPerQuery": 30}
| Field | What it does |
|---|---|
queries | What to search, as typed into Google ("software engineer", "barista part time"). |
locations | Places; each query runs once per location ("software engineer in Seattle, WA"). Empty = the whole market. |
datePosted | any, today (since yesterday), 3days, week, month. |
employmentTypes | FULLTIME, PARTTIME, CONTRACTOR, INTERN; several = one search each, merged. |
remoteOnly | Google's Remote filter. |
maxResultsPerQuery | Jobs per query and location (default 10 = one Google page, up to 200). |
maxItems | Stop after this many jobs in total. A cost cap. |
country, language | Google market and interface language, e.g. DE + de, IN + en, BR + pt-BR. |
includeDescription, maxDescriptionChars | Job text on or off, and an optional length cap. |
jobsDbExport | Adds the jobsDb merge fields. |
includeErrorRows | A free row for every search without jobs (on by default). |
SerpApi parameter names and these input field names are also accepted as they are (query, q, location, date_posted, employment_type, jobType, gl, hl, maxResults, maxPagesPerQuery, num_pages, remote).
More than 10 jobs per search
Google shows 10 jobs per search and loads the rest only inside its own page script. With maxResultsPerQuery above 10 the actor also runs the filter variants Google itself offers on the page (each job type, then narrower date windows, then both) and merges them without duplicates. A broad search ("software engineer in Seattle") reaches 60-150 jobs this way; a narrow one stops as soon as the variants stop adding new jobs. searchText on each row says which variant found it. You pay per job delivered, never per page.
Job-board merge fields (jobsDbExport)
Many Google Jobs postings link to the company's own applicant tracking system. With jobsDbExport: true each row gets a jobsDb object:
| Field | What it holds |
|---|---|
ats, boardToken, boardKey | ATS (greenhouse, lever, ashby, workable, smartrecruiters, recruitee, personio, teamtailor, workday) and the company's board on it. |
atsJobId | <ats>___<posting id>, the same id our Career Site Jobs scraper gives that posting, for joins. |
atsUrl, applyUrls | The ATS posting URL and every apply URL, without tracking parameters. |
fingerprint | sha1 of company, title and location (lower case), to match the same job across aggregators. |
Use it from code
Python:
from apify_client import ApifyClientclient = ApifyClient("<YOUR_APIFY_TOKEN>")run = client.actor("arcadium/google-jobs").call(run_input={"queries": ["machine learning engineer"],"locations": ["London"],"country": "GB","datePosted": "3days",})for item in client.dataset(run["defaultDatasetId"]).iterate_items():if item["rowType"] == "job":print(item["companyName"], "|", item["title"], "|", item["salaryText"], "|", item["applyUrl"])
JavaScript:
import { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });const run = await client.actor('arcadium/google-jobs').call({queries: ['electrician'],locations: ['Phoenix, AZ', 'Dallas, TX'],maxResultsPerQuery: 40,jobsDbExport: true,});const { items } = await client.dataset(run.defaultDatasetId).listItems();console.log(items.filter((i) => i.jobsDb?.ats).length, 'jobs link to a company ATS');
Pricing
You pay per job delivered, at the price of your Apify plan. Error rows and repeats (the same job found by another search) are free.
| Apify plan | Per 1,000 jobs |
|---|---|
| Free | $8.10 |
| Starter (Bronze) | $4.05 |
| Scale (Silver) | $3.60 |
| Business (Gold) | $3.15 |
| Platinum | $3.15 |
| Diamond | $3.15 |
Plus a one-off $0.001 per GB of memory when a run starts. Descriptions, apply links, salary parsing and the job-board fields are included.
Set a maximum cost per run in the Apify run options and the actor stops before it goes over.
Related actors
Use cases
- Job market and salary research: who is hiring for a role, where, at what pay, by job type.
- Recruiting, sourcing and sales: companies hiring for X (a buying signal), with links to their career sites.
- Job boards and aggregators: fresh postings with full text and every apply link, deduplicated by
jobId. - Joining with ATS data:
jobsDbExportgives the company's Greenhouse, Lever, Workday ... board and posting id. - AI agents: "open jobs for X in Y this week" as clean rows.
Limitations
- Google shows 10 jobs per search. Past that, coverage comes from Google's own filter variants, so the total per search is what those variants list (typically 60-150 for a broad search), not every job Google has.
- Results, ranking and the location area are Google's. A location is sent the way you would type it ("in Seattle, WA"); a very small place may return jobs from the surrounding area.
- Some markets show only a preview of the description (about 400 characters,
descriptionComplete: false); the full text is on the apply links. - Salaries are what Google shows; many postings have none.
- Labels such as "3 days ago" follow
language;postedAt,employmentTypesandsalary.periodare normalised for en, de, fr, es, it, pt and nl.
Privacy
Public data only: Google Jobs results as an anonymous visitor sees them. No login, no account, no cookies. Company names are organisations; recruiter names, emails and phone numbers are never output: emails and phone numbers in descriptions are replaced with [email] and [phone], and greetings that name a person are removed.
FAQ
Why do I get fewer jobs than maxResultsPerQuery? Google had no more for that search: its page and filter variants stopped adding new jobs. Broaden the query or the location, or add more locations.
Why is a search an error row with code no_match? Google Jobs listed nothing for it ("It looks like there aren't any Jobs matches"). Check the spelling or broaden it. It is free.
Can I search any country?
Yes: set country (and language for local labels), e.g. "country": "IN" or "country": "DE", "language": "de".
Something looks wrong in the output. Who do I tell? Open an issue on the actor page with the run link. We read them all.