Google Jobs Scraper
Scrape Google Jobs listings. Get employer, location, posting date and salary where shown. Every apply link Google gives comes too.
How it works
- 1Open it on Apify
Hit Run on Apify — it opens the tool in the cloud, no install.
- 2Set the inputs
Adjust
query,location,country(sensible defaults are pre-filled). - 3Click Run
The tool runs on Apify’s cloud and collects the data for you.
- 4Export the results
Download as JSON, CSV or Excel, or pipe straight into your app, Google Sheets, or an AI agent.
Pricing
$0.0015 per job listing = $1.5 per 1,000
| You are charged for | When | Price |
|---|---|---|
| Job listing | Charged once per job listing returned. Sample rows and failure diagnostics are never charged. | $0.0015 |
Pay-per-event pricing: you are billed per result, not per subscription. Billing is handled by Apify on your own account. These are the live Apify store prices, in effect since 2026-08-22, and they are what you are actually charged.
Inputs
| Field | What it does | Type |
|---|---|---|
query | Job title, skills, or keywords to search for, for example software engineer or registered nurse. | string |
location | Optional city, region, country, or remote location to bias the Google Jobs search. | string |
country | Two-letter Google country code used for regional results, such as US, CA, or GB. | string |
language | Google interface language code, such as en, fr, or de. | string |
maxItems | Maximum number of unique job rows to return. Requests are bounded to 100 rows and 8 scroll rounds. | integer |
maxScrolls | Maximum browser scroll rounds used to load more jobs after the first page, from 0 to 8. | integer |
deadlineSeconds | Maximum wall-clock seconds for the complete scrape, from 20 to 180 seconds. | integer |
enableProxyFallback | Retry through the proxy lanes when the direct request cannot be read. Google now serves its "Enable JavaScript to use search" gate to every plain-HTTP client on every address, so the direct lane alone returns no job cards and this must stay on for the Actor to produce rows. | boolean |
What you get
A structured dataset — each result includes fields like:
jobIdtitlecompanylocationsourceapplyUrldatePostedExport every run as JSON, CSV or Excel, or send it to your app, a database, Google Sheets, or an AI agent.
Related tools in Developer & Research Tools
Other ready-to-run tools in the same category — all pay-per-use on the Apify cloud.
GitHub Scraper
Search GitHub repos and users: stars, forks, language, topics, licence, plus user bio, company and followers. No token needed. $0.90 per 1,000 rows.
Stack Overflow / Stack Exchange Scraper
Search Stack Overflow and Stack Exchange by keyword or tag. Score, answer count, views, reputation and body text. $2 per 1,000 questions.
Package Registry Scraper (npm + PyPI)
Get npm and PyPI package metadata as JSON. Version, license, author, repo, keywords and npm monthly downloads. $2 per 1,000 packages.
arXiv Scraper
Search arXiv papers by title, author, abstract or category. Get full abstracts, authors, categories, DOI, dates and PDF links. $2 per 1,000 papers.
OpenAlex Scholarly Works Scraper
Search 250M+ OpenAlex papers with no API key. Get titles, authors, venue, year, citations, DOI, OA links and full abstracts. $2.00 per 1,000 papers.
Crossref Scholarly Works Scraper
Search 150M+ papers on Crossref: DOI, title, authors, journal, publisher, date, citations and abstract. No API key. $1.00 per 1,000 works.
Where this tool sits
- Categories
- Developer & Research Tools
Google Jobs Scraper: listings, employers and the boards they came from
Search the public Google Jobs results and get one row per listing: the job title, the employer, the location, which board Google picked it up from, Google's own stable job ID, the employment type and a link to the Google listing page.
The awkward part, stated plainly because it decides whether this fits your project. Google does not put the apply link in the results it serves, so applyUrl comes back null. What you get instead is applyOptions, the board names Google lists, and googleJobUrl, the listing page that carries the working apply buttons. salary and description come back null on that same path.
| Input | Job keywords, and optionally a location |
| Output | One row per job listing |
| Ceiling | 100 jobs per run, 8 scroll rounds |
| Account needed | None. No Google API key, no job board login |
| Price | $1.50 per 1,000 jobs, flat on every plan |
💼 What Google Jobs Scraper does
It runs your keywords against Google's jobs results for one country and language, reads the listing cards, and writes a row each.
The identity fields. jobId is Google's own htidocid, so the same listing keeps the same ID between runs and deduping is easy. When Google does not give one, a stable hash of the title, employer, location and link is used instead.
The routing fields. source is the board Google credits, like LinkedIn or Indeed. applyOptions lists every board Google offers for that listing. googleJobUrl opens the listing where the apply buttons live.
Duplicates are removed by Google's ID within a run, so the same posting surfacing twice arrives once.
📥 What you give it
{
"query": "software engineer",
"location": "Toronto",
"country": "CA",
"maxItems": 25,
"maxScrolls": 3,
"deadlineSeconds": 90
}
| Field | Default | What it is |
|---|---|---|
query | box starts at software engineer | Job title, skill or keywords. |
location | empty | City, region, country or a remote location. Set country to match it. |
country | US | Two-letter Google country code. This is the edition being searched. |
language | en | Google interface language, like en, fr or de. |
maxItems | 25 | Unique job rows to return, 1 to 100. It is also the most a run can bill you. |
maxScrolls | 3 | Rounds of loading more listings after the first set, 0 to 8. |
deadlineSeconds | 90 | Seconds allowed for fetching, 20 to 180. Read the note below. |
enableProxyFallback | on | Leave it on. Off means a refused first request has nowhere to go and the run returns no jobs. |
proxyConfiguration | leave it alone | Optional. Your own servers are used exactly as given. |
Set country to match your location. This is the one mistake that reliably returns nothing. Asking for Berlin while country is still US comes back empty; Berlin with country: "DE" returns jobs. Change both together.
deadlineSeconds bounds the fetching, not the whole run. A run can take longer than the number you set, because the steps that follow a slow fetch are not inside that budget.
📤 What you get back
A real row from a recent run, with the long Google link cut short:
{
"ok": true,
"jobId": "kqGSSHMeuPf_Dy4XAAAAAA==",
"title": "Software Engineer - E3",
"company": "Lockheed Martin",
"location": "Longmont, CO",
"source": "LinkedIn",
"applyUrl": null,
"sourceUrl": null,
"datePosted": "3 days ago",
"salary": null,
"description": null,
"employmentType": "Full-time",
"query": "software engineer",
"searchLocation": null,
"country": "US",
"googleJobUrl": "https://www.google.com/search?ibp=htl;jobs&q=software+engineer&htidocid=kqGSSHMeuPf_Dy4XAAAAAA%3D%3D&hl=en-US...",
"applyOptions": ["LinkedIn", "BeBee"]
}
| Field | What it is |
|---|---|
jobId | Google's htidocid. Stable between runs, so it is the key to dedupe on. |
source | The board Google credits the listing to, taken from its "via" line. |
applyOptions | Every board Google offers for that listing, by name. |
googleJobUrl | The Google listing page. This is where the apply buttons actually work. |
applyUrl, sourceUrl | null. Google does not serve them in these results. |
datePosted | Google's own wording, like 3 days ago. Not a date. |
salary, description | null on this path, even when Google prints them on the page. |
employmentType | Full-time, Contractor and so on, when Google states it. |
🧾 Reading the output
| Row | How to spot it | Charged |
|---|---|---|
| A job listing | ok: true and no _sample | yes |
| The sample row | _sample: true | no |
| A diagnostic | ok: false and an errorCode | no |
An empty query writes the sample row and stops, so you can see the shape before spending anything.
| Code | What it means |
|---|---|
NO_RESULTS | Google returned no listing cards. A location that does not match country lands here. |
BAD_INPUT | A value in the input is not usable, for example a country code that is not two letters. |
BLOCKED | Google would not serve that request. Re-run it or narrow the search. |
RATE_LIMITED | Google answered with a rate limit. Wait and try again. |
JS_REQUIRED | Google served a page that builds itself in the browser instead of listing cards. |
SERVER_ERROR | Google answered with an error of its own. |
NETWORK | Google was unreachable or the response never finished. |
The Overview table shows applyUrl, which is always empty, and hides googleJobUrl, applyOptions and employmentType, which are not. Read the dataset as JSON or export it.
▶️ How to run it
1. Open Google Jobs Scraper and click Try for free. 2. Put your keywords into Job keywords, replacing the example. 3. Type a Location, then set Google country to that country's code. Both, every time. 4. Set Maximum jobs, then click Start. 5. Download the dataset as JSON, CSV or Excel, or read it from the Apify API.
💰 How much does it cost?
$1.50 per 1,000 jobs, which is $0.0015 a row. The same rate on every Apify plan, no volume tiers.
A job is charged only once a complete row has been written. maxItems caps the rows, so it also caps what a run can bill.
Not charged as jobs: the sample row, every diagnostic row, duplicates removed within a run, and a search that comes back with nothing.
💡 What people use it for
- Watching a job title in one city daily and alerting on new
jobIdvalues. - Seeing which boards a company posts through, by counting
sourceandapplyOptionsacross a role. - Building a picture of hiring in a market, by running the same keywords against several
country
codes.
- Feeding titles, employers and locations into a CRM, using
googleJobUrlas the link a human opens.
🚧 What it does not do
- No apply links.
applyUrlandsourceUrlarenull. UseapplyOptionsfor the board names
and googleJobUrl for the page with the working buttons.
- No salary, no description. Both come back
nullon this path, even where Google shows them. datePostedis relative text, like2 days ago. Convert it against the run time if you need a
date.
- Expect roughly ten listings from one search. The 100 ceiling is a limit, not a promise, and
maxScrolls asks Google for more without guaranteeing it.
- Australia and Ireland come back empty right now. Confirmed working today:
US,GB,DE,
FR, ES, CA. Try one small search before planning a large run against anywhere else.
- A listing with no employer name is skipped, quietly, because a row without a company is not
much use.
deadlineSecondsdoes not bound the whole run, only the fetching inside it.- One search per run. Use a schedule or several runs for a list of titles.
🧭 Which Google scraper do you need?
| If you want | Use |
|---|---|
| Job listings from Google Jobs | This one |
| The ordinary web results page for a keyword | Google Search Results Scraper |
| News articles with the publisher's real link | Google News Scraper |
| Image tiles and their source pages | Google Images Scraper |
| Employers as businesses, with address and phone | Google Maps Scraper |
❓ Questions people ask
Do I need a Google API key or a job board account? No. These are public search results.
Why is applyUrl empty? Because Google does not put it in the results it serves. Open googleJobUrl and the apply buttons are there.
Why did my search return nothing? Nine times out of ten, country did not match location. Set both to the same place and try again.
Can I filter by salary or date? Not in the input. datePosted comes back as Google's relative wording and salary is not populated, so neither is something you can filter on here.
Can I schedule it? Yes. jobId is stable between runs, so diffing yesterday against today gives you the new postings.
Is scraping Google Jobs legal? These are public listings. Apify's write-up on scraping and the law is a good starting point, and we are not lawyers.
🆘 If something breaks
Open the Issues tab on the actor page. Send the keywords, the location, the country and the run ID. The errorCode on the diagnostic row usually names the problem on its own.