# Google Jobs Scraper: Salaries, Apply Links & New Jobs (`mitp1997/google-jobs-scraper`) Actor

Scrape Google Jobs listings with title, company, location, parsed salary, employment type, posting date, full description and direct apply links (LinkedIn, Indeed, Glassdoor…). Query variants beat the 10-jobs limit; only-new mode for job alerts. 34 countries, 8 languages.

- **URL**: https://apify.com/mitp1997/google-jobs-scraper.md
- **Developed by:** [Mit Parekh](https://apify.com/mitp1997) (community)
- **Categories:** Jobs, Lead generation, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$3.00 / 1,000 job results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Google Jobs Scraper: Salaries, Apply Links & New Jobs

**Google Jobs Scraper** extracts job listings from **Google Jobs** (the "Jobs" tab in Google Search) for any job title, keyword and location. Every job comes with the **title, company, location, salary (parsed into min / max / currency / period), employment type, posting date, full job description, highlights (qualifications, responsibilities, benefits) and all direct apply links** (LinkedIn, Indeed, Glassdoor, company career pages…). It works in **English, German, French, Spanish, Portuguese, Italian, Dutch and Hindi** markets. **Only-new mode** turns it into a daily job alert.

- 💼 **One table from every job board.** Google Jobs aggregates LinkedIn, Indeed, Glassdoor, ZipRecruiter, Naukri, StepStone, Welcome to the Jungle, InfoJobs, company ATS pages and more.
- 💰 **Salary parsing.** `"$120K–$150K a year"` becomes `salaryMin 120000`, `salaryMax 150000`, `salaryCurrency USD` and `salaryPeriod YEAR`. It also handles `€45.000–55.000 pro Jahr`, `₹6–10 lakh a year`, `R$ 5 mil por mês` and `50 an hour`.
- 🔗 **Direct apply links.** `applyOptions` lists every site the job can be applied on, with a source, a domain and the URL.
- 🔁 **Goes past Google's ~10 jobs per search.** Query variants (*senior*, *remote*, *part time*, *entry level*… localized per language) are searched automatically and deduplicated by job ID.
- 🆕 **Only new jobs.** It remembers job IDs between runs, so scheduled runs return (and charge for) only jobs you have not seen.
- 🌍 **34 countries** and auto-language per country: US, UK, India, Canada, Germany, France, Spain, Mexico, Brazil, Italy, Netherlands and more.
- 🪶 **No browser.** It runs on 256 MB through Apify's Google SERP proxy. **Status rows are free.** A query with no matches or no Google Jobs gives a free explanatory row, not a charge.

### What job data does it return?

Each dataset item is one unique job (`status: "ok"`):

| Field | Example |
|---|---|
| `title` | `Home Infusion Registered Nurse` |
| `company` | `Accurate Care Home Health` |
| `location` | `Chicago, IL` |
| `via` | `Indeed` (the source Google shows first) |
| `postedAgo` / `postedAt` / `postedDaysAgo` | `2 days ago` / `2026-10-21T13:54:57Z` (approximate, derived from "2 days ago") / `2` |
| `salary` | `50 an hour`, parsed into `salaryMin` 50, `salaryMax` 50, `salaryCurrency` USD, `salaryPeriod` HOUR |
| `employmentType` / `employmentTypes` | `Contractor` / `["CONTRACTOR"]` (normalized: FULL_TIME, PART_TIME, CONTRACTOR, INTERN, TEMPORARY…) |
| `workFromHome`, `benefits`, `degree` | `false`, `["Paid time off"]`, `No Degree Mentioned` |
| `highlights` | `{"Qualifications": [...], "Responsibilities": [...], "Benefits": [...]}` |
| `description` | Full job description text (`descriptionIsPreview: true` if Google only shows a snippet) |
| `applyOptions` | `[{"source": "Indeed", "domain": "indeed.com", "url": "https://www.indeed.com/viewjob?jk=..."}, {"source": "Glassdoor", ...}]` |
| `applyLink` | The first direct apply URL |
| `shareLink` | Google Jobs link for the search |
| `jobId` | Google's job ID (stable, used for deduplication and only-new mode) |
| `query`, `searchQuery`, `position`, `country`, `language`, `searchUrl`, `scrapedAt` | Your query, the exact variant searched, rank on that page and the locale |

Fill rates measured in our cloud tests (23 Oct 2026, 689 jobs, 12 markets): **title, company, via, description and apply links on 100% of jobs**, location 99%, employment type ~95%, posting date ~85%, salary ~40% (only where Google shows one).

Status rows (`status: "no_results"`, `"unavailable"` or `"error"`) explain queries that returned nothing. They are **free**. To analyse only jobs, filter `status = ok`; the **Status** view lists the explanations.

Dataset views: **Overview**, **Salaries**, **Details** (description + highlights), **Apply links**, **Status**.

### How to scrape Google Jobs

1. Enter one or more **job searches**, e.g. `software engineer`, `nurse`, `data analyst`.
2. Optionally set a **location** (`New York`, `Bangalore`, `Berlin`, `remote`…). It is appended to each search, just like typing *"nurse Chicago"* into Google.
3. Pick the **country**. The language is chosen automatically (Germany → German, Brazil → Portuguese…), or you can set it yourself.
4. Choose **max jobs per search** (default 50) and click **Start**.
5. Download the results as **CSV, Excel, JSON, XML or HTML**, or use the API.

### Why does Google Jobs show only ~10 jobs per search?

Google Jobs no longer pages through results for scrapers: one search returns **about 10 jobs**. To get more, the Actor runs **query variants** and merges them by job ID:

`software engineer New York` → `senior software engineer New York` → `remote software engineer New York` → `junior …` → `entry level …` → `lead …` → `part time …` → `contract …` → …

Variants are localized (e.g. German *Senior*, *Remote*, *Teilzeit*, *Werkstudent*; French *CDI*, *alternance*, *télétravail*; Hindi *fresher*, *work from home*). Each variant typically adds ~7–9 new unique jobs. The Actor stops a search when it has `maxJobsPerQuery` jobs, or after `stopAfterEmptyVariants` (default 3) variant searches in a row with no new jobs (the market is exhausted). You can add your own variants with **Custom variants** (`react`, `night shift`, `{query} startup`). In our test, `marketing manager Chicago` with `maxJobsPerQuery: 100` returned **100 unique jobs from 12 searches**.

Turn **Expand queries** off to get exactly one Google search (~10 jobs) per query.

### Input examples

Prefill: 20 software engineer jobs in New York

```json
{ "queries": ["software engineer"], "location": "New York", "maxJobsPerQuery": 20 }
```

Several roles in India, 100 each, posted in the last 7 days

```json
{ "queries": ["python developer", "data analyst", "sales executive"], "location": "Bangalore", "country": "IN", "maxJobsPerQuery": 100, "maxDaysOld": 7 }
```

German market (language auto = German)

```json
{ "queries": ["Softwareentwickler", "Buchhalter"], "location": "Berlin", "country": "DE", "maxJobsPerQuery": 50 }
```

Daily job alert: schedule this and get only jobs you have not seen before

```json
{ "queries": ["barista"], "location": "Seattle", "maxJobsPerQuery": 30, "onlyNewJobs": true, "stateKey": "seattle-barista-alert" }
```

Cheap listing feed without descriptions (smaller dataset)

```json
{ "queries": ["warehouse associate"], "location": "Dallas", "maxJobsPerQuery": 200, "includeDescription": false }
```

### Only new jobs (job alerts)

With `onlyNewJobs: true` the Actor stores the IDs of jobs it returned in the named key-value store **`google-jobs-scraper-state`** (in your account) under a key derived from the queries, location, country and language, or under your own `stateKey`. On the next run, jobs already seen are skipped and **not charged**. The Actor keeps searching variants until it finds `maxJobsPerQuery` *new* jobs or the market is exhausted. Combine it with an Apify **schedule** and an integration (email, Slack, Google Sheets, webhook) for a job alert. In our test, a second run of the same search skipped 18 already-seen jobs and returned 20 new ones. Delete the key in the store to reset the alert.

### Countries and languages

Tested in the cloud: 🇺🇸 US, 🇬🇧 UK, 🇨🇦 Canada, 🇮🇳 India (English and Hindi), 🇩🇪 Germany, 🇫🇷 France, 🇪🇸 Spain, 🇲🇽 Mexico, 🇧🇷 Brazil, 🇮🇹 Italy, 🇳🇱 Netherlands. The country list also includes Austria, Switzerland, Belgium, Ireland, Portugal, Poland, Sweden, Singapore, the Philippines, South Africa, Nigeria, the UAE, Argentina, Colombia, Chile and others.

⚠️ **Google Jobs is not available in every country.** In **Australia**, for example, Google shows regular web results instead of Google Jobs. The Actor detects this and returns a free `unavailable` row instead of junk. Coverage can also be thin. In the **Netherlands**, Google answered "not many matches" for roughly half of our Dutch test searches (see [Limitations](#limitations)).

Tip: write queries in the market's language (`Krankenpfleger` in Germany, `comptable` in France) and leave **Language** empty (auto). A language that doesn't match the market often returns nothing.

### Cost

**$3.00 per 1,000 jobs** ($0.003 per job). Apify platform usage is included, so you pay nothing extra for compute or proxy. You only pay for job rows. Status and error rows are free. Use **Maximum cost per run** in the run options to cap spend. The Actor stops exactly at your limit.

Example: 50 jobs for each of 10 searches = 500 jobs = **$1.50**.

### Use it as a Google Jobs API

```bash
curl -X POST "https://api.apify.com/v2/acts/mitp1997~google-jobs-scraper/run-sync-get-dataset-items?token=YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"queries": ["data engineer"], "location": "London", "country": "GB", "maxJobsPerQuery": 30}'
```

Python:

```python
from apify_client import ApifyClient
client = ApifyClient("YOUR_TOKEN")
run = client.actor("mitp1997/google-jobs-scraper").call(run_input={"queries": ["nurse"], "location": "Chicago", "maxJobsPerQuery": 50})
for job in client.dataset(run["defaultDatasetId"]).iterate_items():
    if job["status"] == "ok":
        print(job["title"], job["company"], job["salaryMin"], job["applyLink"])
```

A run summary (searches, jobs per query, variants used, stop reason) is saved as `RUN_SUMMARY` in the run's key-value store. It is also linked in the **Output** tab.

### Popular use cases

- **Job alerts and job boards.** Feed fresh listings into your niche job board, newsletter or Slack channel.
- **Salary research and benchmarking.** Compare pay by role, city and country with parsed min/max salaries.
- **Lead generation and recruiting.** Find companies that are hiring for a role (a buying signal for HR, staffing and SaaS sales).
- **Labour-market analytics.** Track demand for skills, remote share and employment types over time.
- **Job-seeker automation.** Get new matching jobs every morning with direct apply links.

### Reliability, retries and errors

- Every search goes through Apify's Google SERP proxy with retries (default 3) and a per-search timeout (default 75 s; Google sometimes needs up to a minute).
- Google's "no matches" answer is re-checked once with a fresh session before the Actor gives up on a query.
- Each query is independent. One failing query never fails the run; it gets a free `error` row. The run only fails if every query failed and nothing was saved.
- Duplicates across variants and queries are removed by Google job ID.

### FAQ

**Is scraping Google Jobs legal?** The Actor collects publicly visible job postings. You are responsible for how you use the data (e.g. GDPR for personal data, and the terms of the sites you link to). Job postings normally contain no personal data beyond a company name.

**Why did my search return only ~10 jobs?** Either *Expand queries* is off, or the market is small (variants stopped finding new jobs). Try a broader query, add custom variants, or remove the location.

**Why do I get `no_results`?** Google answered "no matches" for that search in that country. Check the spelling, use the market's language, or broaden the location.

**What does `unavailable` mean?** Google doesn't offer Google Jobs in that country (e.g. Australia), so there is nothing to scrape.

**Can I get the job posting date?** Yes. `postedAgo` is Google's text ("3 days ago"), and `postedAt` / `postedDaysAgo` are derived from it, so they are approximate to the day (hours for "x hours ago"). Use `maxDaysOld` to filter.

**Does it scrape LinkedIn or Indeed directly?** No. It reads Google Jobs, which already lists their postings with apply links back to them.

### Limitations

- About 10 jobs per Google search (Google's limit). Large result sets need variants, and each extra search adds ~7–9 jobs. Very large pulls (1,000+ jobs per query) are only possible in big markets.
- Google Jobs is missing in some countries (e.g. Australia). Coverage can be thin or inconsistent in others: in the Netherlands, about 50% of our test searches (16/32) returned jobs, and the same search sometimes returned jobs and sometimes not.
- `postedAt` is approximate. Salary is present only when Google shows it.
- Results depend on what Google shows at the time of the run. Google's layout changes can temporarily reduce field coverage. Report it and we fix it fast.

### Support

Found a bug or need a field? Open an issue in the **Issues** tab. We usually reply within a day. Related Actors: [Google Trends Scraper](https://apify.com/mitp1997/google-trends-scraper-reliable), [Keyword Ideas](https://apify.com/mitp1997/keyword-ideas-autocomplete), [Google Ads Transparency Scraper](https://apify.com/mitp1997/google-ads-transparency-scraper) (competitor ads on Google Search, Display and YouTube) and [Telegram Channel Scraper](https://apify.com/mitp1997/telegram-channel-scraper) (posts, views and subscriber counts from public Telegram channels).

# Actor input Schema

## `queries` (type: `array`):

What you would type into Google, e.g. <code>software engineer</code>, <code>nurse</code>, <code>python developer berlin</code>. Each search returns its own jobs; one run can contain many searches.

## `location` (type: `string`):

City, region or 'remote' appended to every search, e.g. <code>New York</code>, <code>London</code>, <code>Bangalore</code>. Leave empty if the location is already part of your searches.

## `country` (type: `string`):

Google country (<code>gl</code>). Results come from this market. Note: Google does not offer Google Jobs in some countries, e.g. Australia.

## `language` (type: `string`):

Google interface language (<code>hl</code>), e.g. <code>en</code>, <code>de</code>, <code>fr</code>, <code>es</code>, <code>pt-BR</code>, <code>it</code>, <code>nl</code>, <code>hi</code>. Leave empty to use the main language of the country (recommended: Google Jobs often returns nothing when the language does not match the market).

## `maxJobsPerQuery` (type: `integer`):

Google Jobs shows about 10 jobs per search. To get more, the Actor automatically searches close variants (senior, remote, junior, part-time, ...) and removes duplicates. Typical: 40-100 unique jobs per search term.

## `maxItems` (type: `integer`):

Hard cap for the whole run (all searches). 0 = no limit. You only pay for jobs saved.

## `expandQueries` (type: `boolean`):

Search localised variants of every query (e.g. 'senior …', 'remote …', 'teilzeit …') to collect more than 10 jobs per query. Switch off for exactly one Google search (max ~10 jobs) per query.

## `customVariants` (type: `array`):

Extra modifiers searched before the built-in ones, e.g. <code>react</code>, <code>night shift</code>. Use <code>{query}</code> to control placement, e.g. <code>{query} startup</code>.

## `stopAfterEmptyVariants` (type: `integer`):

Stop expanding a query once this many variant searches in a row return no new jobs (the market is exhausted).

## `maxDaysOld` (type: `integer`):

Skip jobs posted more than this many days ago (based on Google's 'x days ago'). 0 = any age. Jobs without a posting date are kept.

## `onlyNewJobs` (type: `boolean`):

Remember job IDs between runs (named key-value store <code>google-jobs-scraper-state</code>) and only save - and charge for - jobs this search has not returned before. Ideal for scheduled daily job alerts.

## `stateKey` (type: `string`):

Name for the remembered-jobs list. By default it is derived from queries + location + country + language, so changing the searches starts a fresh history. Set a fixed key to share history between tasks.

## `includeDescription` (type: `boolean`):

Full job description and Google's 'Job highlights' (qualifications, responsibilities, benefits). Switch off for smaller datasets.

## `maxConcurrency` (type: `integer`):

Google searches running at the same time.

## `maxRequestRetries` (type: `integer`):

Retries for timeouts, captchas and broken pages. Failed searches are never charged.

## `requestTimeoutSecs` (type: `integer`):

Google's SERP proxy sometimes needs up to a minute.

## `googleDomain` (type: `string`):

Optional Google domain to search, e.g. <code>www.google.nl</code> or <code>www.google.co.in</code>. Default <code>www.google.com</code> (the market is set by <i>Country</i> either way).

## Actor input object example

```json
{
  "queries": [
    "software engineer",
    "data analyst"
  ],
  "location": "New York",
  "country": "US",
  "language": "de",
  "maxJobsPerQuery": 20,
  "maxItems": 0,
  "expandQueries": true,
  "customVariants": [
    "react",
    "{query} startup"
  ],
  "stopAfterEmptyVariants": 3,
  "maxDaysOld": 0,
  "onlyNewJobs": false,
  "includeDescription": true,
  "maxConcurrency": 4,
  "maxRequestRetries": 3,
  "requestTimeoutSecs": 75
}
```

# Actor output Schema

## `overview` (type: `string`):

Title, company, location, salary, type, posted, via and apply link.

## `salaries` (type: `string`):

Parsed salary ranges: min, max, currency and period.

## `details` (type: `string`):

Descriptions, highlights, benefits, approximate posting date.

## `applyLinks` (type: `string`):

Every apply option (LinkedIn, Indeed, company site, ...) per job.

## `runSummary` (type: `string`):

Jobs per query, searches used, duplicates, failures and stop reason.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "queries": [
        "software engineer"
    ],
    "location": "New York",
    "maxJobsPerQuery": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("mitp1997/google-jobs-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "queries": ["software engineer"],
    "location": "New York",
    "maxJobsPerQuery": 20,
}

# Run the Actor and wait for it to finish
run = client.actor("mitp1997/google-jobs-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "queries": [
    "software engineer"
  ],
  "location": "New York",
  "maxJobsPerQuery": 20
}' |
apify call mitp1997/google-jobs-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,mitp1997/google-jobs-scraper"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/13xUasuMvo5caeSvY/builds/TNfrpavg7Zd0g930w/openapi.json
