# Google Jobs Scraper — Apply Links, Salary & Filters (`diopside/google-jobs`) Actor

Google Jobs postings with full description, parsed salary, schedule, job highlights and every apply link as a direct URL. Drop-in compatible with the leading Google Jobs actor. Date filter Google actually honours, multi-query fan-out, empty results reported not hidden.

- **URL**: https://apify.com/diopside/google-jobs.md
- **Developed by:** [DIOPSIDE AI](https://apify.com/diopside) (community)
- **Categories:** Jobs, Business, Agents
- **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 records

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, salary, filters

**Drop-in compatible with the leading Google Jobs actors — cheaper, and it never reports a silent
empty result.** Same input field names (`query`, `location`, `country`, `language`, `google_domain`,
`posted_within`, `num_results`, `include_lrad`, `lrad_value`, `max_delay`) and the same SerpApi-shaped
output field names (`title`, `company_name`, `via`, `description`, `extensions`,
`detected_extensions`, `job_highlights`, `apply_options`, `job_id`, `share_link`). Point your existing
input at this actor id and it runs unchanged; our extra fields are added alongside, never renamed.

Every job comes back complete in one pass: the **full description** (including the part Google
collapses behind "Show full description"), the **salary** parsed into min/max/currency/period, the
schedule, Google's extracted qualifications/responsibilities/benefits, and **every apply link as a
direct URL** — LinkedIn, Indeed, ZipRecruiter, the company's own careers page — not a Google redirect.

### Why this one

| | This actor | Typical incumbent |
|---|---|---|
| Price | **$0.002 per job** | $0.015 per job, or $0.15 per page of ten |
| Failed runs (store stats, 30 d) | — | up to **19.6%** on the most-used one |
| Empty result handling | retried on a fresh IP, then reported in the status message | silently returns nothing |
| Date filter | `tbs=qdr` — the parameter Google actually honours | `htichips`, which Google ignores |
| Description | full text | often the truncated snippet |
| Apply links | direct URLs + domains | Google redirect URLs |

Four things make the difference:

1. **The route.** Google's `/search` has required JavaScript since 2025 — a plain client gets a 90 KB
   "enable JavaScript" shell on *every* IP tier, datacenter and residential alike (we measured all of
   them). This actor uses Apify's Google SERP proxy, the one route that returns the rendered jobs
   page, and reads Google's own server-side markup. No browser, so a search takes about a second.
2. **Empty is not success.** Google sometimes answers 200 with zero job cards on one session and ten
   on the next. Each search gets up to three attempts on fresh IPs, and any search that still comes
   back empty is named in the run's status message instead of quietly shrinking your dataset.
3. **A layout change fails loudly.** Google rotates its CSS class names. Each field has a structural
   fallback (`data-encoded-docid`, `data-title`, the per-card `data-maindata` JSON), and if cards are
   present but unreadable the run fails rather than billing you for rows of `null`.
4. **The date filter is the one Google honours.** Google Jobs scrapers commonly send
   `htichips=date_posted:today`; Google accepts that parameter and ignores it — in our measurements a
   "today" search returned jobs 5, 8 and 19 days old. Google's own filter links use `tbs=qdr:d`, which
   returned ten jobs all under 24 hours old. This actor uses `tbs` — and then checks the ages of what
   actually came back, warning in the run status when more than 40% of dated jobs fall outside the
   window. (Google's own filter is occasionally loose at the edges: a `3days` search may include one
   older posting. You will see it in `posted_at`, which is exactly the point.)

### How many jobs you get

**Google's jobs vertical exposes ten jobs per query/filter combination — there is no page two.**
`start=10`, `num=100`, the `callback:550` "more jobs" endpoint and the job-detail continuation were
all measured returning either nothing or the same ten jobs; the highest-rated competing actor,
asked for three pages, also fetched one page and returned ten. Any actor advertising 100 results for
one query is fanning out behind the scenes — often billing you per "page processed" while it does.

So fan out explicitly, in one run, de-duplicated by Google's job id:

```json
{
  "queries": ["software engineer", "backend engineer", "data engineer"],
  "locations": ["New York, NY", "Austin, TX", "Remote"],
  "posted_within": "3days",
  "num_results": 90
}
```

9 searches → up to 90 jobs. `num_results` caps the run; `max_delay` paces it.

### Input

| Field | Type | Default | Notes |
|---|---|---|---|
| `query` | string | `Software Engineer` | What you would type into Google Jobs |
| `queries` | string\[] | — | Extra queries, run alongside `query` |
| `location` | string | `New York, NY` | Folded into the query text — Google Jobs ignores `uule` |
| `locations` | string\[] | — | Every query is run against every location |
| `country` | string | `us` | Google's `gl`. Google Jobs is not available in every country |
| `language` | string | — | Google's `hl`. Leave empty in non-English markets — forcing `en` can return zero jobs |
| `google_domain` | string | `google.com` | e.g. `google.co.uk`, `google.de` |
| `posted_within` | enum | `any` | `hour`, `today`, `3days`, `week`, `month` — applied with `tbs=qdr`, the filter Google honours |
| `employment_type` | enum | `any` | `FULLTIME`, `PARTTIME`, `CONTRACTOR`, `INTERN` — applied to the parsed schedule (Google's jobs vertical has no employment-type control); the run reports how many jobs were dropped |
| `remote_only` | boolean | `false` | Adds "remote" to the query, exactly as Google's own Remote chip does; results come back located "Anywhere" |
| `include_lrad` / `lrad_value` | boolean / string | `false` / `25` | Sent to Google as `lrad`; best-effort — Google's current jobs UI exposes no radius control |
| `num_results` | integer | `100` | Hard cap on records |
| `max_delay` | integer | `1` | Seconds between searches |
| `max_pagination` | integer | `0` | Accepted for compatibility, logged and ignored (see above) |
| `proxyConfiguration` | object | Google SERP proxy | Leave as is; no other tier reaches this page |

### Output

```json
{
  "title": "Software Engineer, Front End, Pomelli",
  "company_name": "Google",
  "location": "New York, NY",
  "via": "via Indeed",
  "via_source": "Indeed",
  "description": "Note: By applying to this position you will have an opportunity to share your preferred working location...",
  "extensions": ["2 days ago", "147K–210K a year", "Full-time"],
  "detected_extensions": {
    "posted_at": "2 days ago",
    "schedule_type": "Full-time",
    "salary": "147K–210K a year",
    "work_from_home": null,
    "qualifications": null
  },
  "job_highlights": [
    { "title": "Qualifications", "items": ["Bachelor's degree or equivalent practical experience", "2 years of experience with front-end and user interface development"] },
    { "title": "Responsibilities", "items": ["Write product or system development code"] }
  ],
  "apply_options": [
    { "title": "Indeed", "link": "https://www.indeed.com/viewjob?jk=902d27e60be7aa21", "domain": "indeed.com" },
    { "title": "LinkedIn", "link": "https://www.linkedin.com/jobs/view/4468856901", "domain": "linkedin.com" }
  ],
  "apply_domains": ["indeed.com", "linkedin.com"],
  "job_id": "uxZzDPHtdB_zAAAAAAAAAA==",
  "share_link": "https://www.google.com/search?ibp=htl;jobs&q=software+engineer&htidocid=uxZzDPHtdB_z...",
  "thumbnail": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcR7qIzv",
  "posted_at": "2026-09-17T07:01:12+00:00",
  "posted_at_relative": "2 days ago",
  "schedule_type": "Full-time",
  "salary": { "min": 147000, "max": 210000, "currency": null, "period": "year", "raw": "147K–210K a year" },
  "result_position": 1,
  "query": "Software Engineer",
  "search_location": "New York, NY",
  "country": "us",
  "language": "en",
  "google_domain": "google.com",
  "search_timestamp": "2026-09-19T07:01:12+00:00",
  "search_url": "http://www.google.com/search?q=Software+Engineer+in+New+York%2C+NY&udm=8&hl=en&gl=us"
}
```

Fields the incumbents do not give you: `via_source` (board name without the localized "via" prefix),
`posted_at` (the relative badge resolved to an absolute UTC timestamp, so records stay sortable across
runs), `salary` parsed into numbers, `apply_domains`, and `search_url` — the exact Google URL used,
filters included, so any record can be reproduced by hand.

Localized markets are first-class: German, French, Spanish, Italian, Portuguese and Dutch pages parse,
including their "vor 5 Tagen" / "hace 3 días" posting dates and comma-decimal salaries. When a locale
uses wording we cannot date, `posted_at` is `null` and the original text is still in
`posted_at_relative` — never a guess.

### Typical run sizes

| Input | Searches | Jobs | Time |
|---|---|---|---|
| One query, one location | 1 | 10 | ~5 s |
| One query, five locations | 5 | up to 50 | ~15 s |
| Three queries × three locations | 9 | up to 90 | ~25 s |

### Use cases

- **Job boards and aggregators** — fill a niche board from Google's aggregate of LinkedIn, Indeed,
  ZipRecruiter, Workday and company career sites, with direct apply links.
- **Recruiting intelligence** — track which companies are hiring which roles in which cities, daily,
  with `posted_within: "today"` (which really does return only jobs under 24 hours old).
- **Salary benchmarking** — `salary` is parsed into min/max/currency/period, ready to aggregate.
- **Talent-market monitoring** — run the same queries on a schedule and diff on `job_id`.

### Switching from another Google Jobs actor

Change the actor id. Your `query`, `location`, `country`, `language`, `google_domain`,
`posted_within`, `num_results`, `include_lrad`, `lrad_value` and `max_delay` fields are read as-is,
and `max_pagination` is accepted and ignored. The output keys you already parse
(`title`, `company_name`, `location`, `via`, `description`, `extensions`, `detected_extensions`,
`job_highlights`, `apply_options`, `job_id`, `share_link`) keep their names and shapes.

### Notes

- Public job postings only — no personal data, no login, nothing behind a paywall.
- Google Jobs is unavailable in some countries; a search there returns nothing, and the run says so
  in its status message rather than failing silently.
- Billing is one event per job record pushed, plus the Apify actor-start event.

# Actor input Schema

## `query` (type: `string`):

What you would type into Google Jobs, e.g. "software engineer". Same field name as the leading Google Jobs actor, so existing inputs work unchanged.

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

Run several queries in one run. Google serves ten jobs per query, so this (with Locations) is how you build volume. Combined with `query`; results are de-duplicated.

## `location` (type: `string`):

City, state or country, e.g. "New York, NY". Folded into the search the way a person types it — Google Jobs ignores the `uule` location parameter.

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

Every query is run against every location. Combined with `location`; results are de-duplicated.

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

Two-letter country for the search (Google's `gl`). Google Jobs returns nothing for some countries — it is not available everywhere.

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

Interface language (Google's `hl`). Leave empty to let Google pick the language that matches the country — forcing `en` in a non-English market can return zero jobs.

## `google_domain` (type: `string`):

Google domain to search, e.g. google.com, google.co.uk, google.de.

## `posted_within` (type: `string`):

Google's date-posted filter, applied the way Google's own UI applies it (tbs=qdr). Verified: a "today" search returns jobs all under 24 hours old. The run warns if Google ever stops honouring it, instead of quietly handing you stale results.

## `employment_type` (type: `string`):

Keep only jobs with this schedule. Google's jobs vertical no longer offers an employment-type filter, so this is applied to the parsed schedule of each job and the run reports how many were dropped. Jobs where Google shows no schedule are dropped when this is set.

## `remote_only` (type: `boolean`):

Remote jobs only. Google's own "Remote" chip is a link that adds "remote" to the query, so that is what this does — the results come back located "Anywhere".

## `include_lrad` (type: `boolean`):

Search within a radius of the location instead of the location itself.

## `lrad_value` (type: `string`):

Radius used when "Use a location radius" is on (miles in the US, kilometres elsewhere). Passed to Google as `lrad`; Google's current jobs UI exposes no radius control, so treat it as best-effort.

## `num_results` (type: `integer`):

Stop after this many job records. Google exposes ten jobs per query/filter combination, so a run can return at most 10 × queries × locations.

## `max_delay` (type: `integer`):

Pause between searches. 1 second is plenty; raise it for very large fan-outs.

## `max_pagination` (type: `integer`):

Accepted so inputs written for other Google Jobs actors keep working. Google's jobs vertical serves one page per search — `start=10`, `num=100` and the async "more jobs" endpoint all return nothing — so this value is logged and ignored. Use `queries`/`locations` for volume.

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

Leave as is. Google's /search requires JavaScript for every ordinary client and IP tier; Apify's Google SERP proxy group is the only route that returns the rendered jobs page.

## Actor input object example

```json
{
  "query": "Software Engineer",
  "location": "New York, NY",
  "country": "us",
  "google_domain": "google.com",
  "posted_within": "any",
  "employment_type": "any",
  "remote_only": false,
  "include_lrad": false,
  "lrad_value": "25",
  "num_results": 100,
  "max_delay": 1,
  "max_pagination": 0,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "GOOGLE_SERP"
    ]
  }
}
```

# Actor output Schema

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

All job records. Append ?format=csv for CSV.

## `datasetUrl` (type: `string`):

The default dataset.

# 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 = {
    "query": "Software Engineer",
    "location": "New York, NY",
    "country": "us",
    "google_domain": "google.com",
    "num_results": 100,
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "GOOGLE_SERP"
        ]
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("diopside/google-jobs").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 = {
    "query": "Software Engineer",
    "location": "New York, NY",
    "country": "us",
    "google_domain": "google.com",
    "num_results": 100,
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["GOOGLE_SERP"],
    },
}

# Run the Actor and wait for it to finish
run = client.actor("diopside/google-jobs").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 '{
  "query": "Software Engineer",
  "location": "New York, NY",
  "country": "us",
  "google_domain": "google.com",
  "num_results": 100,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "GOOGLE_SERP"
    ]
  }
}' |
apify call diopside/google-jobs --silent --output-dataset

```

## MCP server setup

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

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/S6M2RpKB0H7v3jdMf/builds/6fCRFcyPpWnw42U8G/openapi.json
