# Google Jobs Scraper \[$2.5/1k💰] Apply Links, Salary & Emails (`memo23/google-jobs-scraper`) Actor

Google Jobs scraper for the feed Google merges from LinkedIn, Indeed, Glassdoor, ZipRecruiter and career sites. Every apply link, salary, full description, posted date. 99% run success, $2.50 per 1k jobs, no per-page fees. New-jobs-only mode for daily runs, optional employer emails.

- **URL**: https://apify.com/memo23/google-jobs-scraper.md
- **Developed by:** [Muhamed Didovic](https://apify.com/memo23) (community)
- **Categories:** Jobs, Lead generation, AI
- **Features:** Limited permissions
- **Stats:** 193 total users, 54 monthly users, 91.9% runs succeeded, 0 bookmarks
- **User rating**: 5.00 out of 5 stars

## Pricing

from $2.50 / 1,000 job results

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 — Multi-Board Apply Links, Salary & Employer Emails

Scrape **Google's jobs vertical** — the aggregated feed Google builds from LinkedIn, Indeed, Glassdoor, ZipRecruiter, Monster, Dice, Built In, Workday, Greenhouse, Lever and thousands of company career sites. One search surfaces postings from all of them at once.

Every row carries the job's title, company, location, source board, posted date, salary, schedule, benefits — and **every syndicated apply link Google knows about**, not just one.

**$2.50 per 1,000 jobs, billed per job returned.** No per-page or upfront charges, so a search that finds nothing costs only the $0.005 start fee. Over the last 30 days, 99% of public runs succeeded (424 of 428).

![How the Google Jobs Scraper works](https://raw.githubusercontent.com/muhamed-didovic/muhamed-didovic.github.io/main/assets/how-it-works-google-jobs.png)

***

### Why use this scraper

| | |
|---|---|
| **Every apply route, not one** | Google lists the same job on LinkedIn *and* Indeed *and* the employer's own ATS. Most scrapers return a single link. This one returns all 4–8, each labelled with its board. |
| **Full descriptions, structured** | Not just a teaser: the complete job description (median ~3,500 characters) plus Google's own qualification, responsibility and benefit bullets as clean arrays. |
| **New-jobs-only monitoring** | Turn on one checkbox and the actor remembers what it already sent you. Schedule it daily and every run returns *only* the postings that are new — no re-reading yesterday's list, and no paying for duplicate rows. |
| **Employer contact emails** | Optional: resolve the hiring company's own website and pull a public contact address, turning a job feed into a lead list. |
| **Built for breadth** | On top of paging, your keywords × locations expand into a full search matrix run in parallel, then de-duplicated. |
| **One consistent row shape** | Identical field names to the rest of this jobs-scraper family, so one column mapping works across all of them. |
| **Fails loudly, not silently** | If Google cannot be reached, the run ends FAILED with the reason in the status message. A run that ends SUCCEEDED with zero rows means Google itself had no jobs for that search. |

***

### Coming from another Google Jobs scraper?

These are the problems users report most often on Google Jobs actors in the Store, and how this one handles each.

| Common complaint | What this actor does |
|---|---|
| "It doesn't work" / runs return nothing | 99% of public runs succeeded over the last 30 days. Google is fetched through several scraping services with automatic failover, and a run that cannot reach Google ends FAILED with the reason, so you never pay for a silent empty run. |
| Charged for a run that returned zero jobs | You pay per job returned ($0.0025 each). There is no per-page or upfront charge. An empty search costs only the $0.005 start fee. |
| Zero results when only a country is set | Leave `locations` empty and the actor searches the whole selected country (for example `nurse jobs in United Kingdom`). |
| Lots of duplicates on daily scheduled runs | `onlyNewJobs` remembers every job already sent (45 days) and returns only new ones. Add `datePosted: "today"` or `postedWithinHours: 24` for a strict last-24-hours feed. |
| Only one apply link per job | `applyLinks` lists every apply route Google shows (LinkedIn, Indeed, the employer's ATS and more), each labelled with its board. |
| Output tab shows an error or no columns | The dataset has a ready-made "Jobs" table view, and every row has the same keys. |

***

### Overview

Google's jobs vertical (the "Jobs" tab, internally `udm=8`) is the largest job aggregator most people never scrape directly — because it will not render without JavaScript and blocks datacenter traffic outright. This actor handles that for you and returns clean, structured rows.

Give it a list of **keywords** and a list of **locations**. It builds one Google search per combination, fetches them concurrently, parses each result card, merges everything, drops duplicates, and writes one row per job.

***

### Supported inputs

| Input | Example | Notes |
|---|---|---|
| **Keywords** | `software engineer`, `registered nurse` | Job titles, skills, or free text |
| **Locations** | `New York`, `Chicago`, `London` | Leave empty to search the whole country — see the FAQ |
| **Country** | `us`, `gb`, `ca`, `au`, `in` | Which Google market to search |
| **Google Jobs URLs** | `https://www.google.com/search?q=nurse+jobs+in+chicago&udm=8` | Paste directly; legacy `ibp=htl;jobs` links are upgraded automatically |

***

### Use cases

- **Job boards & aggregators** — fill a niche board from a source that already merges every major board.
- **Recruitment agencies** — monitor who is hiring for your specialty in your city, daily, and see which ATS each employer uses.
- **Sales & lead generation** — companies that are hiring are companies that are spending. Turn on email enrichment and every posting becomes a qualified lead with a contact address.
- **Market & salary research** — track posting volume, salary bands and remote share by role and metro over time.
- **Candidate tools** — power a job-alert product with a de-duplicated, multi-board feed.
- **HR & competitive intelligence** — watch a competitor's hiring: which roles, which locations, which salary bands.

***

### How it works

1. **Expand** — your keywords × locations become a matrix of Google searches.
2. **Fetch** — each search is rendered server-side through a built-in unblocking service, from a residential exit node in your chosen country. Nothing to configure.
3. **Parse** — each result card yields title, company, location, source board, posted date, salary, schedule, benefits and qualifications.
4. **Attribute** — Google renders each job's apply links in its own container; the actor matches them 1:1 to the right job, so links never bleed between postings.
5. **De-duplicate** — the same job found by two different searches is emitted once.
6. **Filter** — recency, salary-only, remote-only and schedule filters are applied.
7. **Enrich** *(optional)* — resolve the employer's website and scrape a public contact email.
8. **Push** — one clean row per job.

***

### Input configuration

| Field | Type | Default | Description |
|---|---|---|---|
| `keywords` | array | `["software engineer"]` | Job titles / keywords to search |
| `locations` | array | `["New York", "San Francisco"]` | Cities or regions. Empty = the whole selected country |
| `country` | select | `us` | Google market (`gl`). See the EU note in the FAQ |
| `language` | string | `en` | Google interface language (`hl`) |
| `googleDomain` | string | — | e.g. `google.co.uk` |
| `startUrls` | array | — | Paste Google Jobs URLs directly |
| **Google-side filters** | | | *applied before Google's ~10-result cut* |
| `datePosted` | select | — | `today` / `3days` / `week` / `month` |
| `employmentTypes` | array | — | `FULLTIME` / `PARTTIME` / `CONTRACTOR` / `INTERN` — **also widens coverage** |
| `workFromHome` | boolean | `false` | Google's own remote filter |
| `radius` | integer | — | Miles around the location |
| **Client-side filters** | | | *applied to what Google returned* |
| `postedWithinHours` | integer | — | Keep only jobs posted in the last N hours |
| `salaryOnly` | boolean | `false` | Keep only jobs that display a salary |
| `remoteOnly` | boolean | `false` | Keep only remote-looking jobs |
| `scheduleTypes` | array | — | e.g. `Full-time`, `Contractor` |
| `companyFilter` | array | — | Only these employers |
| `excludeCompanies` | array | — | Drop these employers (e.g. staffing agencies) |
| `viaFilter` | array | — | Only jobs from these source boards |
| `onlyNewJobs` | boolean | `false` | Emit only postings not seen in previous runs |
| `enrichEmails` | boolean | `false` | Find employer contact emails |
| `qualifyByPayment` | boolean | `false` | Flag employers that take payments online |
| `maxPagesPerQuery` | integer | `3` | Extra "load more" pages per search (1–10). Google often returns no extra page, see below |
| `maxItems` | integer | `1000` | Stop after this many jobs |
| `maxConcurrency` | integer | `10` | Parallel Google searches |

#### Getting more than 10 jobs per search

Google's jobs tab shows ~10 results per search. The reliable way to get more is the search matrix:

1. **The search matrix**: `keywords × locations × employmentTypes` each add distinct searches, run in parallel and de-duplicated. Employment types are especially effective, since each type returns a largely different result set (`FULLTIME` + `CONTRACTOR` doubled results with zero overlap in testing). 3 keywords × 4 cities ≈ **120 jobs**; add two employment types and it is ≈ 240.
2. **Pages per search** (`maxPagesPerQuery`): the actor also tries Google's "load more" cursor for further pages. Google currently answers it with an empty page, so plan on ~10 jobs per search and treat extra pages as a bonus. Empty extra pages cost nothing.

#### Example input

```json
{
  "keywords": ["software engineer", "data analyst"],
  "locations": ["New York", "Chicago", "Austin"],
  "country": "us",
  "postedWithinHours": 24,
  "onlyNewJobs": true,
  "maxItems": 500
}
```

That is 2 × 3 = 6 searches, filtered to the last 24 hours, returning only jobs no previous run has seen.

***

### Output overview

One row per job. Fields the source does not provide are `null` (or `[]`) rather than missing, so your columns never shift.

### Output sample

```json
{
  "type": "job",
  "source": "google-jobs",
  "jobId": "lead software engineer java aws|jpmorganchase|new york ny",
  "title": "Lead Software Engineer: Java/AWS",
  "companyName": "JPMorganChase",
  "location": "New York, NY",
  "remote": false,
  "via": "LinkedIn",
  "postedRaw": "12 hours ago",
  "postedDate": "2026-07-25T09:26:48.375Z",
  "salary": {
    "currency": "USD",
    "min": 152000,
    "max": 215000,
    "raw": "152K–215K a year",
    "unit": "YEAR"
  },
  "salaryRaw": "152K–215K a year",
  "contractType": "Full-time",
  "employmentTypes": ["Full-time"],
  "benefits": ["Health insurance", "Dental insurance"],
  "qualifications": [],
  "applyType": "external",
  "applyUrl": "https://www.linkedin.com/jobs/view/lead-software-engineer-java-aws-at-jpmorganchase-4445316605?utm_campaign=google_jobs_apply",
  "applyLinks": [
    { "board": "LinkedIn",       "url": "https://www.linkedin.com/jobs/view/..." },
    { "board": "JPMorgan Chase", "url": "https://jpmc.fa.oraclecloud.com/hcmUI/CandidateExperience/..." },
    { "board": "Indeed",         "url": "https://www.indeed.com/viewjob?jk=88e34225b97d1f3a..." },
    { "board": "Built In NYC",   "url": "https://www.builtinnyc.com/job/lead-software-engineer-java-aws/..." }
  ],
  "applyLinkCount": 8,
  "searchKeyword": "software engineer",
  "searchLocation": "New York",
  "searchCountry": "us",
  "scrapedAt": "2026-07-25T21:26:52.983Z"
}
```

### Key output fields

| Field | Type | Description |
|---|---|---|
| `title` | string | Job title |
| `companyName` | string | Hiring employer |
| `location` | string | As shown by Google, e.g. `New York, NY` |
| `remote` | boolean | Inferred from the title and location |
| `via` | string | The board Google syndicated this posting from |
| `postedDate` | ISO date | Absolute date derived from Google's "12 hours ago" |
| `postedRaw` | string | Google's original relative wording |
| `salary` | object | `{ currency, min, max, raw, unit }` |
| `contractType` | string | `Full-time`, `Contractor`, `Internship`, … |
| `description` | string | **Full job description text** — median ~3,500 characters |
| `qualifications` | array | Requirement bullets, as identified by Google from the original post |
| `responsibilities` | array | Duty bullets from the same source |
| `benefits` | array | e.g. `Health insurance`, `401(k)` |
| `applyLinks` | array | **Every** apply route, each with its `board` name |
| `applyUrl` | string | The primary apply link (the `via` board where possible) |
| `contactEmail` | string | Employer email — only with `enrichEmails` |
| `contactWebsite` | string | Employer website — only with `enrichEmails` |
| `searchKeyword` / `searchLocation` | string | Which of your searches produced this row |

***

### FAQ

**How many jobs will I get per search?**
About 10. Google's jobs tab returns ~10 jobs per search, and its "load more" pages currently come back empty for most searches. For volume, widen the matrix: 5 keywords × 6 cities ≈ 300 jobs, and each extra employment type adds a largely new set.

**Should I use the Google-side or the client-side filters?**
Prefer the Google-side ones (`datePosted`, `employmentTypes`, `workFromHome`, `radius`). Because Google only returns ~10 results, filtering *there* changes which jobs you receive; filtering afterwards only throws away rows you already paid for. Asking Google for "past 24 hours" returns 10 fresh jobs; asking for anything and then filtering to 24 hours might leave you 2.

**Do I have to provide a location?**
No. Google's jobs vertical answers a bare keyword with "there aren't any Jobs matches on this topic", so when `locations` is empty the actor searches the whole selected country instead (`nurse jobs in United Kingdom`). For more jobs, list several cities: each city is its own search with its own ~10-30 results.

**What does a run cost?**
$2.50 per 1,000 jobs ($0.0025 per job) plus a $0.005 start fee. Jobs removed by your filters, duplicates, and jobs already sent by `onlyNewJobs` are not billed. With `enrichEmails` on, each employer email that is found adds about $0.05; rows without an email cost nothing extra. Free Apify plans are capped at 100 jobs per run.

**Why do I get no results for Germany / France / Spain?**
Google has wound its jobs widget down across the EU, and those markets now return nothing at all. This is Google's change, not a fault in the actor — the log warns you when you pick an affected country. The US, UK, Canada, Australia and India all return rich results.

**Why is `salary` empty on many rows?**
Because Google only displays a salary when the source posting includes one — roughly 30% of the time in our testing. The `salaryOnly` filter keeps just those rows if you need complete compensation data.

**Does the email enrichment find an address for every employer?**
No, and it is honest about that. It resolves the employer's website from the company name and reads its public contact pages. Large corporations (Amazon, Charles Schwab) usually publish no contact email, so `contactEmail` will be empty even though `contactWebsite` is filled. Hit rates are much better on small and mid-sized employers.

**How does "only new jobs" decide what is new?**
A job's identity is its title + company + location. Once emitted, it is remembered for 45 days in the actor's key-value store, then forgotten — so a genuine re-posting months later reaches you again. The first run returns everything; every run after that returns only what changed.

**Do I need a proxy?**
No. Google is fetched through a built-in unblocking service with no configuration. The proxy input is used only by the optional email-enrichment step.

***

### Pricing

| Event | When | Price |
|---|---|---|
| Job result | Each job written to the dataset | $0.0025 ($2.50 per 1,000) |
| Actor start | Once per run | $0.005 |
| Additional data | Only with `enrichEmails`: each employer email found | about $0.05 per email |

Pages, retries and empty searches are never billed. Set a maximum cost per run in Apify and the actor stops before going over it.

***

### Support

Found a bug or want a field added? Open an issue on the actor's **Issues** tab and I will take a look.

### Additional services

Need this customised — a specific country, extra fields, a private variant, or a different delivery format? Get in touch through the Apify Store profile.

### Explore more scrapers

This actor is part of a large family of job-board scrapers sharing the same output shape: **LinkedIn Jobs**, **Indeed**, **ZipRecruiter**, **Glassdoor**, **Dice**, **SimplyHired**, **Seek**, **StepStone**, **Reed**, **TotalJobs**, **Workday**, **Greenhouse**, **Lever**, **Ashby**, **BambooHR**, **SmartRecruiters** and many more.

***

### 🤖 For AI agents & LLM apps

This actor is a clean tool call for agents that need live labour-market data.

- **Deterministic input** — `keywords` and `locations` are plain string arrays; no URL construction required.
- **Stable schema** — every row has the same keys in the same order, with `null` instead of missing fields, so parsing never breaks.
- **Structured salary** — `salary.min` / `salary.max` / `salary.currency` are already numbers and codes, no regex needed downstream.
- **Idempotent scheduling** — `onlyNewJobs: true` gives an agent a change feed instead of a full snapshot, so memory stays small and tokens stay cheap.
- **Provenance built in** — `searchKeyword`, `searchLocation` and `via` let an agent explain exactly where each row came from.

Minimal input (tested):

```json
{ "keywords": ["registered nurse"], "locations": ["Chicago"], "country": "us", "maxItems": 20 }
```

- **Output fields**: `title`, `companyName`, `location`, `remote`, `via`, `postedDate`, `salary {currency,min,max,unit}`, `contractType`, `description`, `qualifications[]`, `benefits[]`, `applyUrl`, `applyLinks[{board,url}]`, `contactEmail`, `searchKeyword`, `searchLocation`.
- **Billing**: $0.0025 per job row, $0.005 per run start. Nothing for empty searches.
- **Behaviour**: ~10 jobs per keyword x location (x employment type); more keywords and cities = more jobs. Empty `locations` searches the whole `country`. EU countries return nothing (Google's change). A run that cannot reach Google ends FAILED with the reason.

***

### ⚠️ Disclaimer

This actor collects only **publicly available** information displayed on Google search result pages. It does not log in, does not bypass any paywall, and does not access private or personal data.

Job listings and their content remain the property of their respective sources and employers. You are responsible for ensuring your use of the scraped data complies with Google's Terms of Service, the source boards' terms, and all applicable laws in your jurisdiction — including GDPR, CCPA and equivalent data-protection regulations where relevant. This tool is provided for legitimate research, recruitment and market-analysis purposes.

Google's jobs vertical is a third-party surface that can change or be withdrawn at any time — as already happened across the EU. Availability and field coverage may vary by country and over time.

***

### SEO Keywords

google jobs scraper, google for jobs scraper, google jobs api, scrape google jobs, google job search scraper, google jobs data extraction, job listings scraper, multi board job scraper, linkedin indeed glassdoor aggregator, job aggregator scraper, apply link scraper, job posting scraper, salary data scraper, employer email finder, recruitment lead generation, hiring intent data, job market research, job monitoring api, new job alerts api, ats detection, workday greenhouse lever scraper, remote jobs scraper, job feed api, google serp jobs, udm=8 scraper, apify job scraper, job board data, talent intelligence, recruiting automation

# Actor input Schema

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

Job titles or keywords to search. Examples: `software engineer`, `registered nurse`, `warehouse operative`.

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

Cities or regions to search in. Each keyword is searched once per location. Leave empty to search the whole selected country (for example `nurse jobs in United Kingdom`). Examples: `New York`, `Chicago`, `London`.

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

Which Google market to search. NOTE: Google has wound its jobs widget down across the EU — Germany, France, Spain, Italy, Netherlands, Ireland and other EU markets return ZERO jobs. Use the US, UK, Canada, Australia or India.

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

Google interface language (the `hl` parameter). Leave as `en` unless you need localized job text.

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

Paste any Google jobs-vertical URL, e.g. `https://www.google.com/search?q=nurse+jobs+in+chicago&udm=8&hl=en&gl=us`. Older `ibp=htl;jobs` links are accepted and upgraded automatically.

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

Ask Google for recent jobs only. Because Google returns just ~10 results per search, filtering here gives you 10 FRESH jobs instead of 10 mixed-age ones that mostly get discarded.

## `employmentTypes` (type: `array`):

Google honours one employment type per search, so each type you tick becomes its own search. That WIDENS coverage as well as filtering — each type returns a largely different set of jobs, so ticking two roughly doubles unique results.

## `workFromHome` (type: `boolean`):

Use Google's own remote-work filter. More reliable than keyword matching — in testing this returned 10/10 remote jobs, all of them absent from the unfiltered search.

## `radius` (type: `integer`):

How far around the location to search. Leave empty for Google's default.

## `googleDomain` (type: `string`):

Which Google domain to query, e.g. `google.co.uk` or `google.ca`. Leave empty for google.com.

## `postedWithinHours` (type: `integer`):

Keep only jobs posted in the last N hours. 24 = last day, 168 = last week. Leave empty for all jobs.

## `salaryOnly` (type: `boolean`):

Keep only jobs where Google displayed a salary. Roughly 30% of postings show one, so this filter cuts volume substantially.

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

Keep only jobs whose title or location indicates remote / work-from-home.

## `scheduleTypes` (type: `array`):

Keep only these schedules. Leave empty for all.

## `companyFilter` (type: `array`):

Keep only jobs whose employer name contains one of these (case-insensitive). Example: `Amazon`, `Google`. Leave empty for all employers.

## `excludeCompanies` (type: `array`):

Drop jobs whose employer name contains one of these. Useful for filtering out staffing agencies and job-mill recruiters.

## `viaFilter` (type: `array`):

Keep only jobs Google syndicated from these boards, matched against the `via` field. Example: `LinkedIn`, `Indeed`.

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

Emit only postings that no previous run of this actor has returned. Job identity is remembered for 45 days in a key-value store, so genuine repostings still show up later. Schedule the actor daily and every run gives you a clean list of what is new.

## `enrichEmails` (type: `boolean`):

For each job, look up the employer's own website from the company name and scrape its contact/about pages for a public email address. Adds `contactEmail`, `contactWebsite` and a full `emailEnrichment` object to every row. Not every employer can be resolved.

## `qualifyByPayment` (type: `boolean`):

Requires employer email enrichment. While reading the employer's site, also detect payment processors (Stripe, PayPal, Shopify, etc.) to qualify them as a commercial lead. No extra requests.

## `maxPagesPerQuery` (type: `integer`):

How many of Google's 'load more' pages to try per search (1-10, default 3). Each search returns ~10 jobs on page 1. Google currently answers most extra-page requests with an empty page, so for more jobs add keywords, locations or employment types instead. Empty pages are not billed.

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

Stop after this many jobs.

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

How many Google searches to run at once. Higher is faster; 10 is a good default.

## `proxy` (type: `object`):

Used only by the optional email-enrichment step when it fetches employer websites. Google itself is always fetched through the actor's own unblocking service.

## Actor input object example

```json
{
  "keywords": [
    "software engineer"
  ],
  "locations": [
    "New York",
    "San Francisco"
  ],
  "country": "us",
  "language": "en",
  "datePosted": "",
  "workFromHome": false,
  "salaryOnly": false,
  "remoteOnly": false,
  "onlyNewJobs": false,
  "enrichEmails": false,
  "qualifyByPayment": false,
  "maxPagesPerQuery": 3,
  "maxItems": 1000,
  "maxConcurrency": 10,
  "proxy": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

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

Every job returned — title, company, location, source board, salary, full description, apply links and (optionally) employer email.

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

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

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

```

## MCP server setup

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