# Google Jobs Scraper & API: Apply Links, Salary, Description (`varnmill/google-jobs-scraper`) Actor

Scrape Google Jobs (the search Google builds from LinkedIn, Indeed, Glassdoor, ZipRecruiter and career sites) by keyword and location or URL, with every direct apply link, salary, highlights and full description, no login needed. Only-new-jobs mode for scheduled alerts; $2 per 1,000 jobs.

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

## Pricing

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

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 & API: Apply Links, Salary, Description

Get job postings from **Google Jobs** (the search Google builds from LinkedIn, Indeed, Glassdoor, ZipRecruiter, Monster and employer career sites) by keyword and location or from a Google jobs URL, each with **every direct apply link** (real URLs, not Google redirects), salary, job type, posting date, Google's job highlights and the full description, as JSON, CSV or Excel, with no login, Google account or proxy setup.

**Try it:** Click **Start** with the default input: 10 data analyst jobs in Chicago in 8 s for about $0.02 (run of October 21, 2026, build 1.0.9: one Google request, 0 duplicates, 4 of 10 with a salary, 6 apply links per job on average).

From that default run:

| Title | Company | Via | Posted | Salary | Apply links |
|---|---|---|---|---|---|
| Data Analyst III - Consulting | GHX | SimplyHired | 3 days ago | $74K–$98K a year | 1 |
| Senior Data Analyst | TransUnion | Indeed | 15 days ago | $90K–$150K a year | 8 |
| Business Intelligence Analyst / Data Engineer | GEICO | LinkedIn | 6 days ago | $91,225–$172,200 a year | 8 |
| Data Visualization Analyst | Lockton, Inc. | Indeed | | $75K–$115K a year | 8 |
| Sr. Data Analyst | Capital One | ZipRecruiter | 15 days ago | | 8 |

**Pricing:** **$2 per 1,000 jobs** ($0.002 each), plus $0.003 per search per run only in only-new-jobs mode and $0.00005 per run start. Duplicates, searches with no jobs and failed runs are not charged; Google SERP proxy and platform usage are included.

Google shows about 10 jobs per search; raise **Max jobs per search** and the Actor runs related searches (full time, remote, entry level, senior, part time, contract and more) and merges them without duplicates, usually 60 to 90 jobs per keyword and location. Turn on **Only jobs not returned by earlier runs** and schedule it for daily job alerts or lists of hiring companies.

### More real examples

#### Warehouse associate, Dallas (October 12, 2026)

Input:

```json
{
  "queries": ["warehouse associate"],
  "locations": ["Dallas, TX"],
  "maxJobsPerSearch": 30
}
```

Result: **30 unique jobs in 83 seconds** from 5 Google requests (1 plain search plus 4 related searches; 7 duplicates removed and not charged). 17 of 30 had a salary, each job had on average 5.0 direct apply links, and the most common sources were LinkedIn (12), Indeed (5), Amazon Careers (2), Learn4Good (2). Cost at $2 per 1,000 jobs: $0.06.

First job in the dataset (description shortened here):

```json
{
  "jobId": "Kla7jhfm7fquCkGaAAAAAA==",
  "title": "Warehouse Associate - $26/hr +",
  "company": "Uline",
  "location": "Dallas, TX",
  "via": "LinkedIn",
  "postedAgo": "5 days ago",
  "postedAt": "2026-10-08T00:56:23+00:00",
  "employmentType": "Full-time",
  "salary": {
    "min": 26,
    "max": 32,
    "currency": "USD",
    "period": "HOUR",
    "text": "$26–$32 an hour"
  },
  "isRemote": false,
  "noDegreeMentioned": true,
  "applyLink": "https://www.linkedin.com/jobs/view/warehouse-associate-%2426-hr-%2B-at-uline-4476713101",
  "applyLinks": [
    {
      "source": "LinkedIn",
      "url": "https://www.linkedin.com/jobs/view/warehouse-associate-%2426-hr-%2B-at-uline-4476713101",
      "shownOnGoogle": true
    },
    {
      "source": "Indeed",
      "url": "https://www.indeed.com/viewjob?jk=19a784aedc300565",
      "shownOnGoogle": true
    },
    {
      "source": "Uline - Uline Careers",
      "url": "https://gfj.uline.jobs/hiring/warehouse-associate-26-hr-dallas-tx-69c295d3ca70fe08f3a9ce16",
      "shownOnGoogle": true
    },
    {
      "...": "5 more"
    }
  ],
  "highlights": {
    "Qualifications": [
      "High school diploma or equivalent",
      "Frequently move packages weighing up to 50 - 70 lbs",
      "... 2 more"
    ],
    "Benefits": [
      "Pay from $26 to $32 per hour with significant growth and earning potential!",
      "Great Pay and Benefits: Competitive pay and comprehensive benefits to support your future and well-being",
      "... 6 more"
    ],
    "Responsibilities": [
      "Full-Time Hours: Monday - Friday, 10:30 AM to 7 PM + overtime opportunities",
      "Pick customer order items and package them for same day shipping",
      "... 2 more"
    ]
  },
  "description": "Warehouse Associate\n\nPay from $26 to $32 per hour with significant growth and earning potential!\n\nTexas Branch - Irving, TX\n\nGo-getters wanted! Looking to join a top-tier team? Join Uline as a Warehouse Associate for job ...",
  "googleJobsUrl": "https://www.google.com/search?q=warehouse+associate+jobs+in+Dallas,+TX&ibp=htl;jobs&udm=8&htidocid=Kla7jhfm7fquCkGaAAAAAA%3D%3D",
  "position": 2,
  "searchQuery": "warehouse associate",
  "searchLocation": "Dallas, TX",
  "googleQuery": "warehouse associate jobs in Dallas, TX",
  "foundBy": "plain search",
  "country": "us",
  "scrapedAt": "2026-10-13T00:56:23+00:00"
}
```

### What you get

- **Direct apply links** for every board Google lists for the job (often 5 to 10: LinkedIn, Indeed, ZipRecruiter, Glassdoor, the employer's own site...), with `utm_` tags removed.
- **Salary** parsed into `min`, `max`, `currency` and `period` (YEAR, HOUR, MONTH...) when Google shows one. In our US test runs 43 to 57 percent of jobs had a salary.
- **Job highlights** (Qualifications, Benefits, Responsibilities) and the **full description** (usually 2,000 to 9,000 characters).
- **Posting age** as shown (`3 days ago`) and as an approximate ISO date (`postedAt`).
- **Employment type**, `isRemote`, `noDegreeMentioned`, the board it came `via`, and a link to the job on Google.
- **Google's job ID** (`jobId`), stable across runs, so you can de-duplicate or join with earlier data.

### How it works, and how many jobs you get per search

Google shows about **10 jobs per search** and no longer serves a usable "next page" to scrapers. To go deeper, this actor runs a set of related searches for each keyword and location (full time, entry level, senior, part time, contract, remote, hybrid, internship, posted today, posted this month and more), merges them, and removes duplicates by Google's job ID. It stops as soon as related searches stop finding new jobs, so you never pay for padding.

In our tests one keyword plus a city reached **60 to 90 unique jobs** (12 searches for "marketing manager, New York" gave 91). Set `maxJobsPerSearch` to what you need (default 50). For more coverage, add more locations (nearby cities) or more specific keywords.

Pages are fetched through Apify's Google SERP proxy with automatic retries; slow or broken Google answers are retried up to 4 times, so runs finish instead of timing out.

### Input examples

**Keywords × locations** (each keyword is searched in each location):

```json
{
  "queries": ["registered nurse", "software engineer"],
  "locations": ["Austin, TX", "Denver, CO"],
  "maxJobsPerSearch": 40,
  "maxItems": 200,
  "datePosted": "week"
}
```

**Paste Google jobs URLs** (from `google.com/search?...&udm=8` or older `ibp=htl;jobs` links; filters in the URL are kept):

```json
{
  "startUrls": [{ "url": "https://www.google.com/search?q=python+developer+jobs+remote&udm=8&hl=en&gl=us" }],
  "maxJobsPerSearch": 20
}
```

**Only new jobs, for schedules** (daily job alerts, lead lists of hiring companies):

```json
{
  "queries": ["electrician"],
  "locations": ["Phoenix, AZ"],
  "onlyNewJobs": true,
  "stateName": "phoenix-electricians"
}
```

The first run returns everything it finds. Later runs with the same `stateName` return, and charge for, only jobs that earlier runs have not returned. To keep scheduled checks cheap, later runs look at Google's first page for each search and run the related searches only when that page has at least 3 new jobs. For daily alerts, add `"datePosted": "today"` so the first page holds the freshest jobs. Jobs not seen for `forgetAfterDays` (default 60) are forgotten.

Countries: United States, United Kingdom, Canada, Australia, India, New Zealand, Singapore, South Africa, UAE, Philippines, Malaysia, Nigeria, Kenya, Brazil, Mexico, Japan. Google has switched its jobs search off in most EU countries.

### Output fields

| Field | Example |
|---|---|
| `jobId` | `FYe5zXq-syi_TXg3AAAAAA==` |
| `title` | `Lead Finance Data Analyst - Enterprise Reporting` |
| `company` | `UL Solutions` |
| `location` | `Chicago, IL` (or `Anywhere` for remote) |
| `via` | `LinkedIn` |
| `postedAgo` / `postedAt` | `5 hours ago` / `2026-10-12T19:41:31+00:00` |
| `employmentType` | `Full-time` |
| `salary` | `{"min": 95000, "max": 112000, "currency": "USD", "period": "YEAR", "text": "$95K–$112K a year"}` |
| `isRemote`, `noDegreeMentioned` | `false`, `true` |
| `applyLink` | first apply URL (the `via` source) |
| `applyLinks` | `[{"source": "LinkedIn", "url": "https://www.linkedin.com/jobs/view/...", "shownOnGoogle": true}, ...]` |
| `highlights` | `{"Qualifications": [...], "Benefits": [...], "Responsibilities": [...]}` |
| `description` | full text (turn off with `includeDescription: false`) |
| `googleJobsUrl` | opens the job on Google |
| `searchQuery`, `searchLocation`, `googleQuery`, `foundBy`, `position`, `country`, `scrapedAt` | where and when it was found |

The dataset has three views: Overview, All fields, and Apply links (one row per link). A run summary (Google requests, retries, duplicates removed, jobs returned) is saved as `RUN_SUMMARY` in the run's key-value store.

### Pricing

Pay per result, no subscription:

| Event | Price |
|---|---|
| Job returned | **$0.002** ($2 per 1,000 jobs) |
| New-jobs check (only with `onlyNewJobs`) | $0.003 per search per run |
| Actor start | $0.00005 |

You are not charged for duplicates, for jobs skipped by `onlyNewJobs`, or for searches where Google has no jobs. The new-jobs check covers the Google request a scheduled run makes even when nothing new has been posted; normal runs never pay it. Platform usage and proxies are included.

| You want | Cost |
|---|---|
| 100 jobs | $0.20 |
| 1,000 jobs | $2.00 |
| Daily alert for 5 searches, about 20 new jobs a day | about $1.65 a month ($1.20 for jobs + $0.45 for checks) |

### Accuracy and limits

- Data is what Google shows on its jobs search at run time. `postedAt` is computed from Google's "N days ago" and is approximate (Google rounds to hours or days; "30+ days ago" becomes 30 days).
- Google returns about 10 jobs per search; depth comes from related searches (see above), so very broad keywords in big cities will not return every posting Google has. Narrow keywords and more locations give better coverage.
- The `remote` related search can add nationwide "Anywhere" jobs; filter on `isRemote` or `location` if you want only local ones.
- Each Google page takes 5 to 50 seconds to load. A run with one keyword and 10 jobs takes under a minute; 100 jobs across two searches takes about 2 to 3 minutes.
- Company logos are not included.
- Salary currency is detected from the symbol; plain numbers without a symbol get `currency: null`.

### FAQ

**Is there an API?** Yes. Every Apify actor is an API: call `POST https://api.apify.com/v2/acts/varnmill~google-jobs-scraper/run-sync-get-dataset-items?token=YOUR_TOKEN` with the input JSON and get the jobs back as JSON. It also works from Python, JavaScript, Make, n8n, Zapier and the Apify MCP server for AI agents.

**Do I need a proxy or a Google account?** No. Google is fetched through Apify's Google SERP proxy, which is included in the price.

**Why only about 10 jobs per Google search?** That is what Google's jobs search returns per query today. The related searches are how you get 60 to 90 per keyword and location.

**Are the apply links direct?** Yes. They are the real posting URLs (LinkedIn, Indeed, employer ATS...), not `google.com/url` redirects.

**Can I get jobs posted in the last 24 hours only?** Set `datePosted` to `today`. For alerts, combine it with `onlyNewJobs` on a daily schedule.

**Is scraping Google Jobs legal?** The actor only collects job postings that are publicly shown to anyone on Google, without logging in. Make sure your use of the data complies with the laws that apply to you.

### Changelog

- **1.0.10** (October 21, 2026): README leads with the default-input example from an October 21 run. No code changes.
- **1.0** (October 12, 2026): First release. Keyword × location search, Google jobs URLs, related-search depth with de-duplication by job ID, direct apply links, salary parsing, highlights, full descriptions, only-new-jobs mode for schedules, 16 countries.

# Actor input Schema

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

Job titles or keywords, one per line, for example 'registered nurse' or 'python developer'. Each one is searched once per location. The word 'jobs' is added for you.

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

Cities, states or regions, for example 'Austin, TX' or 'Manchester'. Leave empty to search the whole country.

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

Optional. Paste Google jobs search URLs (google.com/search?q=...\&udm=8, or older ibp=htl;jobs links). Their query and filters are kept. Use together with or instead of Job searches.

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

Google shows about 10 jobs per search. To go further, the actor runs related searches (full time, remote, entry level, senior, part time, contract and more) and merges them without duplicates, until this number is reached or the related searches stop finding new jobs (usually 60 to 90 per keyword and location).

## `maxItems` (type: `integer`):

The run stops after this many jobs across all searches. You pay per job returned, so this also caps the cost.

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

Which Google market to search. Google has switched its jobs search off in most EU countries, so they are not listed.

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

Google interface language code (hl), for example 'en' or 'es'. Leave empty for the country's usual language.

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

Ask Google for recently posted jobs only.

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

For scheduled runs: remember every job ID in a named key-value store and output (and charge for) only jobs that earlier runs with the same state name have not returned. Later runs read Google's first page per search and run related searches only when it has at least 3 new jobs. Adds a $0.003 check fee per search per run.

## `stateName` (type: `string`):

Runs that share a state name share their memory of seen jobs. Use a different name per saved search or task.

## `forgetAfterDays` (type: `integer`):

Jobs not seen again for this many days are forgotten and count as new if they come back.

## `includeDescription` (type: `boolean`):

The full job description is usually 2,000 to 9,000 characters. Turn off for smaller datasets; the price is the same.

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

How many Google pages to load at once. Each page takes 5 to 50 seconds, so higher is faster for many searches.

## Actor input object example

```json
{
  "queries": [
    "data analyst"
  ],
  "locations": [
    "Chicago, IL"
  ],
  "maxJobsPerSearch": 10,
  "maxItems": 10,
  "country": "us",
  "datePosted": "any",
  "onlyNewJobs": false,
  "stateName": "default",
  "forgetAfterDays": 60,
  "includeDescription": true,
  "maxConcurrency": 8
}
```

# Actor output Schema

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

No description

## `jobsCsv` (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": [
        "data analyst"
    ],
    "locations": [
        "Chicago, IL"
    ],
    "maxJobsPerSearch": 10,
    "maxItems": 10
};

// Run the Actor and wait for it to finish
const run = await client.actor("varnmill/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": ["data analyst"],
    "locations": ["Chicago, IL"],
    "maxJobsPerSearch": 10,
    "maxItems": 10,
}

# Run the Actor and wait for it to finish
run = client.actor("varnmill/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": [
    "data analyst"
  ],
  "locations": [
    "Chicago, IL"
  ],
  "maxJobsPerSearch": 10,
  "maxItems": 10
}' |
apify call varnmill/google-jobs-scraper --silent --output-dataset

```

## MCP server setup

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