# Google Jobs Scraper (`krammer/google-jobs-scraper`) Actor

Search Google Jobs by keyword, location and country. Get structured job postings with parsed salary, posted date, job type and work mode. Pay per results page; empty searches are free.

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

## Pricing

from $28.00 / 1,000 results pages

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

Search Google Jobs results by keyword, location and country, and get clean, structured job postings in an Apify dataset. You pay per Google results page fetched: $0.028 to $0.09 per results page (up to 10 jobs), depending on your Apify plan. Searches that return nothing are free.

### What it does

Give it one or more search queries (and optionally locations). It runs every query and location pair as a separate Google Jobs search and delivers the postings as dataset items.

- Searches Google Jobs results for 4 countries: United States, United Kingdom, India, Germany.
- Returns title, company, location, full description, highlights, benefits, apply links and the raw salary, job type and posted text.
- Adds structured fields parsed from the text: salary min/max/currency/period, posted date, employment types and work mode (remote or hybrid).
- Filters by posting date and employment type, and can add the word "remote" to every search.
- Removes duplicate postings inside one run and skips postings that have no title or company.
- Charges once per Google results page (up to 10 jobs); searches with no results are free.

### Use cases

- **Salary research.** Search one role in several cities and compare `salaryMin`, `salaryMax`, `salaryCurrency` and `salaryPeriod`. Only postings where Google shows a salary have these fields.
- **Find companies that are hiring.** Search a role and a city, then group the results by `company`. Recruiters and sales teams can use this list to see who is hiring for what, and where.
- **Daily job alerts.** Run the Actor on an Apify schedule with `datePosted` set to `7d`, and keep the `job_id` values you have already seen so you can drop them on your side (runs do not share duplicate memory). `7d` plus your own `job_id` list gives you only new jobs and keeps more results per page than `24h`: Google returns its most relevant jobs first, and in a busy search often only a few of them are from the last 24 hours.
- **Daily job hunt with an AI assistant.** The [examples repo](https://github.com/krammer0/google-jobs-scraper-examples?utm_source=apify\&utm_medium=readme) has Python scripts and a Claude Code skill that scores new jobs against your resume and lists the ones to apply to. You still apply yourself.
- **Feed a job board or a spreadsheet.** Every item has the same fields, so you can export the dataset or read it with the API and load it into your own database.
- **Give an AI agent live job data.** Agents can call the Actor through the Apify API or the Apify MCP server. See [Use with AI agents and the API](#use-with-ai-agents-and-the-api).

### How to get started

1. Click **Try for free** (or **Start**) on this page. You need an Apify account; the Free plan works.
2. Enter one or more search queries, for example `software engineer`.
3. Optional: add locations, choose a country, and set `datePosted`, `jobTypes` or `maxResultsPerSearch`.
4. Click **Start**. A search for 100 jobs usually finishes in less than a minute.
5. Open the **Output** tab to see the jobs. Export them as JSON, CSV or Excel, or read them with the API.
6. To repeat the search automatically, create a schedule for the Actor in Apify Console.

### Input

| Field | Type | Required | Default | Description |
|---|---|---|---|---|
| `queries` | array of strings | yes | `["software engineer"]` in the Console | 1 to 20 search queries. Each query is searched separately with every location. |
| `locations` | array of strings | no | `[]` | 0 to 20 cities or regions, for example `"New York, NY"`. Leave empty to search the whole country. |
| `country` | string | no | `us` | One of `us`, `gb`, `in`, `de`. |
| `language` | string | no | empty (chosen from the country) | One of `en`, `de`, `hi`, or empty. Empty means `en` for `us`, `gb`, `in` and `de` for `de`. |
| `datePosted` | string | no | `any` | One of `any`, `24h`, `3d`, `7d`, `30d`. Jobs with no posting time are kept. |
| `maxResultsPerSearch` | integer | no | `100` | 1 to 200. Upper limit of delivered jobs for each query and location pair. |
| `jobTypes` | array of strings | no | `[]` | Any of `FULLTIME`, `PARTTIME`, `CONTRACTOR`, `INTERN`. Empty means no filter. |
| `remoteOnly` | boolean | no | `false` | When true, the word "remote" is added to the text of every search. |

The number of searches is the number of queries times the number of locations (at least 1). It must not exceed 100, or the run fails at start with a message that says so.

Example input:

```json
{
  "queries": ["software engineer", "data analyst"],
  "locations": ["New York, NY", "Austin, TX"],
  "country": "us",
  "datePosted": "7d",
  "jobTypes": ["FULLTIME"],
  "maxResultsPerSearch": 50
}
```

### Output

Each dataset item is one job posting.

| Field | Type | Description |
|---|---|---|
| `job_id` | string | Google's identifier of the posting. Used to remove duplicates inside a run. |
| `title` | string | Job title. |
| `company` | string | Company name. |
| `location` | string or null | Location text as shown by Google. |
| `description` | string or null | Full description text. |
| `highlights` | array or null | Sections such as qualifications and responsibilities: `[{ "title", "items": [...] }]`. |
| `apply_options` | array or null | Places to apply: `[{ "title", "link" }]`. |
| `salary` | string or null | Salary text as shown by Google. |
| `benefits` | array of strings or null | Benefit labels, when Google shows them. |
| `job_type` | string or null | Employment type text as shown by Google. |
| `posted` | string or null | Posted text as shown by Google, for example "3 days ago". |
| `page` | integer or null | Result page the posting was found on. |
| `searchQuery` | string | The query that found the posting. |
| `searchLocation` | string or null | The location that found the posting, or null if none was set. |
| `country` | string | The country code used for the search. |
| `salaryMin`, `salaryMax` | number or null | Salary range parsed from `salary`. Same value when there is one number. |
| `salaryCurrency` | string or null | ISO 4217 code (`USD`, `GBP`, `EUR`, `INR`). Null when the currency is unclear. |
| `salaryPeriod` | string or null | `hour`, `day`, `week`, `month` or `year`. |
| `postedDate` | string or null | `YYYY-MM-DD` (UTC): run start time minus the relative time in `posted`. |
| `postedHoursAgo` | integer or null | The same relative time in hours (a month counts as 30 days). |
| `jobTypes` | array or null | Subset of `FULLTIME`, `PARTTIME`, `CONTRACTOR`, `INTERN`. |
| `workMode` | string or null | `remote` or `hybrid` when the title or location says so; otherwise null (null does not mean on-site). |

A field is null when Google does not show it. The structured fields are parsed from English and German text only; for other text they are null.

Example item (abridged, company name replaced):

```json
{
  "job_id": "RtLv7vlPTbn2E40SAAAAAA==",
  "title": "Staff Software Engineer - Card Technology",
  "company": "Example Corp",
  "location": "New York, NY (+3 others)",
  "description": "Example Corp is hiring a Staff Software Engineer for its card technology team. ...",
  "highlights": [{ "title": "Qualifications", "items": ["8+ years of software engineering experience"] }],
  "apply_options": [{ "title": "Example Corp Careers", "link": "https://careers.example.com/jobs/12345" }],
  "salary": "$150K–$200K a year",
  "benefits": ["Health insurance"],
  "job_type": "Full-time",
  "posted": "3 days ago",
  "page": 1,
  "searchQuery": "software engineer",
  "searchLocation": "New York, NY",
  "country": "us",
  "salaryMin": 150000,
  "salaryMax": 200000,
  "salaryCurrency": "USD",
  "salaryPeriod": "year",
  "postedDate": "2026-11-05",
  "postedHoursAgo": 72,
  "jobTypes": ["FULLTIME"],
  "workMode": null
}
```

### Supported countries

| Code | Country | Languages | Default language |
|---|---|---|---|
| `us` | United States | `en` | `en` |
| `gb` | United Kingdom | `en` | `en` |
| `in` | India | `en`, `hi` | `en` |
| `de` | Germany | `de`, `en` | `de` |

A language that is not in the list for the chosen country is still searched, with a warning in the log that results may be sparse. A country outside this list fails the run at start.

### Use with AI agents and the API

Python, with the `apify-client` package:

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("krammer/google-jobs-scraper").call(run_input={
    "queries": ["machine learning engineer"],
    "locations": ["Berlin"],
    "country": "de",
    "language": "en",
    "datePosted": "7d",
    "maxResultsPerSearch": 30,
})
for job in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(job["title"], "|", job["company"], "|", job["salaryMin"], job["salaryCurrency"])
```

curl (starts the run, waits for it to finish and returns the dataset items; for large inputs use the asynchronous run endpoint instead):

```bash
curl -X POST "https://api.apify.com/v2/acts/krammer~google-jobs-scraper/run-sync-get-dataset-items" \
  -H "Authorization: Bearer <YOUR_APIFY_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{"queries": ["registered nurse"], "locations": ["Chicago, IL"], "maxResultsPerSearch": 20}'
```

Every input field has a description written for AI agents, so tools that read the input schema (for example the Apify MCP server) can read what each field is for.

### Limits

- Each search delivers at most `maxResultsPerSearch` jobs (1 to 200, default 100).
- At most 100 searches per run (queries x locations), at most 20 queries and 20 locations.
- The number of results depends on what Google shows for the query. A search can return fewer jobs than the maximum.
- If you set a maximum total charge for the run, the run stops when it is reached and keeps the jobs already delivered.

### FAQ

**Why did I get fewer jobs than `maxResultsPerSearch`?**
Google may have fewer postings for that query. Also, the `datePosted` and `jobTypes` filters are applied to the results Google returns; the Actor does not fetch extra results to make up for removed ones. Duplicates and postings without a title or company are removed too.

**Am I charged when a search returns nothing?**
No. A results page is charged only if it contains at least one job posting with a title and company. If Google returns no results, nothing is charged. Pages whose jobs are all removed by your filters, or were all delivered by an earlier search in the same run, are still charged, because those pages were fetched. The log explains why a search delivered nothing.

**What counts as a duplicate?**
Postings with the same `job_id` inside one run. The first one is kept and later ones are dropped from the dataset. Billing is per results page, so the page that contained a duplicate is still charged. Runs do not share this memory, so a later run delivers the same posting again.

**How do the filters work?**
`datePosted` keeps jobs whose `postedHoursAgo` is within 24 hours, 3 days, 7 days or 30 days. `jobTypes` keeps jobs whose `jobTypes` has at least one of the types you list. If Google does not show the posting time of a job, the job is kept (`postedHoursAgo` is `null`). If Google does not show the type of a job, the job is removed when `jobTypes` is set.

**Does `remoteOnly` remove on-site jobs?**
No. It adds the word "remote" to the search text, so the search asks for remote jobs, but the results are not filtered. Use the `workMode` field of the output if you need to check each job.

### Billing and failures

- You are charged once for each Google results page fetched that contains at least one valid job posting: $0.028 to $0.09 per results page (up to 10 jobs), depending on your Apify plan (Free plan $0.09, Starter $0.036, Scale $0.032, Business $0.028).
- You are not charged for searches with no results, pages with only invalid jobs (missing title or company), or searches that failed.
- Pages are charged even if all their jobs were removed by your filters or were duplicates of an earlier search in the run.
- If some searches fail, the run still finishes as SUCCEEDED. The status message says so, for example "Partially completed: 12 jobs delivered; 1 of 3 searches failed (...)". Result pages that were fetched are charged; failed searches are not. If no search could be fetched at all, the run is FAILED and nothing is charged.
- The `RUN_SUMMARY` record in the default key-value store lists the outcome, the number of jobs, the failure reasons and the request ids of failed searches.
- If the platform migrates your run to another server, the last batch of jobs may be sent again once. This is a known window.
- Free Apify plans have a monthly allowance for this Actor. The allowance counts every result retrieved for your searches, including duplicates and jobs removed by your filters, because those results are still fetched. When it is used up, the run stops, nothing more is charged, and the status message shows the amount used. Paid Apify plans are not limited. The allowance resets at the start of each month (UTC).

# Actor input Schema

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

Use this field when the user names a job title, skill or keyword to search for. Required, 1 to 20 queries, each 1 to 200 characters. Every query is searched separately with every location.

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

Use this field when the user names a city or region. Leave empty to search the whole country set in `country`. Each location is searched separately with every query. 0 to 20 locations.

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

Use this field when the user names a country to search in. Supported: us, gb, in, de. Default is us.

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

Use this field when the user wants results in a specific language. Supported: en, de, hi. Empty (default) picks the language from the country: us, gb and in use en; de uses de.

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

Use this field when the user wants only recent postings. Values: any (default), 24h, 3d, 7d, 30d. Filtering is applied to the results Google returns, so fewer than maxResultsPerSearch results may be delivered. Jobs whose posting time is unknown are kept. For daily alerts, 7d plus your own list of seen job_id values usually gives more new jobs per page than 24h.

## `maxResultsPerSearch` (type: `integer`):

Use this field when the user wants to limit how many jobs each search returns. Range 1 to 200, default 100. The total number of searches (queries x locations) must not exceed 100.

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

Use this field when the user wants specific employment types. Values: FULLTIME, PARTTIME, CONTRACTOR, INTERN. Empty (default) means no filter. Filtering is applied to the results Google returns, so fewer than maxResultsPerSearch results may be delivered. Jobs whose job type is unknown are excluded when this filter is set.

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

Use this field when the user wants remote jobs only. Default is false. When true, the word remote is added to each search text.

## Actor input object example

```json
{
  "queries": [
    "software engineer"
  ],
  "locations": [],
  "country": "us",
  "language": "",
  "datePosted": "any",
  "maxResultsPerSearch": 100,
  "jobTypes": [],
  "remoteOnly": false
}
```

# Actor output Schema

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

One item per job posting, deduplicated across searches.

## `runSummary` (type: `string`):

Run outcome, per-search status and pages charged.

# 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"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("krammer/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"] }

# Run the Actor and wait for it to finish
run = client.actor("krammer/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"
  ]
}' |
apify call krammer/google-jobs-scraper --silent --output-dataset

```

## MCP server setup

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