# Google Jobs Scraper — Apply Links, Salaries & New-Job Monitor (`steadyscrape/google-jobs-scraper`) Actor

Google Jobs listings by keyword and location in 14 countries: title, company, salary, schedule, full description, highlights and the real apply link of every site carrying the posting. Keyword × location fan-out, de-duplicated, monitor mode for new jobs. Export to CSV, JSON or API.

- **URL**: https://apify.com/steadyscrape/google-jobs-scraper.md
- **Developed by:** [Steady Scrape](https://apify.com/steadyscrape) (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

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?

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 — apply links, salaries and a new-job monitor

Search **Google Jobs** by job title and location and get, for every posting: title, company, location, the site Google
credits, **salary as numbers**, schedule, benefits, the **full description**, Google's highlights (qualifications,
responsibilities, benefits) and **the real apply link on every site that carries the job** — LinkedIn, Indeed,
ZipRecruiter, the employer's own careers page — not Google's redirect.

Google Jobs aggregates LinkedIn, Indeed, Glassdoor, ZipRecruiter, Monster, Workday, Greenhouse, Lever and thousands of
company career sites, so one search here stands in for a dozen scrapers.

**US$0.003 per search + US$0.001 per job.** A full search of 10 jobs is US$0.013. Duplicates, jobs a monitor run
already delivered and searches Google did not answer cost nothing.

### Read this first: 10 jobs per search, by design

Google gives the first page of a jobs search — **10 jobs** — to any client that is not a signed-in browser session; the
"load more" behind it answers only to that browser's cookies. This Actor does not run a browser and does not pretend
otherwise. **Volume comes from many narrow searches**, which is also how you get better data:

- `keywords` × `locations` × `jobTypes` multiply into searches: 5 titles × 20 cities × 2 job types = 200 searches and up
  to 2,000 jobs, **de-duplicated across searches** (a job found twice is delivered and charged once).
- `datePosted` narrows each search to what is fresh, and **monitor mode** turns a daily schedule into a feed of new
  postings only.

Measured on 2026-09-20: 4 titles × 5 US cities × 2 job types = 40 searches → **389 unique jobs, 2,006 apply links, in
161 seconds**.

If you need the 300 results of one single search, use an Actor that rents a browser farm, and expect to pay for it.

### What makes it different

- **Real apply links.** Google hides every apply link behind `google.com/goto?url=…`. Each one is followed one step and
  cleaned of Google's tracking parameters, so you get `https://www.indeed.com/viewjob?jk=93dde939dcf8fb55`. 2,005 of
  2,006 links resolved in the run above; a link Google does not answer keeps Google's address, which still works.
- **Salary you can sort by.** `$100K–$127K a year`, `$2.1K–$2.2K a week`, `£22 an hour`, `₹9L–₹14L a year` become `min`, `max`, `currency`
  and `period` — thousands, lakhs and single rates included — next to the text as Google wrote it.
- **Monitor mode.** `onlyNewJobs` remembers every job it delivered to you and skips it next time: schedule it daily with
  `datePosted: "3days"` and each run is only what is new. Jobs cut off by your spending limit are not remembered, so
  the next run delivers them.
- **Fan-out with one budget.** Searches run ten at a time; rows and charges leave one at a time, so `maxJobs` and your
  spending limit are exact, not approximate.
- **Every search leaves a trace.** A search without jobs is a free `no_jobs` row; a search Google did not answer in
  three attempts is a free `blocked` row and is not charged. A run never ends with an empty dataset and no explanation.
- **Fails loudly when Google changes.** If a job card comes without its title or company the run stops with a clear
  error instead of filling your dataset with half-empty rows.

### Use cases

- **Job boards and aggregators**: fill a niche board from Google's aggregated feed, with the apply link of your
  preferred source.
- **Recruiting and staffing**: who is hiring for which roles in which cities, daily, with the ATS or board each
  employer posts on.
- **Sales intelligence**: companies hiring for a role are companies buying for that function. Filter by title, city
  and recency, and enrich from `company`.
- **Salary and labour-market research**: pay ranges by title and metro, share of remote postings, posting volume over
  time.
- **Job alerts**: a scheduled monitor run delivering only new postings to a webhook, Slack, Google Sheets, Make or n8n.

### Input

```json
{
    "keywords": ["software engineer", "data analyst"],
    "locations": ["New York", "Austin, TX", "Seattle"],
    "jobTypes": ["full_time", "contract"],
    "datePosted": "week",
    "country": "us",
    "onlyNewJobs": false
}
```

| field | what it does |
|---|---|
| `keywords` | Job titles or keywords. Each is searched in each location as "*keyword* jobs in *location*". |
| `locations` | Cities, regions or countries. Empty: each keyword is searched without a place. |
| `queries` | Searches in your own words, sent as written: `empleos de marketing en Madrid`. Use it for languages other than English. |
| `country` | Two-letter code of the Google that answers. Default `us`. See the list below. |
| `datePosted` | `any`, `today` (since yesterday), `3days`, `week`, `month` — Google's own filter, exact. |
| `jobTypes` | `full_time`, `part_time`, `contract`, `internship`. Each one is its own search. It steers Google's ranking; filter exactly on the `scheduleType` column. |
| `remoteOnly` | Adds Google's remote filter to every search. |
| `onlyNewJobs` | Monitor: skips jobs an earlier run of yours already delivered. |
| `resolveApplyLinks` | Default on. Off: links stay as Google's redirect addresses. |
| `maxJobsPerSearch` | 1–10. |
| `maxJobs` | Ceiling for the whole run; `0` for none. |

**Countries.** Jobs came back on 2026-09-20 for `us`, `gb`, `ca`, `in`, `de`, `fr`, `es`, `it`, `br`, `mx`, `za`, `sg`,
`ae` and `ph`. Google shows no job listings at all in `au`, `nl`, `ie` and `jp`. In the European Union Google shows a
**preview** of about 400 characters instead of the full description, and no highlights: that is what the row carries.
The page is always asked for in English, so `postedText` and `scheduleType` read the same in every country; titles and
descriptions stay in the language of the posting.

### Output

One row per job:

```json
{
    "status": "ok",
    "reason": null,
    "search": { "query": "registered nurse jobs in Seattle full time", "keyword": "registered nurse", "location": "Seattle", "jobType": "full_time", "country": "us" },
    "position": 6,
    "jobId": "CKZ42SVlMJJ0VVvdAAAAAA==",
    "title": "Ambulatory RN (Outpatient Clinic)",
    "company": "VTech Solution Inc",
    "companyLogo": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQoJvqD_A…",
    "location": "Seattle, WA",
    "via": "Indeed",
    "postedText": "5 days ago",
    "postedAt": "2026-09-15T03:02:06.821Z",
    "scheduleType": "Full-time and Contractor",
    "remote": false,
    "salary": { "text": "$2.1K–$2.2K a week", "min": 2100, "max": 2200, "currency": "$", "period": "week" },
    "tags": ["No degree mentioned"],
    "description": "Job Summary\n\nWe are seeking a dedicated and experienced Ambulatory Registered Nurse (RN) to join our outpatient care tea…",
    "highlights": [
        { "title": "Qualifications", "items": ["RN License for Washington (Required)", "…"] },
        { "title": "Benefits", "items": ["No weekends, No Call, no holidays", "…"] },
        { "title": "Responsibilities", "items": ["This vital role involves providing comprehensive nursing care to patients in a fast-paced outpatient setting, …", "…"] }
    ],
    "applyLinks": [
        { "publisher": "Indeed", "url": "https://www.indeed.com/viewjob?jk=93dde939dcf8fb55" },
        { "publisher": "Glassdoor", "url": "https://www.glassdoor.com/job-listing/jv?jl=1010262990846" },
        { "publisher": "SimplyHired", "url": "https://www.simplyhired.com/job/tSHCJDp9JZ9eYqRK-HVnZcQ3E5Bplzph-VLSlqGZLS4vZzN5pa8BIQ" }
    ],
    "googleUrl": "https://www.google.com/search?ibp=htl;jobs&q=registered+nurse+jobs+in+Seattle+full+time&htidocid=CKZ42SVlMJJ0VVvdAAAAAA%3D%3D…",
    "scrapedAt": "2026-09-20T03:02:06.821Z"
}
```

| field | notes |
|---|---|
| `status` | `ok` for a job; `no_jobs` or `blocked` for a search that delivered none, with the `reason` in words. |
| `search` | The search that found the job first: its words, keyword, location, job type and country. |
| `position` | 1–10, the rank Google gave the job in that search. |
| `jobId` | Google's id of the posting, the key for de-duplication and for the monitor. |
| `via` | The site Google credits the posting to. |
| `postedText`, `postedAt` | Google's words ("3 days ago") and the instant they name, counted back from `scrapedAt`. `null` when Google prints no age. |
| `scheduleType` | `Full-time`, `Part-time`, `Contractor`, `Internship` or a combination, as Google prints it. |
| `remote` | `true` when Google lists the job under "Anywhere" or tags it "Work from home". |
| `salary` | `null` when Google prints none. `currency` is the sign Google prints (`$`, `£`, `€`, `₹`). |
| `tags` | Google's other chips: benefits and "No degree mentioned". |
| `description` | The full posting text, line breaks kept (a ~400-character preview in the EU). |
| `highlights` | Google's summary of the posting by section; empty when Google made none. |
| `applyLinks` | Every site that carries the posting, in Google's order; the first is the one it credits. |
| `googleUrl` | The job on Google. |

The run also writes a `SUMMARY` record: searches per status, jobs, duplicates skipped, jobs the monitor skipped,
requests made and apply links left unresolved.

### Cost

| run | you pay |
|---|---|
| 1 search, 10 jobs | US$0.013 |
| 40 searches, 389 jobs (4 titles × 5 cities × 2 job types) | US$0.51 |
| 200 searches a day on a monitor that finds 150 new jobs | US$0.75 a day |

The search fee pays for the Google SERP proxy request behind each search; it is charged only when Google answers.
Set a maximum charge per run in the run options and the Actor stops exactly there.

### Limits

- 10 jobs per search (see above), and at most 2,000 searches per run.
- Google's ranking decides which 10. Narrow searches (title + city + job type + date) beat broad ones.
- Job types steer the ranking rather than filter: a "contract" search can list a full-time job. `scheduleType` tells.
- No company ratings, no "similar jobs", no sign-in features.
- Google changes its page without notice. The Actor is tested daily; if something breaks, open an issue and it gets
  fixed fast.

### Is it legal?

The Actor reads public search results, the same ones any visitor sees without an account, and collects no personal
data beyond what employers publish in a job posting. You are responsible for how you use the data and for complying
with the terms and laws that apply to you, including GDPR when postings name a contact person.

# Actor input Schema

## `keywords` (type: `array`):

One per line: <code>software engineer</code>, <code>registered nurse</code>. Each keyword is searched in each location, so 5 keywords and 10 locations are 50 searches. The word "jobs" is added for you.

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

Cities, regions or countries, one per line: <code>New York</code>, <code>Austin, TX</code>, <code>London</code>. Leave empty to search each keyword without a place.

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

Sent to Google as written, one search each: <code>empleos de marketing en Madrid</code>, <code>vagas desenvolvedor São Paulo</code>, <code>walmart cashier jobs near dallas</code>. Use this for languages other than English, where "<i>keyword</i> jobs in <i>place</i>" is not how people search.

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

Two-letter code of the country whose Google answers. Jobs were returned on 2026-09-19 for <code>us</code>, <code>gb</code>, <code>ca</code>, <code>in</code>, <code>de</code>, <code>fr</code>, <code>es</code>, <code>it</code>, <code>br</code>, <code>mx</code>, <code>za</code>, <code>sg</code>, <code>ae</code> and <code>ph</code>. Google lists no jobs at all in <code>au</code>, <code>nl</code>, <code>ie</code> and <code>jp</code>.

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

Google's own "date posted" filter. For a scheduled run, <b>today</b> or <b>3days</b> together with the monitor below keeps each run to what is new.

## `jobTypes` (type: `array`):

Each type you tick is searched separately, so ticking two doubles the searches and brings up to 10 more jobs for each keyword and location. The type steers Google's ranking rather than filtering strictly: every row carries the schedule Google prints (<code>scheduleType</code>), which is the column to filter on. Leave empty for every type in one search.

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

Adds Google's "remote" filter to every search.

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

Turns a scheduled run into a job alert: a job this Actor already delivered to you in an earlier run is skipped and not charged. The first run delivers everything and is the baseline.

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

Google hides each apply link behind its own redirect. With this on, every link is followed one step, so you get <code>https://www.linkedin.com/jobs/view/…</code> instead of <code>https://www.google.com/goto?url=…</code>. It costs nothing extra.

## `maxJobsPerSearch` (type: `integer`):

Google gives at most 10. Lower it to keep only the top results of each search.

## `maxJobs` (type: `integer`):

The run stops searching once it has delivered this many. <code>0</code> means no ceiling besides your spending limit.

## Actor input object example

```json
{
  "keywords": [
    "software engineer"
  ],
  "locations": [
    "New York"
  ],
  "country": "us",
  "datePosted": "any",
  "remoteOnly": false,
  "onlyNewJobs": false,
  "resolveApplyLinks": true,
  "maxJobsPerSearch": 10,
  "maxJobs": 0
}
```

# 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 = {
    "keywords": [
        "software engineer"
    ],
    "locations": [
        "New York"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("steadyscrape/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 = {
    "keywords": ["software engineer"],
    "locations": ["New York"],
}

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

```

## MCP server setup

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