# Google Jobs Scraper - Job Search Results by Query & Location (`sauliusautomatesit/google-jobs-scraper`) Actor

Search Google Jobs by keyword and location and get structured job results, source boards and apply links. Bulk query/location searches, deduplicated output, pay per job result.

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

## Pricing

from $3.40 / 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.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Google Jobs Scraper — Job Search Results by Query & Location

Search Google Jobs by keyword and location and get structured job results, source boards and
apply links.

Give it a list of job titles and a list of cities. It runs every combination against the Google
Jobs tab and returns clean, deduplicated rows — title, company, location, salary when Google
shows it, the source job board, and **direct apply links** to LinkedIn, Indeed, Lever, Workday
and company ATS pages.

***

### What you get per job

```json
{
  "jobId": "U_04ajnAmBmBGSuSAAAAAA==",
  "title": "Software Engineer I - Backend",
  "company": "Booking.com",
  "location": "London",
  "salary": null,
  "source": "Booking.com Careers",
  "applyLinks": [
    { "url": "https://jobs.booking.com/booking/jobs/30069?utm_campaign=google_jobs_apply", "domain": "booking.com" },
    { "url": "https://uk.linkedin.com/jobs/view/software-engineer-i-backend-at-booking-com-4455282813", "domain": "linkedin.com" }
  ],
  "postedAt": "7 days ago",
  "employmentType": "Full-time",
  "descriptionSnippet": "As a Backend Software Engineer you will work on ...",
  "googleJobsUrl": "https://www.google.com/search?q=software+engineer+London&udm=8",
  "query": "software engineer",
  "searchLocation": "London"
}
```

`salary` is `null` whenever Google does not display a salary chip — roughly 6 in 10 jobs. It is
**never inferred** from the description text, even when the description mentions a figure.
The same applies to every other field: missing means `null`, not guessed.

***

### Input

```json
{
  "queries": ["software engineer", "data analyst"],
  "locations": ["London", "Manchester"],
  "countryCode": "gb",
  "maxResultsPerSearch": 10,
  "maxTotalResults": 1000
}
```

That runs 4 searches (2 queries × 2 locations) and returns up to 40 unique jobs.

| Field | Type | Default | Notes |
|---|---|---|---|
| `queries` | string\[] | `[]` | Job keywords. Combined with every location. |
| `locations` | string\[] | `[]` | Cities/regions. Empty = search without a location. |
| `searches` | object\[] | `[]` | Explicit pairs instead of the grid; each may set its own `countryCode`. |
| `countryCode` | string | `"us"` | Selects the Google domain and locale. |
| `languageCode` | string | `"en"` | Passed to Google as `hl`. |
| `maxResultsPerSearch` | int | `10` | Google caps the Jobs tab at 10 per query (see limits). |
| `maxTotalResults` | int | `1000` | Hard cap on unique results for the run. |
| `retryOnEmpty` | bool | `true` | One retry when a search returns no jobs. |

#### Explicit pairs, with per-search country

```json
{
  "searches": [
    { "query": "chef", "location": "Edinburgh", "countryCode": "gb" },
    { "query": "barista", "location": "Seattle", "countryCode": "us" },
    { "query": "welder", "location": "Calgary", "countryCode": "ca" }
  ]
}
```

#### API

```bash
curl -X POST "https://api.apify.com/v2/acts/YOUR_USERNAME~google-jobs-scraper/runs?token=YOUR_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{
        "queries": ["registered nurse"],
        "locations": ["Leeds", "Bristol"],
        "countryCode": "gb"
      }'
```

```javascript
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: 'YOUR_TOKEN' });
const run = await client.actor('YOUR_USERNAME/google-jobs-scraper').call({
    queries: ['truck driver'],
    locations: ['Dallas', 'Houston'],
    countryCode: 'us',
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items);
```

***

### Pricing

Pay per result.

| Event | Price |
|---|---|
| `job_result` — one unique, valid job row | **$0.004** |
| `apify-actor-start` — Apify's standard start event, charged once per gigabyte of the run's memory (1 GB by default) | $0.00005 |

Those are the Free-plan prices. Apify's paid plans get the standard Store discount off every
result — Bronze 5 %, Silver 10 %, Gold and above 15 % — so `job_result` costs $0.0038, $0.0036 or
$0.0034 on those plans. Actor start stays $0.00005 on every plan.

You are charged **only for unique job results actually delivered**. Duplicates across searches,
searches that return nothing, and failed searches are never charged.

***

### Limits you should know before buying

These are properties of Google's Jobs tab, measured — not guesses.

- **10 results per query, no pagination.** Google's Jobs tab serves ten jobs and offers no
  working next-page mechanism. `start=`, `num=`, `ijn=` and filter chips were all tested and
  none returns an eleventh job. To get more volume, use **more query/location combinations**
  rather than deeper pages. Narrower queries ("paediatric nurse" vs "nurse") and more cities
  are the way to scale.
- **Coverage varies by country and by query.** Verified working: **US, UK, Canada, Germany,
  India**. **Australia returned no Google Jobs results at all** across every query and URL form
  tested. Ireland returns results for some queries and nothing for others — and the same is
  occasionally true inside supported countries (a specific role/city pair can return nothing
  while its neighbours return ten). When Google serves no job cards the Actor reports
  `NO_RESULTS` for that search and **charges nothing** for it. This is Google's coverage, not a
  scraper limitation.
- **Results move.** Google's result set for the same query changes over time; roughly 97% of
  job IDs persisted between repeated runs in testing. Job IDs are stable identifiers for the
  same posting, so they work for deduplication and change tracking across runs.
- **Salary is sparse.** Present on about 38% of jobs.

Measured reliability where Google serves jobs: **50/50 successful extractions** across 25
query/location combinations run twice (98% without the built-in single retry).

### What this Actor does not do

It returns Google Jobs search results as structured data. It does not scrape LinkedIn, Indeed
or ATS platforms directly, does not open individual job pages for enrichment, does not infer
salaries, and does not do company enrichment, candidate matching or CV scoring.

### Notes

This Actor is not affiliated with, endorsed by, or an official API of Google. It reads publicly
visible Google Search results. Coverage of Google Jobs is not guaranteed to be complete.
Apply links point to the third-party job boards and employer ATS pages that Google lists as
application options for each posting.

# Actor input Schema

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

Job search keywords, e.g. "software engineer". Each query is combined with every location below.

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

Cities or regions, e.g. "London". Leave empty to search without a location.

## `searches` (type: `array`):

Exact pairs instead of the query x location grid, e.g. \[{"query":"chef","location":"Edinburgh","countryCode":"gb"}]. Each item may set its own countryCode.

## `countryCode` (type: `string`):

Two-letter country code that selects the Google domain and locale. Verified coverage: us, gb, ca, de, in. Google serves no Jobs results for some countries (e.g. au).

## `languageCode` (type: `string`):

Interface language passed to Google as hl, e.g. "en".

## `maxResultsPerSearch` (type: `integer`):

Google returns up to 10 jobs per query on the Jobs tab and offers no pagination. Values above 10 only take effect together with "Extra date filters".

## `maxTotalResults` (type: `integer`):

Hard cap on unique job results across the whole run.

## `retryOnEmpty` (type: `boolean`):

Google intermittently serves a jobs-less page; one retry recovers it. Costs one extra SERP only when a search comes back empty.

## `extraDateFilters` (type: `array`):

Google's Jobs tab has no pagination. Adding date-filtered views surfaces some extra jobs the default view omits. Each filter costs one additional Google SERP per search, so enable only if you need more than 10 results per query.

## Actor input object example

```json
{
  "queries": [
    "software engineer",
    "data analyst"
  ],
  "locations": [
    "New York"
  ],
  "searches": [],
  "countryCode": "us",
  "languageCode": "en",
  "maxResultsPerSearch": 10,
  "maxTotalResults": 1000,
  "retryOnEmpty": true,
  "extraDateFilters": []
}
```

# Actor output Schema

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

One item per unique job result, with title, company, location, salary (when Google shows one), source job board, direct apply links, posted date, employment type, description snippet, and the query and location it came from. Download as JSON, CSV or Excel, or read it from this API endpoint.

# 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",
        "data analyst"
    ],
    "locations": [
        "New York"
    ],
    "countryCode": "us"
};

// Run the Actor and wait for it to finish
const run = await client.actor("sauliusautomatesit/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",
        "data analyst",
    ],
    "locations": ["New York"],
    "countryCode": "us",
}

# Run the Actor and wait for it to finish
run = client.actor("sauliusautomatesit/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",
    "data analyst"
  ],
  "locations": [
    "New York"
  ],
  "countryCode": "us"
}' |
apify call sauliusautomatesit/google-jobs-scraper --silent --output-dataset

```

## MCP server setup

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