# Google Jobs Scraper - Listings, Salaries & Apply Links (`bao-labs/google-jobs-api`) Actor

Scrape Google Jobs: job title, company, location, salary (when shown), job type, posting date and apply links. 50 to 64 unique jobs per query in our tests. Pay only for jobs returned. Not affiliated with Google.

- **URL**: https://apify.com/bao-labs/google-jobs-api.md
- **Developed by:** [Bảo Vương Gia](https://apify.com/bao-labs) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.00 / 1,000 job listings

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

### What does Google Jobs Scraper do?

**Google Jobs Scraper collects job listings from [Google Jobs](https://www.google.com/search?q=jobs\&udm=8)**, the job search built into Google that gathers postings from LinkedIn, Indeed, company career sites and other job boards. Give it a search like `nurse Chicago` and get back structured rows with the **job title, company, location, salary (when Google shows one), job type, posting date and direct apply links**.

- **Many jobs per search, not just Google's first 10.** It follows the date-posted and job-type filter pages that Google itself offers and removes duplicates. In our tests, common queries returned 50 to 64 unique jobs.
- **Tested in 16 countries, not only in English.** Fields are read by page structure, not by wording. Our test runs returned jobs in the United States, United Kingdom, Canada, Germany, France, Spain, Italy, Netherlands, India, Brazil, Mexico, Vietnam, Japan, Singapore, the Philippines and South Africa, with the interface languages en, de, fr, es, pt, it, nl, vi and ja. Other countries and languages have not been tested yet.
- **You pay only for jobs returned.** A search where Google has no jobs returns one free notice row.

Run it from Apify Console, call it through the API, schedule it, or connect it to Make, Zapier, Google Sheets and other tools through Apify's integrations.

> This Actor is an independent tool. It is **not affiliated with, endorsed by or sponsored by Google**, and the data is not official Google data: it is what Google Jobs shows publicly on its result pages.

### Who is it for?

- **Job market research**: who is hiring for a role in a city, at what pay, and on which job boards.
- **Recruiters and HR tech**: find companies hiring right now, each with a link to the posting.
- **Salary benchmarking**: collect the salary ranges Google shows for a role and place.
- **Job boards and aggregators**: feed postings, with their original apply links, into your own product.

### How to scrape Google Jobs

1. Open the **Input** tab.
2. Enter one or more **search queries**, the way you would type them into Google Jobs, with the place inside the query: `nurse Chicago`, `software engineer remote`.
3. Set the **country** (two-letter code, e.g. `US`, `GB`, `DE`) and the **language** (e.g. `en`, `de`).
4. Optionally change **Maximum jobs per query** (default 50).
5. Click **Start**. When the run finishes, open the **Output** tab or download the data as JSON, CSV, Excel or HTML.

### Input

| Field | What it does | Default |
|---|---|---|
| `queries` | Search queries, one per line. Put the place in the query: `accountant Toronto`. Required. | – |
| `country` | Two-letter code of the Google Jobs market to search. | `US` |
| `language` | Interface language. Job ads stay in the language they were written in. | `en` |
| `maxResults` | Stop a query after this many unique jobs (1–500). | `50` |

Under **Advanced**: `maxRetries`, `emptyRetries`, `saveFailedPages` and `proxyConfiguration`. The defaults are the recommended settings.

Example input:

```json
{
  "queries": ["nurse Chicago", "data analyst London"],
  "country": "US",
  "language": "en",
  "maxResults": 50
}
```

One run uses one country and one language. To cover several markets, start one run per country.

### Output

Each job is one row in the dataset. Download it as **JSON, CSV, Excel or HTML**, or read it through the API.

Example row (a real result from a test run on the Apify platform on 2026-09-29; the description and the list of apply links are shortened here):

```json
{
  "searchQuery": "nurse Chicago",
  "country": "US",
  "language": "en",
  "position": 1,
  "title": "Registered Nurse 1, Gen Surg (13W), Day Shift",
  "company": "Hospital: Rush University Medical Center",
  "location": "Chicago, IL",
  "source": "Indeed",
  "viaText": "via Indeed",
  "salary": null,
  "employmentType": "Full-time",
  "tags": [
    "37.50–57.19 an hour",
    "Health insurance"
  ],
  "postedText": "23 hours ago",
  "postedAt": "2026-09-28",
  "applyUrl": "https://www.indeed.com/viewjob?jk=3bd158e423226d7a&utm_campaign=google_jobs_apply&utm_source=google_jobs_apply&utm_medium=organic",
  "applyLinks": [
    {
      "name": "Apply on Indeed",
      "url": "https://www.indeed.com/viewjob?jk=3bd158e423226d7a&utm_campaign=google_jobs_apply&utm_source=google_jobs_apply&utm_medium=organic",
      "direct": true
    },
    {
      "name": "Apply on ZipRecruiter",
      "url": "https://www.ziprecruiter.com/c/Rush-University-Medical-Center/Job/Registered-Nurse-1-%7C-Gen-Surg-%7C-Day-Shift/-in-Chicago,IL?jid=07bf66bcdad61081&utm_campaign=google_jobs_apply&utm_source=google_jobs_apply&utm_medium=organic",
      "direct": true
    }
  ],
  "descriptionPreview": "Job highlightsIdentified by Google from the original job postQualificationsCurrent State of Illinois Registered Nurse licensure requiredMain…",
  "googleJobId": "lK7uIH1gvpcdgvAsAAAAAA==",
  "scrapedAt": "2026-09-29T20:04:50.061Z"
}
```

#### Data fields

| Field | Description |
|---|---|
| `title` | Job title |
| `company` | Hiring company |
| `location` | Location as shown by Google |
| `source` / `viaText` | The job board or site the posting comes from, e.g. `LinkedIn`, `Indeed`, and Google's "via ..." text |
| `salary` | Salary text, only when Google shows one (`null` otherwise) |
| `employmentType` | e.g. `Full-time`, `Part-time`, `Contractor` |
| `postedText` / `postedAt` | Posting age as Google shows it (`23 hours ago`) and the approximate date worked out from it |
| `applyUrl` | Link to apply on the first job board Google lists |
| `applyLinks` | Every apply option Google lists, each with its URL |
| `descriptionPreview` | The first part of the job description shown by Google |
| `tags` | Other labels Google shows, e.g. a pay rate or "Health insurance" |
| `googleJobId` | Google's id for the posting, used to remove duplicates |
| `searchQuery`, `country`, `language`, `position` | The search the row came from and the job's position in that search |
| `notice`, `message` | Only on notice rows (see the FAQ). A notice row is not a job. |

### How reliable is it?

Figures from our own test runs on the Apify platform on 2026-09-29 and 2026-09-30. They describe those runs, not a guarantee.

- **50 runs with 50 different queries** (30 common, 20 rare) in 15 countries and 9 languages: **50 of 50 runs finished without error.** 35 returned jobs. The other 15 returned a notice instead of jobs: 12 were rare queries for which Google Jobs itself had no postings, and 3 were in Australia and Poland, where Google Jobs was not available (see Limitations).
- **Depth**: with the limit set to 500, 10 common queries in 9 countries returned **50 to 64 unique jobs each**. All 90 result pages loaded.
- **Country check**: one query each in the United States, United Kingdom, Canada, Germany, France, Spain, Italy, Netherlands, India, Brazil, Mexico, Japan, Singapore, the Philippines and South Africa. Google Jobs returned jobs in all 15. Four of them (France, India, the Philippines, South Africa) timed out on the first try: India, the Philippines and South Africa worked on a second run, and France worked after we made the Actor wait longer for the page.
- **Completeness** over the 309 jobs from the 50 runs: title, company, source and apply link on all 309, location on 307. Google showed a posting date for 259 and a salary for 89; the rest are left empty because Google did not show them.

### How much does it cost to scrape Google Jobs?

This Actor uses **pay-per-event** pricing: you pay for each job returned, plus a fee for each run.

| | Price |
|---|---|
| Per job returned | $0.002 ($2 per 1,000 jobs) |
| Per run (start fee) | $0.00005 with the default 512 MB of memory |

- A search where Google has no jobs, or a country without Google Jobs, costs **no job fees**: it returns one notice row, and notice rows are never charged.
- The start fee is charged once per GB of run memory (minimum once): $0.00005 per GB. With the default 512 MB it is $0.00005 per run.
- You can set a **maximum cost per run** in the run options. We checked this with automated tests and with real runs on Apify. When the limit is reached the Actor stops and keeps the jobs already paid for. It can go over by at most 1 job, because of how the Apify library counts charges.

### Tips

- **Put the location in the query** (`nurse Chicago`) rather than relying on the country alone.
- **Want more than about 60 jobs for a role?** Split it into several queries: by city (`nurse Chicago`, `nurse Evanston`) or by title (`registered nurse`, `ICU nurse`).
- **The default limit is 50 jobs per query.** Raise `maxResults` if you want everything Google exposes for the search (in our tests up to 64).
- Rare job titles often have no postings on Google Jobs at all; the run then returns a `NO_MATCHES` notice.

### Limitations

- **Google Jobs is not available everywhere.** In our tests Google showed ordinary web results instead of Google Jobs for every query we tried in **Australia and Poland**, including 19 variations of proxy location, wording and URL form. It also happens now and then for rare queries elsewhere. Such searches return a `NO_JOBS_VERTICAL` notice.
- **Fields Google does not show are left empty.** Most often this is the salary and, for some postings, the posting date. Nothing is guessed or filled in.
- The posting date is **approximate**: Google shows relative ages ("3 days ago").
- Around 50 to 64 jobs per query is what Google's own filters exposed in our tests. It is not the full number of postings for a role.
- One run searches one country and one language.

### FAQ

**Is it legal to scrape Google Jobs?**
This Actor reads only **publicly available job postings**: what anyone sees on Google Jobs without logging in. It does not look for personal data, but the `descriptionPreview` text is copied from the posting and contains whatever the employer wrote there. You are responsible for using the data in line with the laws that apply to you and with the terms of the sites involved.

**Is this an official Google API?**
No. The Actor is independent and not affiliated with Google. It reads the public Google Jobs result pages.

**What does a notice row mean?**
A row with a `notice` field is not a job. `NO_MATCHES` means Google Jobs has no postings for that query (the Actor asked again before concluding this). `NO_JOBS_VERTICAL` means Google showed ordinary web results instead of Google Jobs: always in Australia and Poland in our tests, sometimes for rare queries elsewhere. Notice rows are never charged.

**Why did I get fewer jobs than my limit?**
Google exposes a limited set of postings per search. The Actor returns every unique job it can reach, up to your limit.

**Something not working?**
Open an issue in the **Issues** tab with the run link, and we will look into it. For a custom data solution, reach out through the same tab.

# Actor input Schema

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

What you would type into Google Jobs, one query per line. Put the place inside the query. Examples: "nurse Chicago", "accountant Toronto", "software engineer remote". Each query is searched separately.

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

Two-letter country code of the Google Jobs market to search, e.g. US, GB, DE, FR, IN, BR. It applies to all queries in the run. Google Jobs is not available in every country: in our tests it was not available in Australia (AU) and Poland (PL), where you get a free notice instead of jobs.

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

Interface language code, e.g. en, de, fr, es, pt, vi, ja. It sets the language of Google's page (for example "3 days ago" in `postedText`). Job ads stay in the language they were written in.

## `maxResults` (type: `integer`):

Stop a query after this many unique jobs (1-500). One Google results page holds 10; more come from Google's own date-posted and job-type filter pages. In our tests common queries had 50 to 64 unique jobs available, so a limit above about 60 rarely returns more. You pay only for jobs returned.

## `maxRetries` (type: `integer`):

How many times a page that fails to load is retried (0-10). The default is fine for most runs.

## `emptyRetries` (type: `integer`):

Google sometimes shows no jobs for a common query depending on the proxy location. Ask again this many times (0-5) before accepting an empty result. Empty results are never charged.

## `saveFailedPages` (type: `boolean`):

Store the raw HTML of pages that could not be read in the run's key-value store, for troubleshooting. Leave off unless support asks for it.

## `proxyConfiguration` (type: `object`):

Google SERP proxy is used by default and is the recommended setting. Leave as is unless you know you need another proxy.

## Actor input object example

```json
{
  "queries": [
    "nurse Chicago"
  ],
  "country": "US",
  "language": "en",
  "maxResults": 50,
  "maxRetries": 3,
  "emptyRetries": 2,
  "saveFailedPages": false,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "GOOGLE_SERP"
    ]
  }
}
```

# Actor output Schema

## `results` (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": [
        "nurse Chicago"
    ],
    "country": "US",
    "language": "en",
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "GOOGLE_SERP"
        ]
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("bao-labs/google-jobs-api").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": ["nurse Chicago"],
    "country": "US",
    "language": "en",
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["GOOGLE_SERP"],
    },
}

# Run the Actor and wait for it to finish
run = client.actor("bao-labs/google-jobs-api").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": [
    "nurse Chicago"
  ],
  "country": "US",
  "language": "en",
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "GOOGLE_SERP"
    ]
  }
}' |
apify call bao-labs/google-jobs-api --silent --output-dataset

```

## MCP server setup

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

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/IqGk8VYGvwbHjgDGb/builds/ktmZAU1VCl2EYQTlo/openapi.json
