# Google Jobs Scraper (Cheap, No Browser) (`subimpact/google-jobs-scraper`) Actor

Scrape Google Jobs listings by query and country: title, company, location, source (LinkedIn, Indeed...), posted date, salary, job type, description and apply links. No browser. $1.00 per 1,000 jobs plus Google SERP proxy usage.

- **URL**: https://apify.com/subimpact/google-jobs-scraper.md
- **Developed by:** [subimpact](https://apify.com/subimpact) (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 and usage. You are charged both the fixed price for specific events and for Apify platform usage.

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 (Cheap, No Browser)

Search Google Jobs by query and country and get one clean row per job: title, company, location, source, posting age, salary, employment type, description and apply links. Pure HTTP through Apify's Google SERP proxy. No browser, no Google API key, no login.

### What it does

- Runs one Google Jobs search per query (`google.com/search?q=...&udm=8`, Google's Jobs view).
- Parses both copies of each listing that Google ships (the list card and the hidden detail panel) and merges them by Google's job id, so every job becomes exactly one dataset row.
- Returns the description text, the part that is collapsed behind "..." included, plus every "Apply" button Google shows.
- Pushes rows as they are ready. You are charged only after a row is written to the dataset.
- Writes a status row instead of failing silently when a query has no jobs or Google returns an unexpected page. Status rows are free.

### Input

The prefilled input in the Apify Console is also a working API input:

```json
{
  "queries": ["software engineer jobs kuala lumpur"],
  "countryCode": "my",
  "maxJobsPerQuery": 10
}
```

An empty input `{}` also works: it searches `software engineer jobs` in the `us` Google, with English, and delivers about 10 jobs.

| Input | Type | Default | What it does |
| --- | --- | --- | --- |
| `queries` | array of strings | `["software engineer jobs"]` | Google Jobs searches, for example `software engineer jobs kuala lumpur` or `barista jobs singapore`. Up to 100 per run. Duplicate queries are removed. |
| `countryCode` | string | `us` | Two-letter Google country code (`gl`): `us`, `my`, `sg`, `gb`, `au`, `ca`, `in` and so on. `uk` is accepted and mapped to `gb`. |
| `languageCode` | string | `en` | Google interface language (`hl`): `en`, `ms`, `de`, or a regional code such as `en-MY`. |
| `location` | string | empty | Free text appended to every query, for example `Kuala Lumpur` or `remote`. |
| `maxJobsPerQuery` | integer | `10` | Cap on delivered rows per query (1-10). Google returns about 10 listings per query and has no working page 2, so add more specific queries to collect more. |
| `maxItems` | integer | `100` | Global cap across all queries. This is the hard cost ceiling of the run. |
| `concurrency` | integer | `5` | Queries fetched in parallel, 1 to 10. |

### Output

One dataset row per job. Every row has the same fields:

| Field | Example | Notes |
| --- | --- | --- |
| `jobId` | `81WRPjdaGz_QX9mmAAAAAA` | Google's stable document id for the listing. |
| `jobTitle` | `Backend Software Engineer (Golang)` | As shown by Google. |
| `companyName` | `Technology Bucket (M) Sdn Bhd` | As shown by Google. |
| `location` | `Kuala Lumpur, Federal Territory of Kuala Lumpur` | Null when Google shows no location. |
| `via` | `Indeed` | Original job board or careers site. |
| `postedAgo` | `2 days ago` | Relative age exactly as Google shows it. |
| `postedAt` | `2026-10-22T12:00:00.000Z` | Estimate derived from `postedAgo` and the scrape time. Google only gives coarse text, so treat it as approximate. |
| `employmentType` | `Full-time` | Full-time, Part-time, Contractor and similar, when shown. |
| `salary` | `RM 8K–RM 12K a month` | Raw pay string exactly as shown. Null when no salary is shown. |
| `description` | `Roles & Responsibilities: ...` | Description text from the detail panel, including the collapsed remainder. |
| `applyLinks` | `[{"title": "Apply directly on Indeed", "url": "https://www.google.com/goto?url=..."}]` | Apply buttons as `{title, url}`. The URL is Google's redirect, which lands on the employer or job-board page. |
| `companyLogo` | `https://encrypted-tbn0.gstatic.com/images?...` | Null when Google shows a letter avatar instead of a logo. |
| `query` | `software engineer jobs kuala lumpur` | The query that produced the row. |
| `countryCode` | `my` | Google `gl` setting. |
| `languageCode` | `en` | Google `hl` setting. |
| `position` | `1` | Rank on Google's results page for that query, starting at 1. |
| `scrapedAt` | `2026-10-24T12:00:00.000Z` | When the page was fetched. |
| `status` | `ok` | `ok` for a job, `empty` or `error` for a status row. |
| `errorMessage` | `null` | Why a status row is not `ok`. |

Example row, with the description shortened here and the Google redirect token replaced by `...` in the link:

```json
{
  "jobId": "81WRPjdaGz_QX9mmAAAAAA",
  "jobTitle": "Backend Software Engineer (Golang)",
  "companyName": "Technology Bucket (M) Sdn Bhd",
  "location": "Kuala Lumpur, Federal Territory of Kuala Lumpur",
  "via": "Indeed",
  "postedAgo": "2 days ago",
  "postedAt": "2026-10-22T12:00:00.000Z",
  "employmentType": "Full-time",
  "salary": "RM 8K–RM 12K a month",
  "description": "Roles & Responsibilities:\n• Design, develop, and maintain backend services and APIs using Go (Golang).\n• Build scalable, distributed, and event-driven systems ...",
  "applyLinks": [
    {
      "title": "Apply directly on Indeed",
      "url": "https://www.google.com/goto?url=..."
    },
    {
      "title": "Apply on LinkedIn",
      "url": "https://www.google.com/goto?url=..."
    }
  ],
  "companyLogo": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRDOj5L_0R_M-vGNHZeOqUiv83LhTU7qeoZbM3N&s=0",
  "query": "software engineer jobs kuala lumpur",
  "countryCode": "my",
  "languageCode": "en",
  "position": 1,
  "scrapedAt": "2026-10-24T12:00:00.000Z",
  "status": "ok",
  "errorMessage": null
}
```

A query with no jobs gets one status row (`status: "empty"`) and a failed query gets one status row (`status: "error"`); job fields are null on those rows and they are never charged.

### Pricing

- **$0.001 per job row**, which is **$1.00 per 1,000 jobs**. The `job` event is charged once, right after the row is successfully pushed to the dataset.
- **Error and empty status rows are free.** So are rows that fail to push.
- **Apify platform usage and Google SERP proxy usage are billed to you separately** by Apify. The Google SERP proxy costs about **$0.0025 per query page**. At about 10 jobs per page that is about **$0.25 per 1,000 jobs**.
- All in, about **$1.25 per 1,000 jobs** including estimated proxy usage.

A default run (`{}`) is one query and about 10 jobs: about $0.01 of job charges plus about $0.0025 of proxy usage, well under $0.05. `maxItems` is the hard ceiling on job rows; set it to the number of jobs you are willing to pay for.

### How this compares

Free-plan list prices checked 24 October 2026. Competitor platform usage is extra (see each listing) and is not included below.

| Actor | Price on the Free plan | What users saw in the last 30 days |
| --- | --- | --- |
| **This actor** | **$1.00 per 1,000 jobs**, plus about $0.25 per 1,000 jobs of Google SERP proxy usage | New actor |
| johnvc/Google-Jobs-Scraper | $0.09 per results page of up to 10 jobs, about $9 per 1,000 jobs | 490 users, 91.5% run success |
| epctex/google-jobs-scraper | see listing | 0.1% run success, rating 2.9 |
| gio21 Google Jobs actor | see listing | 87% run success, rating 2.4 |

At the list prices above, 1,000 jobs cost about $1.25 here including the estimated proxy usage, against about $9 with johnvc before their platform usage.

### Limits and things to know

- **First page only.** Google's Jobs view returns about 10 listings per query. The usual `start=10` page parameter returns an empty Jobs page, and `num=20` or `num=100` are ignored (measured 24 October 2026). Each query therefore delivers up to about 10 rows. Add more queries to collect more jobs.
- **No date-posted filter.** Google's `htichips=date_posted:today` and `:week` parameters were measured on 24 October 2026 and did not change the results, so this actor does not offer a date filter rather than pretend one works.
- **The same job found by two queries is delivered once.** Deduplication uses Google's job id, falling back to title plus company plus location.
- **Apply links are Google redirects.** They work in a browser but carry an expiring token, so follow them when you use them rather than storing them forever.
- **`postedAt` is an estimate.** Google exposes text such as "3 days ago", not an exact timestamp. Months are counted as 30 days and years as 365 days.
- **Google can change its markup or rate-limit a session.** The actor checks the response shape before selling anything: an unexpected page becomes a free error row, not a charge. CAPTCHA and "sorry" pages are detected and retried with a fresh proxy session.
- **Hard limits:** 100 queries per run, 500 rows accepted per `maxJobsPerQuery`, 1,000 rows per `maxItems`, 10 concurrent queries, 45 second request timeout, 3 retries with a fresh proxy session, and no retry on HTTP 404.
- **The Google SERP proxy is required.** The actor runs on Apify Proxy with the `GOOGLE_SERP` group. If that proxy is unavailable, the run fails with a clear message. It never falls back to a direct connection.

### How it works

The actor sends a plain HTTP GET to `http://www.google.com/search?q=...&udm=8` through Apify's Google SERP proxy. Google returns a normal results page: a list of about 10 job cards plus a hidden detail panel for each card. The parser takes the job id from the card's stable attributes, pairs the card with its detail panel, and reads the title, company, location, source, posting age, salary, employment type, description and apply buttons. It does not use a browser, so it needs no browser image and the default memory of 256 MB is enough.

### Responsible use

This actor collects publicly visible job listings for legitimate research, recruiting, market analysis and job-board aggregation. Respect Google's Terms of Service and the laws that apply to you, keep request volumes reasonable, and do not use the data to harass candidates or to republish listings in a way that infringes the rights of the employer or job board. Job ads go stale: confirm details with the employer before acting on them.

# Actor input Schema

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

One or more Google Jobs searches, e.g. `software engineer jobs kuala lumpur` or `barista jobs singapore`. Each query returns Google's first Jobs page (about 10 listings). Add more queries to cover more jobs. Maximum 100 queries per run.

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

Two-letter country code used for the Google country setting. Examples: us, my, sg, gb, au, ca, in. `uk` is accepted and mapped to gb.

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

Google interface language. Use a two-letter code like en, ms, de, or a regional code like en-MY.

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

Free text appended to every query, e.g. `Kuala Lumpur` or `remote`. Leave empty to search the query exactly as written. The country code still controls the Google country setting.

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

Google shows about 10 jobs per search and has no working page 2 (verified October 2026). To collect more, add more specific queries (role + city, or different locations).

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

Global cost ceiling: the run stops after this many job rows, across all queries. At $0.001 per job, 100 rows cost $0.10 plus proxy usage. The default single query returns about 10 jobs, so a default run costs about $0.01 plus proxy usage. Hard cap 1,000.

## `concurrency` (type: `integer`):

How many queries are fetched in parallel. Higher is faster but uses more proxy sessions at once. Default 5.

## Actor input object example

```json
{
  "queries": [
    "software engineer jobs kuala lumpur"
  ],
  "countryCode": "my",
  "languageCode": "en",
  "maxJobsPerQuery": 10,
  "maxItems": 100,
  "concurrency": 5
}
```

# Actor output Schema

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

Dataset items: jobTitle, companyName, location, via, postedAgo, postedAt, employmentType, salary, description, applyLinks, companyLogo, query, position, scrapedAt and status.

# 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 jobs kuala lumpur"
    ],
    "countryCode": "my",
    "languageCode": "en",
    "maxJobsPerQuery": 10,
    "maxItems": 100,
    "concurrency": 5
};

// Run the Actor and wait for it to finish
const run = await client.actor("subimpact/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 jobs kuala lumpur"],
    "countryCode": "my",
    "languageCode": "en",
    "maxJobsPerQuery": 10,
    "maxItems": 100,
    "concurrency": 5,
}

# Run the Actor and wait for it to finish
run = client.actor("subimpact/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 jobs kuala lumpur"
  ],
  "countryCode": "my",
  "languageCode": "en",
  "maxJobsPerQuery": 10,
  "maxItems": 100,
  "concurrency": 5
}' |
apify call subimpact/google-jobs-scraper --silent --output-dataset

```

## MCP server setup

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