# Google Jobs Scraper (`cornflower_vision/google-jobs-scraper`) Actor

Scrape Google Jobs with salary, full job description and direct apply links (Indeed, LinkedIn, company sites). More than 10 jobs per query, no duplicates. Pay only per job.

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

## Pricing

from $3.00 / 1,000 jobs

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

Scrape job listings from **Google Jobs**, the job search box Google shows for searches like "nurse jobs in Chicago". Google Jobs gathers postings from LinkedIn, Indeed, ZipRecruiter, Glassdoor, company career pages and hundreds of other job boards, so one search covers them all.

For every job you get the title, company, location, salary (as text and as numbers), employment type, posting date, the **full job description**, Google's job highlights (qualifications, benefits, responsibilities) and the **direct apply links** to each job board.

- **More than 10 jobs per search.** Google shows only 10 jobs per search page. The Actor also runs narrower searches for the same query and merges the results, so a query can reach 50 jobs or more (up to 100).
- **No duplicates.** Each job appears once per run, even when several queries or searches find it, and you pay for it once.
- **Pay only for jobs.** No charge for searches that find nothing, for retries or for duplicates.
- **Light.** No browser, so runs need little memory and cost little to run.

### What you can use it for

- Track who is hiring for a role in a city, and at what pay.
- Collect salary ranges for a job title across locations.
- Find companies hiring right now for B2B sales leads.
- Build a job board or a job alert feed.
- Feed job descriptions to an AI model to compare skills or requirements.

### Input

| Field | What it does | Default |
|---|---|---|
| **Search queries** (`queries`) | What you would type into Google, one per line, e.g. `nurse jobs in Chicago` or `python developer remote`. | required |
| **Locations** (`locations`) | Optional. Every query is searched in each location (added to the end of the query), e.g. `Austin, TX`, `Denver, CO`. | none |
| **Max jobs per query** (`maxJobsPerQuery`) | 1–100. The Actor stops a query earlier when Google has no more jobs for it. | 30 |
| **Date posted** (`datePosted`) | `any`, `month`, `week` or `day` (past 24 hours). | `any` |
| **Find more than 10 jobs per query** (`expandSearch`) | Turn off to run exactly one Google search per query (at most 10 jobs). | on |
| **Country** (`country`) | Two-letter code Google uses to localize the search, e.g. `us`, `gb` (or `uk`), `ca`, `in`. A plain `$` in salaries is that country's dollar. In some countries, write the query in that country's language, e.g. `việc làm python hồ chí minh` with `vn` (see [Limitations](#limitations)). | `us` |
| **Language** (`language`) | Google's interface language. See [Limitations](#limitations). | `en` |
| **Searches at the same time** (`maxConcurrency`) | 1–20. With 256 MB of memory (the default) the Actor runs at most 8 searches at once; give the run more memory for more. | 5 |

Example:

```json
{
    "queries": ["registered nurse", "warehouse associate"],
    "locations": ["Austin, TX", "Denver, CO"],
    "maxJobsPerQuery": 50,
    "datePosted": "week"
}
```

This runs 4 queries ("registered nurse Austin, TX", "registered nurse Denver, CO", ...), with up to 50 jobs posted in the past week for each.

### Output

One item per job. Example (description and lists shortened):

```json
{
  "query": "sales representative jobs charlotte",
  "rank": 12,
  "title": "Entry Level Sales Representative",
  "companyName": "Power Home Remodeling",
  "location": "Charlotte, NC",
  "via": "Indeed",
  "postedAt": "21 days ago",
  "postedDate": "2026-09-30",
  "employmentType": "Full-time",
  "salary": "$72K–$130K a year",
  "salaryMin": 72000,
  "salaryMax": 130000,
  "salaryCurrency": "USD",
  "salaryPeriod": "year",
  "educationRequirement": "No degree mentioned",
  "benefits": ["Dental insurance", "Health insurance", "Paid time off"],
  "description": "$3,900 Sign-On Bonus*\n\nEntry Level Sales Representative\n\nBuild Your Career With Purpose\n\n...",
  "jobHighlights": [
    { "title": "Qualifications", "items": ["Strong communication and interpersonal skills", "..."] },
    { "title": "Benefits", "items": ["$3,900 Sign-On Bonus*", "..."] },
    { "title": "Responsibilities", "items": ["Meet with homeowners to deliver engaging presentations...", "..."] }
  ],
  "applyUrl": "https://www.indeed.com/viewjob?jk=acf6c9af8e0ab385",
  "applyOptions": [
    { "title": "Apply directly on Indeed", "url": "https://www.indeed.com/viewjob?jk=acf6c9af8e0ab385" },
    { "title": "Apply on LinkedIn", "url": "https://www.linkedin.com/jobs/view/entry-level-sales-representative-at-power-home-remodeling-4473938005" }
  ],
  "extensions": ["21 days ago", "72K–130K a year", "Full-time", "No Degree Mentioned", "Dental insurance", "Health insurance", "Paid time off"],
  "companyLogo": "https://encrypted-tbn0.gstatic.com/images?q=tbn:...",
  "googleJobsUrl": "https://www.google.com/search?ibp=htl;jobs&q=...&htidocid=Q1qCd3IIQGefYGeYAAAAAA%3D%3D...",
  "jobId": "Q1qCd3IIQGefYGeYAAAAAA==",
  "foundWith": "sales representative jobs charlotte entry level",
  "scrapedAt": "2026-10-21T14:17:05Z"
}
```

Notes on some fields:

- Fields Google does not show for a job are `null` or empty (`[]`). In our tests (October 2026) Google showed a salary for about a third of US jobs.
- `postedDate` is estimated from `postedAt` ("5 days ago"); a month counts as 30 days.
- `salaryMin` and `salaryMax` are numbers from the salary text ("$22–$25 an hour" gives 22 and 25, `salaryPeriod` "hour"). A plain `$` is the dollar of the country you search (US dollars for `us`, Canadian dollars for `ca`).
- `description` is the full text. In the EU (Germany in our tests) Google shows only the first lines ("Job preview"); the Actor still gets the full text from the page.
- `jobHighlights` and `benefits` are shown by Google on US pages; British and Canadian pages had none in our tests.
- `applyUrl` and `applyOptions` are the job board links themselves, not Google redirects.
- `foundWith` is the Google search that found the job (see below). `rank` is the order in which jobs were found for the query.
- `jobId` is Google's id of the posting. Use it to spot the same job in later runs.

In the **Output** tab, the **Jobs**, **Salaries** and **Descriptions** views show the most useful columns. You can download everything as JSON, CSV, Excel, XML or HTML, or get it through the [Apify API](https://docs.apify.com/api/v2).

The key-value store also has a `SUMMARY` record: for each query, how many jobs, searches and requests it took, how many jobs were too old for `datePosted`, and why it stopped.

### How it gets more than 10 jobs per query

Google Jobs shows 10 jobs per search and has no page 2 for automated requests. So for each query the Actor runs, in this order, until it has `maxJobsPerQuery` jobs:

1. the query itself;
2. the same query with Google's "date posted" filters (past month, then past week);
3. the query with one extra word Google itself suggests: "full time", "part time", "entry level", "senior", "contract";
4. the query with the "past 24 hours" filter.

Every search returns jobs for your query, in the same place, and duplicates are removed. An extra word can tilt the mix, for example toward part-time or senior jobs; `foundWith` tells you which search found each job. If you only want the exact search, turn off **Find more than 10 jobs per query**.

After the first search, the extra searches a query still needs run at the same time, so a query with 30 jobs takes about as long as two searches. A query stops early when its first search has fewer than 10 jobs (Google has no more), or when extra searches find almost nothing new. A first search without any jobs is asked for 3 times before the query ends, because Google's answer changes from one request to the next.

### Pricing

This Actor uses **pay-per-event** pricing: you pay for each job in your results, **$3 per 1,000 jobs** at the time of writing (see the **Pricing** tab for the current price). Jobs found twice are charged once. Searches without jobs, retries, Google's blocks and jobs older than your `datePosted` cost you nothing.

A few examples:

- 1 query, 30 jobs: about $0.09
- 20 queries × 50 jobs: about $3
- 1,000 jobs per day for a month: about $90

You can set a maximum cost per run in the run options. The Actor then stops at or just under that amount (it does not open another page whose results it could not charge for) and keeps everything found so far.

### Limitations

- The data is what Google Jobs shows: if a job board gave Google no salary or date, the Actor has none either.
- Google's date filter now and then lets an older job through (a job from 21 days ago in a "past week" search in our tests). The Actor leaves such jobs out, free of charge (`olderThanDatePosted` in `SUMMARY`). Jobs without a posting date are kept.
- Up to 100 jobs per query. For more, split a broad query into narrower ones, for example by city. One run takes up to 1,000 searches (queries × locations); split bigger jobs into several runs.
- Tested on English pages of searches in the US, the UK, Canada and Germany (October 2026). Google Jobs is not offered in every country: an Australian search gave ordinary web results in our tests, so such searches return no jobs. In some countries Google shows no jobs for a query written in English: `python developer jobs Paris` (`fr`) and `python developer jobs in Ho Chi Minh City` (`vn`) gave "no Jobs matches", while `développeur python Paris` and `việc làm python hồ chí minh` gave jobs. Write the query in the country's language and keep `language` at `en`: titles and descriptions stay as the employer wrote them, and Google's labels stay in English, so every field is filled. With a language other than `en`, `postedAt`, `employmentType`, `salary` and `educationRequirement` stay empty (Google writes their labels in that language), but `extensions` still holds the same details as Google shows them.
- Each Google search goes through Apify's Google SERP proxy, from the country you choose, which takes a few seconds per search. When Google still answers as if the visitor were in another country, the Actor notices from the page and searches again (at no cost to you). Search queries are public Google searches; the Actor does not log in anywhere.

### FAQ

**Why did a query return fewer jobs than I asked for?**
Google had no more jobs for it, or the extra searches stopped finding new ones. The `SUMMARY` record says why each query stopped.

**Can I get only new jobs every day?**
Schedule the Actor with `datePosted: "day"` and keep the `jobId`s you already have.

**Is it legal to scrape Google Jobs?**
The Actor only collects job postings that are publicly shown in Google search results. You are responsible for how you use the data, especially any personal data in it. If in doubt, ask a lawyer.

**Something doesn't work or a field is missing.**
Please open an issue in the **Issues** tab with the run ID. Google changes its pages from time to time; the Actor saves a copy of any page that looks unusual, which makes fixes quick.

# Actor input Schema

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

What you would type into Google, one per line, for example <code>nurse jobs in Chicago</code> or <code>python developer remote</code>. Each query is searched separately.

## `locations` (type: `array`):

Search every query in each of these places, for example <code>Austin, TX</code> and <code>Denver, CO</code>. The location is added to the end of the query. Leave empty if your queries already say where.

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

Google shows 10 jobs per search. For more, the Actor also runs narrower searches for the same query (by date posted, and with words like 'part time' or 'entry level') and removes duplicates, so a query can reach 50 jobs or more. It stops early when Google has no more.

## `datePosted` (type: `string`):

Only jobs posted in this period.

## `expandSearch` (type: `boolean`):

Run narrower searches for each query to get past Google's 10 jobs per search (see Max jobs per query). Turn off to run exactly one search per query.

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

Two-letter country code Google uses to localize the search (gl), for example <code>us</code>, <code>gb</code>, <code>ca</code>, <code>in</code>, <code>de</code>. In some countries Google shows jobs only for a query in that country's language: in Vietnam write <code>việc làm python hồ chí minh</code>, not <code>python developer jobs in Ho Chi Minh City</code>.

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

Language of the Google interface (hl), for example <code>en</code>, <code>de</code>, <code>fr</code>. Posted date, employment type and salary are read from labels Google writes in English, so <code>en</code> gives the most complete results.

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

How many Google searches run in parallel. With 256 MB of memory (the default) at most 8 do; give the run more memory for more.

## Actor input object example

```json
{
  "queries": [
    "software engineer jobs in Austin, TX"
  ],
  "maxJobsPerQuery": 30,
  "datePosted": "any",
  "expandSearch": true,
  "country": "us",
  "language": "en",
  "maxConcurrency": 5
}
```

# Actor output Schema

## `jobs` (type: `string`):

No description

## `summary` (type: `string`):

No description

# 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 jobs in Austin, TX"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("cornflower_vision/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 jobs in Austin, TX"] }

# Run the Actor and wait for it to finish
run = client.actor("cornflower_vision/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 jobs in Austin, TX"
  ]
}' |
apify call cornflower_vision/google-jobs-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,cornflower_vision/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/DKX3qUmkMpb7jqiBS/builds/gIGSySeoH3G8ZRwxh/openapi.json
