# Google Jobs Scraper – Real Apply Links, Salary & 20+ Job Boards (`inovaflow/google-jobs-scraper`) Actor

Scrape Google for Jobs by phrase and location: one search covers LinkedIn, Indeed, ZipRecruiter, employer career pages and 20+ boards. Typed JSON per job with apply links resolved to their real destination, salary, schedule type, posted date, description, highlights and source board.

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

## Pricing

from $1.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?

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 for Jobs is the widest net there is: one search covers LinkedIn, Indeed, ZipRecruiter, Monster, Glassdoor, Built In, employer career pages and 20+ other boards, already merged by Google. The catch is that a Google job card has no address of its own — tools that only read the list hand you rich text with **no link a person can apply through**. **Google Jobs Scraper** fixes exactly that: every job comes back as typed JSON with its **"Apply on …" buttons resolved to the real destination** (the employer's ATS, LinkedIn, Indeed, …), plus salary, schedule type, posted date, full description, Google's highlights and the board the posting came from. No Google account, no browser to babysit.

- **Job boards & aggregators** — one source that already spans every major board, with a real apply link on every row.
- **Recruiters & sourcers** — see which boards carry a role, which employers post directly, and who is hiring right now.
- **Sales & B2B teams** — hiring is a buying signal: the run's companies-hiring summary ranks employers by open roles and shows their ATS links.
- **Analysts, researchers & AI agents** — salary and schedule data across boards, deduplicated by Google's own job identity.

### What does Google Jobs Scraper do?

Give it search phrases and locations (or paste a Google Jobs URL from your browser) and it returns the matching **Google for Jobs listings** — each with title, company, location, **the board it came from (`via`)**, posted date, **salary (parsed into min / max / currency / period)**, employment type, benefits chips, **Google's job highlights (qualifications, responsibilities, benefits)**, the full description, and **every apply option with its resolved URL**, ranked so `applyUrl` is the best place to apply (employer ATS first). Duplicates across searches are removed by Google's document id, and a **companies-hiring summary** is written next to the dataset. It works as a **Google Jobs API**: call it from the Apify API, schedule it, or use it from an AI agent through MCP.

### Why use this Google Jobs scraper?

- **Real apply links.** Each "Apply on …" button is followed to its destination; `applyUrl` is the best of them (employer ATS → recognised board → direct apply → anything but a syndication farm) and `atsUrl` flags a direct employer link when one exists.
- **20+ boards in one pass.** LinkedIn, Indeed, ZipRecruiter, Monster, Glassdoor, Built In, Dice, employer career pages and more — deduplicated.
- **Typed fields, not blobs.** `salaryMin` / `salaryMax` / `salaryCurrency` / `salaryPeriod`, `postedAt` as an ISO date, `employmentType`, `isRemote`, structured `highlights`.
- **Beyond ten results.** Google shows ten jobs per phrase; ask for more and the Actor widens the phrase with natural variants (`… remote`, `… senior`, `… contract`, …) until your cap is reached.
- **Any country and language.** Pick the Google edition (`us`, `gb`, `de`, `in`, …) and interface language.
- **Companies hiring.** `COMPANIES` ranks employers by open roles with titles, locations, the boards carrying them, newest posting and ATS links.
- **Pay per job, no minimums.** 5 jobs cost 5 jobs. Duplicates, filtered-out jobs and empty pages are free.

### What data does it extract?

| Field | Description |
| --- | --- |
| `id`, `googleUrl` | Google's job document id and the canonical Google Jobs link |
| `title`, `company`, `location`, `isRemote`, `via` | Core fields; `via` is the board the posting was found on |
| `postedAt`, `postedText`, `ageDays` | ISO date derived from Google's "N days ago", the original text and the age in days |
| `employmentType`, `benefits`, `qualificationChip` | Google's chips: Full-time / Contractor / …, benefit chips, "No degree mentioned" |
| `salaryText`, `salaryMin`, `salaryMax`, `salaryCurrency`, `salaryPeriod` | Salary when Google shows one |
| `applyUrl`, `applySource`, `atsUrl`, `directApplyAvailable` | Best resolved apply link, which board it is on, the employer's own ATS link when present |
| `applyOptions[]` | Every apply button: `source`, resolved `url`, Google's `googleUrl`, `directApply`, `isAts` |
| `highlights` | `qualifications[]`, `responsibilities[]`, `benefits[]` as Google extracted them |
| `description` | Full job description as readable text |
| `thumbnailUrl`, `searchQuery`, `searchLocation`, `searchVariant`, `scrapedAt` | Logo when shown; provenance (which phrase / variant found the job) |

### How to scrape Google for Jobs

1. Open the Actor and click **Try for free**.
2. Enter **Search phrases** (one per line) and **Locations** (one per line). Every phrase is searched in every location. Pick the **Country** if it is not the United States.
3. Optionally narrow with **Posted within**, **Remote only**, **Employment type**, or paste a Google Jobs URL from your browser.
4. Set **Max jobs per phrase** (10 = one Google page; more = the Actor widens the phrase) and click **Start**.
5. Read the **Output** tab, download **JSON / CSV / Excel**, or open `COMPANIES` to see who is hiring most.

#### How to get real apply links from Google Jobs

Leave **Resolve apply links** on (default). Every row then has `applyUrl` pointing at the board or employer page, `applyOptions[]` with all alternatives, and `atsUrl` when the employer's own applicant-tracking system is among them — ideal for job boards that want to send candidates straight to the source.

#### How to track new jobs across all boards every day

Set **Posted within** to *Last 24 hours* and schedule the Actor daily in Apify. Add a webhook or an integration (Slack, Google Sheets, Make, Zapier, n8n) to push new rows where your team works.

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

Pay-per-event: **$0.001 per job delivered** plus a small run-start fee — **1,000 jobs for about $1**. Duplicates, filtered-out jobs and empty pages are never charged. Google result pages are fetched through Apify's Google SERP proxy, which every Apify plan includes and bills per page (a page holds up to ten jobs); apply-link redirects use ordinary datacenter proxy traffic (a few kilobytes each).

### Input

Example — two roles in two cities, remote, posted this week, up to 30 jobs per phrase:

```json
{
    "queries": ["python developer", "data engineer"],
    "locations": ["New York", "Austin"],
    "country": "us",
    "postedWithin": "week",
    "remoteOnly": true,
    "maxJobsPerQuery": 30
}
```

Or paste a Google Jobs URL from your browser:

```json
{ "startUrls": [{ "url": "https://www.google.com/search?q=registered+nurse+jobs+Texas&udm=8" }] }
```

All fields are optional except one of `queries` / `startUrls`. Aliases such as `keywords`, `query`, `location`, `num_results`, `limit` and `jobTypes` are accepted, so inputs written for other Google Jobs scrapers usually work unchanged.

### Output

One dataset row per job:

```json
{
    "id": "5M12SHHWnEg7yxXuAAAAAA==",
    "title": "Lead Python Developer, New York",
    "company": "ION Group",
    "location": "New York, NY",
    "isRemote": false,
    "via": "Built In NYC",
    "postedAt": "2026-08-21",
    "postedText": "19 days ago",
    "ageDays": 19,
    "employmentType": "Full-time",
    "salaryText": "$140K–$210K a year",
    "salaryMin": 140000,
    "salaryMax": 210000,
    "salaryCurrency": "USD",
    "salaryPeriod": "year",
    "benefits": ["Health insurance", "Dental insurance"],
    "applyUrl": "https://www.builtinnyc.com/job/python-developer-new-york/1433153",
    "applySource": "Built In NYC",
    "atsUrl": null,
    "directApplyAvailable": true,
    "applyOptions": [
        { "source": "Built In NYC", "url": "https://www.builtinnyc.com/job/python-developer-new-york/1433153", "googleUrl": "https://www.google.com/goto?url=…", "directApply": false, "isAts": false },
        { "source": "Monster", "url": "https://www.monster.com/job-openings/lead-python-developer-…", "googleUrl": "https://www.google.com/goto?url=…", "directApply": false, "isAts": false },
        { "source": "Career.io", "url": "https://career.io/…", "googleUrl": "https://www.google.com/goto?url=…", "directApply": true, "isAts": false }
    ],
    "highlights": {
        "qualifications": ["Operating in an Agile environment, this position requires strong expertise in Python…"],
        "responsibilities": ["Develop scalable Python-based microservices…"],
        "benefits": ["Mid to Senior level – $180,000 - $220,000", "…"]
    },
    "description": "The Role\n\nLab49 is seeking a skilled Python Developer to work on complex and challenging projects…",
    "googleUrl": "https://www.google.com/search?ibp=htl;jobs&q=python+developer+jobs+New+York&htidocid=5M12SHHWnEg7yxXuAAAAAA%3D%3D",
    "searchQuery": "python developer",
    "searchLocation": "New York",
    "searchVariant": null
}
```

Also in the key-value store: `COMPANIES` (+ `COMPANIES.csv`) — employers ranked by open roles — `JOBS.csv`, and `OUTPUT` with run counts (jobs delivered, with salary, with ATS link, Google pages fetched, apply links resolved, filtered, duplicates).

### Using it from AI agents and code

Through the Apify MCP server an agent can call this Actor with plain-language inputs (`queries`, `locations`, `country`, `postedWithin`). Rows are flat and typed, `applyUrl` is always an openable link, and the `OUTPUT` summary says how many Google pages were used and how many jobs were delivered, skipped as duplicates or filtered out — so an agent never has to guess. From code, use the Apify API or clients:

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

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('inovaflow/google-jobs-scraper').call({
    queries: ['registered nurse'],
    locations: ['Texas'],
    postedWithin: 'day',
    maxJobsPerQuery: 30,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
```

### Tips

- **Phrase like a person.** Google understands `senior react developer fintech` or `warehouse jobs no experience`; the Actor adds `jobs` and the location for you.
- **Locations are matched as text**, so the Actor keeps only the city / region part (`Berlin, Germany` → `Berlin`) and puts the country into the Google edition (**Country**) — that is what Google's own job search does, and it finds far more than a full address would.
- **Ten jobs per page is Google's rule.** Each extra variant is one more Google page; `OUTPUT.googlePagesFetched` shows how many were used. Set `maxJobsPerQuery` to 10 for the cheapest possible run.
- **`searchVariant`** tells you which widening (`remote`, `senior`, …) surfaced a job — handy when tuning phrases.
- **Posting age** is only known when Google shows it; jobs without an age are kept even with a *Posted within* window.

### FAQ

**Do I need a Google account or API key?** No.

**Why do some jobs have `applyUrl` on a board rather than the employer?** Google lists the boards that carry the posting; when the employer's own page is not among them, the best available board is chosen. `applyOptions[]` has all of them.

**Can I get more than ten results per phrase?** Yes — set **Max jobs per phrase** higher; the Actor widens the phrase automatically. Expect diminishing returns past ~50 per phrase for narrow searches.

**Which countries work?** Any Google edition; pick it in **Country**. Job content comes back in its original language.

**Is it legal to scrape Google for Jobs?** Job postings are public information published to be found. You are responsible for how you use the data — respect the boards' and employers' terms, and personal-data laws where you operate.

### Support

Found a field you need or a search that misbehaves? Open an issue on the Actor's **Issues** tab — feedback shapes the roadmap.

# Actor input Schema

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

One search per line, as you would type it into Google — e.g. `python developer`, `registered nurse`, `marketing manager`. Google understands natural language, so `senior python developer fintech` works too.

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

Cities, regions or countries — every phrase is searched in every location. E.g. `New York`, `London`, `Texas`, `Germany`. Leave empty to search the whole country.

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

Which Google country edition to search — decides the default job market and formats. Two-letter code.

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

Google shows ten jobs per phrase. Ask for more and the Actor widens the phrase with natural variants (`… remote`, `… senior`, `… full-time`, …), merging and deduplicating the results — each variant is one more Google page.

## `postedWithin` (type: `string`):

Only jobs posted in this window (added to the Google query and checked against the posting age Google shows). Jobs without a stated age are kept.

## `remoteOnly` (type: `boolean`):

Adds `remote` to every phrase.

## `employmentTypes` (type: `array`):

Adds the type to every phrase. Leave empty for all.

## `titleInclude` (type: `array`):

Keep only jobs whose title contains at least one of these words (case-insensitive).

## `titleExclude` (type: `array`):

Drop jobs whose title contains any of these words — e.g. `senior`, `intern`, `manager`.

## `companyExclude` (type: `array`):

Drop jobs from companies whose name contains any of these words — handy for skipping staffing agencies.

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

When more than ten jobs per phrase are wanted, try natural variants of the phrase (`… remote`, `… senior`, `… contract`, …) until the cap is reached or variants stop producing new jobs. Turn off to fetch exactly one Google page per phrase.

## `resolveApplyLinks` (type: `boolean`):

Follow every *Apply on …* button to its real destination (employer ATS, LinkedIn, Indeed, …) so `applyUrl` is a link people can open. Turn off to keep only Google's redirect links (faster).

## `maxApplyOptions` (type: `integer`):

Google lists several boards carrying the same job; this caps how many are resolved and returned.

## `startUrls` (type: `array`):

Paste `google.com/search?q=…&udm=8` (Jobs tab) URLs from your browser — their phrase, country and language are used as additional searches.

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

Google interface language for the search, e.g. `en`, `de`, `fr`. Job content stays in its original language either way.

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

How many Google result pages are fetched at once.

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

Google pages always go through Apify's Google SERP proxy (included in every plan, billed per page). This proxy is only used to follow apply-link redirects — the default datacenter proxy is fine.

## Actor input object example

```json
{
  "queries": [
    "python developer"
  ],
  "locations": [
    "New York"
  ],
  "country": "us",
  "maxJobsPerQuery": 10,
  "postedWithin": "any",
  "remoteOnly": false,
  "employmentTypes": [],
  "expandQueries": true,
  "resolveApplyLinks": true,
  "maxApplyOptions": 10,
  "language": "en",
  "maxConcurrency": 3,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

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

One row per job listing with typed fields and resolved apply links.

## `companies` (type: `string`):

Employers ranked by open roles found in this run — titles, locations, boards carrying them, newest posting, ATS links (hiring-intent signal).

## `jobsCsv` (type: `string`):

Spreadsheet-ready jobs export (first 5,000 rows).

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

Counts: jobs delivered, with salary, with employer ATS link, Google pages fetched, apply links resolved, filtered, duplicates.

# 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": [
        "python developer"
    ],
    "locations": [
        "New York"
    ],
    "maxJobsPerQuery": 10,
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("inovaflow/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": ["python developer"],
    "locations": ["New York"],
    "maxJobsPerQuery": 10,
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("inovaflow/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": [
    "python developer"
  ],
  "locations": [
    "New York"
  ],
  "maxJobsPerQuery": 10,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call inovaflow/google-jobs-scraper --silent --output-dataset

```

## MCP server setup

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