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

Deep search, salaries and clean apply links: 50–100+ Google Jobs per search (past the 10-job limit), salary parsed into numbers, real posted dates, full descriptions, apply links with mismatches filtered out, new-jobs-only mode. Free during launch.

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

## Pricing

Pay per usage

This Actor is paid per platform usage. The Actor is free to use, and you only pay for the Apify platform usage, which gets cheaper the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-usage

## 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: Deep Search, Salaries & Clean Apply Links

**By Bight Systems.** Turn any Google Jobs search into a clean, spreadsheet-ready dataset: job title, company, location, remote/hybrid flag, employment type, **salary parsed into numbers**, **posted date as a real date**, full description, qualifications / responsibilities / benefits, and **apply links with mismatches filtered out**.

Google Jobs collects listings from LinkedIn, Indeed, Glassdoor, ZipRecruiter, company career sites and hundreds of local boards, so one run gives you one de-duplicated feed covering all of them.

*Last verified: November 10, 2026, on 39 live searches in 6 countries (US, UK, Canada, Germany, India, France), plus single-request checks in 30 more countries (36 countries with live results in total; see Supported countries).*

***

### Why this scraper

| | What you get |
|---|---|
| **More than 10 jobs per search** | Google shows only ~10 jobs per search. *Standard* depth re-runs your search with Google's own filter phrases (full time, part time, posted this week…) and merges the results without duplicates: **median 55 jobs per search** in our validation. *Deep* depth: **median 101**. |
| **Free during launch, then pay per job** | No Actor fee during the launch period. Later pricing will be per job returned, not per page: empty searches, duplicate jobs and unsupported countries won't cost anything in results. |
| **Salary as numbers** | `$120K–$150K a year`, `£14–£16 an hour`, `55.000 € bis 75.000 € pro Jahr`, `₹5L–₹8L a year`, `32 k € à 38 k € par an`, `R$ 5,5 mil a R$ 16,5 mil por mês` → min, max, ISO currency, period, plus an annualized range for comparisons. |
| **Real dates** | Google only shows "3 days ago". We also give `postedDate` (ISO date) and `postedHoursAgo`, computed from the run time. |
| **Clean apply links** | Google merges similar postings, so a job's link list can include links to *other* jobs. We drop links whose URL clearly names a different job, label every link (`match` / `employer` / `unverified`), and pick the most trustworthy **primary apply link**. Optional live check of the primary link. |
| **New-job alerts** | "Only new jobs" mode remembers what earlier runs returned and outputs new jobs only. Schedule it daily for alerts. |
| **Honest failures** | Retries with backoff, timeouts, partial results kept if Google misbehaves, a run summary (`OUTPUT`) explaining every search, and a clear message instead of an empty "success" when a country is not supported. |

### Use cases

- **Recruiting & staffing agencies:** find companies hiring for the roles you place, with apply links and descriptions.
- **Job boards & aggregators:** a daily feed of fresh listings per category and city.
- **Salary benchmarking:** normalized pay ranges by title, city and country.
- **Sales intelligence:** hiring activity as a buying signal for your product.
- **Labor-market research & AI agents:** structured, de-duplicated job data through the API.

### Quick start (60 seconds)

1. Enter one or more **Search queries** (e.g. `software engineer`), or paste **Google Jobs URLs** straight from your browser.
2. Optionally add **Locations** (e.g. `Austin, TX`) and pick a **Country**.
3. Choose **Search depth** (*Standard* is a good default) and click **Start**.
4. Open the **Overview** tab of the dataset, or export to CSV, Excel, JSON, or Google Sheets.

#### Example input

```json
{
  "queries": ["software engineer", "registered nurse"],
  "locations": ["Austin, TX"],
  "country": "US",
  "depth": "standard",
  "datePosted": "week",
  "maxJobsPerQuery": 100,
  "onlyNewJobs": true,
  "stateKey": "austin-tech-nursing"
}
```

#### Input fields

| Field | What it does | Default |
|---|---|---|
| `queries` | Job titles/keywords, one per search. You can include a place ("nurse Chicago IL"). | required unless `startUrls` |
| `startUrls` | Google Jobs search URLs (Jobs tab `udm=8`, or older `ibp=htl;jobs` links). We read the search text, country (`gl` or the domain), language (`hl`) and date/employment-type chips. Depth, filters and output settings still apply. | none |
| `locations` | Cities/regions; every query × every location is searched. | none |
| `country` | Google Jobs market (domain, region, currency). | `US` |
| `language` | Interface language; `auto` = the country's main language. | `auto` |
| `datePosted` | `any`, `today`, `3days`, `week`, `month`. | `any` |
| `remoteFilter` | `any`, `remoteOnly`, `excludeRemote`. | `any` |
| `employmentType` | One type via Google's own filter: `any`, `FULLTIME`, `PARTTIME`, `CONTRACTOR`, `INTERN`, `TEMPORARY`. | `any` |
| `employmentTypes` | Several types at once (e.g. `["PARTTIME","CONTRACTOR"]`). Each type is searched separately and merged without duplicates; jobs Google labels with another type are removed (jobs with no type shown are kept). | none |
| `postedWithinHours` | Keep only jobs posted at most X hours ago (e.g. `12`, `48`). Also sets the matching Google date window. Jobs without a posted date are removed. | none |
| `salaryOnly` | Keep only jobs with a parsed salary. | off |
| `minAnnualSalary` | Keep jobs whose annualized salary (top of range) is at least this amount, in the job's currency. Jobs without a salary are removed. | none |
| `includeCompanies` / `excludeCompanies` | Keep / drop jobs whose company name contains one of these words (case-insensitive). | none |
| `depth` | `single` (1 request, ~10 jobs), `standard` (≤9 requests, ~50), `deep` (≤21 requests, ~100+). | `standard` |
| `maxJobsPerQuery` / `maxJobsTotal` | Caps per search and per run (0 = no run cap). | 100 / 0 |
| `onlyNewJobs` + `stateKey` | Skip jobs returned by earlier runs with the same key (remembered 90 days). | off / `default` |
| `includeDescription`, `includeDescriptionHtml`, `includeHighlights` | Control the large text fields. | on, off, on |
| `outputFormat` | `full` (arrays, best for API), `compact` (no long text), `flat` (lists joined into text for CSV/Sheets). | `full` |
| `applyLinkFilter` | `standard` (drop clear mismatches), `strict` (only confirmed links), `off`. | `standard` |
| `verifyPrimaryApplyLink` | Open the primary link and confirm the page title matches. Slower. | off |
| `stripTrackingParams`, `dedupeAcrossQueries` | Remove `utm_*` parameters; return a job once across searches. | on, on |
| `maxConcurrency`, `requestTimeoutSecs`, `maxRetries` | Advanced reliability settings. | 10, 60, 3 |
| `healthCheck`, `healthThresholds`, `healthLinkSample` | Quality-monitoring mode for scheduled runs (see FAQ). | off, defaults, 40 |

### Output

Each dataset item is one job. Example (shortened):

```json
{
  "title": "Staff Accountant (Hybrid)",
  "company": "Northwest Administrators, Inc.",
  "location": "Seattle, WA",
  "isRemote": false,
  "isHybrid": true,
  "workArrangement": "HYBRID",
  "employmentType": "Full-time",
  "employmentTypes": ["FULL_TIME"],
  "salaryText": "$30.70–$41.54 an hour",
  "salaryMin": 30.7,
  "salaryMax": 41.54,
  "salaryCurrency": "USD",
  "salaryPeriod": "HOUR",
  "salaryAnnualMin": 63856,
  "salaryAnnualMax": 86403,
  "postedAtText": "5 days ago",
  "postedDate": "2026-11-05",
  "postedHoursAgo": 120,
  "sourceBoard": "Indeed",
  "companyWebsite": null,
  "primaryApplyUrl": "https://builtin.com/job/staff-accountant-hybrid/12098681",
  "primaryApplySite": "Built In",
  "primaryApplyLinkStatus": "confirmed",
  "applyLinks": [
    {"url": "https://www.indeed.com/viewjob?jk=a5149a544102405f", "site": "Indeed", "isDirect": true, "match": "unverified"},
    {"url": "https://builtin.com/job/staff-accountant-hybrid/12098681", "site": "Built In", "isDirect": false, "match": "match"}
  ],
  "applyLinksCount": 4,
  "applyLinksDropped": 4,
  "description": "Are you a Staff Accountant who enjoys maintaining financial records…",
  "qualifications": ["Strong understanding of accounting principles and practices", "…"],
  "responsibilities": ["Create journal entries to record transactions on a modified cash basis", "…"],
  "benefits": ["…"],
  "googleJobId": "zfGMTblnYdlpkpevAAAAAA==",
  "jobHash": "ba4fbf897bb02f9e",
  "mergedPostingsCount": 8,
  "googleJobsUrl": "https://www.google.com/search?ibp=htl;jobs&q=accountant+jobs+Seattle,+WA&htidocid=…",
  "searchQuery": "accountant",
  "searchLocation": "Seattle, WA",
  "foundViaSlice": "base",
  "country": "US",
  "language": "en",
  "scrapedAt": "2026-11-10T13:06:27Z"
}
```

#### Field reference

| Field | Meaning |
|---|---|
| `title`, `company`, `location` | As shown on Google Jobs. |
| `isRemote`, `isHybrid`, `workArrangement` | Remote = location "Anywhere" or remote in the title/labels; hybrid = "hybrid" in title or description. `REMOTE` / `HYBRID` / `ON_SITE`. |
| `employmentType`, `employmentTypes` | Text as shown, plus normalized list (`FULL_TIME`, `PART_TIME`, `CONTRACTOR`, `INTERN`, `TEMPORARY`, …). |
| `salaryText`, `salaryMin`, `salaryMax`, `salaryCurrency`, `salaryPeriod` | Pay as shown and parsed. Currency is ISO 4217; a bare `$` uses the country's dollar (USD in the US, CAD in Canada). Period: `HOUR`, `DAY`, `WEEK`, `MONTH`, `YEAR`. |
| `salaryAnnualMin`, `salaryAnnualMax` | Annualized (hour × 2080, day × 260, week × 52, month × 12) for comparisons. |
| `postedAtText`, `postedDate`, `postedHoursAgo` | Google's relative text, and the date computed from the run time (approximate by nature; "30+ days ago" is a lower bound). |
| `sourceBoard` | The board Google credits ("via LinkedIn"). |
| `companyWebsite` | Employer domain (e.g. `goldmansachs.com`) when an apply link or a link in the description is on a domain that matches the company name; job boards and ATS hosts are never used. Filled for roughly 15% of jobs in our tests; `null` otherwise. |
| `primaryApplyUrl`, `primaryApplySite`, `primaryApplyLinkStatus` | Best apply link. Status: `confirmed` (live page title checked), `employer` (company's own site), `match` (URL names this job), `unverified` (URL has no readable job name, e.g. an ID-only Indeed link). |
| `applyLinks[]` | Every kept link: `url`, `site`, `isDirect` (Google's "Apply directly" label or an applicant-tracking/career site), `match`. |
| `applyLinksCount`, `applyLinksDropped` | Links kept and links removed by the filter. |
| `description`, `descriptionHtml` | Full description text (and markup if requested). |
| `qualifications`, `responsibilities`, `benefits`, `otherHighlights` | Google's highlight bullets, when the job has them. |
| `degreeNote` | e.g. "No degree mentioned", when Google shows it. |
| `googleJobId`, `jobHash` | Google's job id, and our stable hash of title + company + location (use it to de-duplicate across runs). |
| `mergedPostingsCount` | How many postings Google grouped into this job. |
| `googleJobsUrl` | Link to the job on Google. |
| `searchQuery`, `searchLocation`, `searchText`, `foundViaSlice` | Which search found the job, the exact text sent to Google, and which depth slice found it. |
| `country`, `language`, `scrapedAt` | Run context; `scrapedAt` is UTC. |

The **Overview**, **Salaries**, **Apply links** and **Descriptions & highlights** tabs in the dataset give ready-made tables.
Each run also writes a summary to the key-value store record `OUTPUT`: per-search request counts, jobs found, failures, skipped searches and notes.

### What our validation measured (Nov 10, 2026)

39 live searches (US, UK, Canada, Germany, India, France; cities, remote, niche titles, filters):

| Metric | Result |
|---|---|
| Jobs per search, Standard depth | median **55** (32 searches) |
| Jobs per search, Deep depth | median **101** (83–116) |
| Searches with 10+ jobs | 38 of 39 (the one miss was a temporary empty page from Google; the search returned 55 jobs on re-run, and an automatic fallback for this case has since been added) |
| Requests that failed after retries | 3 of 372 (0.8%); runs kept everything else |
| Title / company / description filled | 100% / 100% / 99% |
| Employment type / posted date / salary present | 97% / 76% / 43% (only as often as Google shows them) |
| Salary text parsed into numbers | 99.9% of salaries shown |
| Primary apply link correct (live page check, 50 sampled jobs) | 26 of 26 readable pages matched the job; 24 sites block automated checks |
| Links our filter dropped that really were another job | 29 of 29 readable pages (40 dropped links sampled) |
| Run time | about 20–105 s per search (Standard/Deep); searches in one run go in parallel |

Limits we found and tell you about: some secondary apply links that we keep (`unverified`) still point to similar jobs, so use `primaryApplyUrl` or `applyLinkFilter: "strict"` when link accuracy matters most. Highlights exist only on some jobs (about 30–45%).

### Pricing

**Free during the launch period.** There is no Actor fee right now. You only pay your normal Apify platform usage (compute and Google SERP proxy), which came to about $0.50 per 1,000 jobs in our tests.

After launch, the Actor will switch to **pay-per-result pricing of about $2.50 per 1,000 jobs**, with lower prices on higher Apify plans. We will announce the change in advance in the Changelog, and Apify will show it on the Pricing tab before it takes effect.

How pricing will work:

- You pay only for jobs returned. There is no per-page fee and no start fee.
- Empty searches, duplicate jobs, jobs skipped by "Only new jobs" mode, and unsupported countries are never charged as results.
- Set **Maximum cost per run** in Apify to cap spend. The run stops cleanly and keeps the results so far.

### How it compares with typical Google Jobs results

| | Typical Google Jobs scraper | This Actor |
|---|---|---|
| Jobs per search | ~10 (one Google page); more pages cost more | **~50 (Standard) or ~100+ (Deep)** per search, de-duplicated |
| Price model | Per page of ~10 results ($3.50–$9 per 1,000 jobs), or per job plus platform usage | Free during launch, then per job (about $2.50 per 1,000) |
| Salary | Raw text | Text **plus** min, max, ISO currency, period and an annualized range |
| Posted date | "3 days ago" | Text **plus** ISO date and hours ago |
| Apply links | Everything Google lists, including links to other merged jobs | Mismatches dropped, every link labelled, one ranked primary link |
| Unsupported countries | Sometimes empty "successful" runs | Clear stop message, no result charges |
| Repeat monitoring | Often re-pay for the same jobs | "Only new jobs" mode |
| Quality monitoring | Rarely offered | Built-in health-check mode (see FAQ) |

### Supported countries

| Status | Countries |
|---|---|
| **Tested Nov 10, 2026** (Standard and Deep depth) | United States, United Kingdom, Canada, Germany, India, France |
| **Tested Nov 10, 2026** (single-request check: 8–10 jobs each, title/company/location/description/apply link filled on every job) | Netherlands, Spain, Brazil, Mexico, South Africa, Austria, Switzerland, Belgium, Italy, Portugal, Argentina, Colombia, Peru, Nigeria, Kenya, Ghana, Egypt, UAE, Saudi Arabia, Pakistan, Bangladesh, Singapore, Malaysia, Philippines, Indonesia, Japan, Hong Kong, Denmark |
| **Works, with limits** (tested Nov 10, 2026) | Greece: results are intermittent (Greek-language searches sometimes come back empty; English searches such as "accountant Athens" work). Chile: Google shows very few listings (2 jobs for "ingeniero Santiago"). |
| **Not supported by Google Jobs** (run stops immediately with a clear message and no result charges) | Australia, New Zealand, Ireland, Poland and Sweden (checked Nov 10, 2026), plus South Korea, Czechia, Turkey, Ukraine, Israel, Norway, Finland, Estonia, Latvia and Lithuania (per public reports) |

Salaries, posted dates and employment types are parsed in English, German, French, Spanish, Portuguese, Dutch, Italian, Japanese, Greek, Danish and Indonesian formats (for example `€ 3.171–€ 4.776 per maand`, `R$ 5,5 mil a R$ 16,5 mil por mês`, `年 ￥450万～￥650万`, `Rp 3,5 jt–Rp 4 jt per bulan`). For depth slices, Standard/Deep use translated filter phrases for English, German, French and Spanish, and English phrases elsewhere. Searches whose location names an unsupported country are skipped the same way. If a configured country doesn't work for you, please open an issue.

### Filters you control (not charged)

`employmentTypes`, `postedWithinHours`, `salaryOnly`, `minAnnualSalary`, `includeCompanies` and `excludeCompanies` are applied after Google returns results. Removed jobs are never pushed to the dataset, so they never count as results. The run summary (`OUTPUT` record) shows how many were removed and why in `filteredOut` (e.g. `{"noSalary": 26, "salaryBelowMinimum": 5}`). Tip: combine filters with *Standard* or *Deep* depth, because filtering happens after the search.

### Use from AI agents (MCP)

AI agents such as Claude, Cursor, ChatGPT-compatible clients or your own LangChain/LangGraph agent can call this Actor through the **Apify MCP server**.

- **Hosted (recommended):** add the MCP server URL `https://mcp.apify.com?tools=bightsystems/google-jobs-scraper` to your client. It signs in with OAuth, or send the header `Authorization: Bearer <YOUR_APIFY_TOKEN>`. The `tools=` part loads only this Actor, which keeps the agent's tool list small.
- **Local:** `npx -y @apify/actors-mcp-server --tools bightsystems/google-jobs-scraper` with `APIFY_TOKEN` set in the environment.

Example client config:

```json
{
  "mcpServers": {
    "google-jobs": {
      "url": "https://mcp.apify.com?tools=bightsystems/google-jobs-scraper",
      "headers": { "Authorization": "Bearer <YOUR_APIFY_TOKEN>" }
    }
  }
}
```

Tips for agents: call the Actor with a small input (one query, one location, `"depth": "single"` or `"standard"`, `"outputFormat": "compact"`), then read the results with the dataset tool. Use `maxJobsTotal` to cap the job count and `callOptions` such as `maxItems` / `maxTotalChargeUsd` to cap spend. The `OUTPUT` record has a short run summary an agent can read first. Example prompt: *"Find part-time or contract accountant jobs in Chicago posted in the last 48 hours that pay at least $60,000, and list company, salary and apply link."*

### Integrations

- **API:** `POST https://api.apify.com/v2/acts/bightsystems~google-jobs-scraper/run-sync-get-dataset-items?token=YOUR_TOKEN` with the input JSON returns the jobs directly. Python and JavaScript clients: `apify-client`.
- **Make, Zapier, n8n:** use the Apify app/node ("Run Actor" then "Get dataset items"), or trigger a scheduled run and send new jobs to Google Sheets, Airtable, Slack or your CRM.
- **Schedules:** Apify Schedules + `onlyNewJobs: true` = a daily alert that returns only new jobs.
- **Exports:** JSON, CSV, Excel, XML, RSS, or Google Sheets from the dataset. Use `outputFormat: "flat"` for one clean row per job in spreadsheets.

### FAQ

**Why do I get fewer jobs than I expected?** Google itself only has a limited number of listings per search; narrow searches (niche titles, small towns) can return 10–30 jobs even at Deep depth. Try a broader title or location, or *Deep* depth.

**Why did a remote search change my query?** Remote searches without a location return few or no jobs on Google, so we add the country name (e.g. "UX designer remote jobs United States"). The run summary notes this.

**Why are some apply links missing compared to Google?** The filter dropped links whose URL names a different job. Set `applyLinkFilter` to `off` to get everything, each labelled.

**Is `postedDate` exact?** It is computed from Google's relative text ("3 days ago") at run time, so it is accurate to the day for recent jobs and a lower bound for "30+ days ago".

**My run returned 0 jobs.** Check the status message and the `OUTPUT` record. Common causes: an unsupported country, a misspelled query, or a very narrow location.

**Do you scrape LinkedIn or Indeed directly?** No. We read Google Jobs results, which list jobs from those sites, and return the links Google provides.

**Is this legal?** We collect publicly shown job listings and do not collect personal data beyond what employers publish in job posts. You are responsible for using the data in line with the laws that apply to you (e.g. GDPR) and with the terms of the sites you link to.

**Can I monitor data quality myself?** Yes. Turn on `healthCheck`. After the searches, the run scores itself: jobs per search, field fill rates, failed requests, and a live check of a sample of primary apply links. It is marked FAILED, with the reasons in the status message, if a threshold is missed (thresholds can be changed in `healthThresholds`). Combine it with an Apify Schedule and run-failure notifications.

**Can I paste a link from Google?** Yes. Search on Google, open the Jobs tab, copy the address bar into `startUrls`. Links from other sites are skipped with a note in the run summary. If you only want the URLs, clear the example queries.

**Is there a search radius?** Not yet. Google currently ignores the radius parameter on its Jobs tab in our tests (5 km and 300 km returned the same town-only results), so we haven't added a radius setting that wouldn't work. Put the city you want in `locations`, or add nearby cities as extra locations.

**Something broke or a field is wrong.** Open an issue on the Actor's Issues tab with the run ID and we will look into it.

### Changelog

See the Changelog tab. Field changes are announced there.

# Changelog

This Actor's version history is a separate document: https://apify.com/bightsystems/google-jobs-scraper/changelog.md

# Actor input Schema

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

Job titles or keywords, one per line, exactly as you'd type them into Google Jobs. You can include a place ("nurse Chicago IL") or use Locations below. Each query × location is one search. Optional if you paste Google Jobs URLs below (clear the example queries if you only want the URLs).

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

Cities, regions or countries, e.g. "Austin, TX" or "London". Every query is searched in every location. Leave empty to search the whole country, or to use places already written into your queries.

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

Paste Google Jobs search URLs (the Jobs tab, `udm=8`, or older `ibp=htl;jobs` links). We read the search text, country (gl or domain), language (hl) and date/employment-type chips from each URL. Depth, filters and output settings below still apply. Non-Google URLs are skipped with a note.

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

Which Google Jobs market to search (sets the Google domain, region and currency). Google Jobs does not serve listings in Australia, New Zealand, Ireland, South Korea, Poland, Czechia, Turkey, Ukraine, Israel, the Nordics or the Baltics — runs for those stop immediately at no charge.

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

Interface language for Google and for the depth slices. "Auto" uses the country's main language (German for DE/AT/CH, English for US/GB/IN…).

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

Only return jobs posted within this window (uses Google Jobs' own date filter).

## `remoteFilter` (type: `string`):

"Remote only" asks Google for remote jobs and keeps only jobs marked remote (location "Anywhere", or remote in title/description). "Exclude remote" drops remote jobs.

## `employmentType` (type: `string`):

Restrict to one employment type (uses Google Jobs' own filter). For several types at once use "Employment types" below.

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

Pick one or more. Each type is searched separately and merged without duplicates, which also widens coverage (each type returns a largely different set of jobs). Jobs whose type Google shows as something else are removed (jobs with no type shown are kept).

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

Keep only jobs posted at most this many hours ago (e.g. 12 or 48). We also ask Google for the matching date window. Jobs without a posted date are removed when this is set.

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

Keep only jobs that show a salary parsed into numbers.

## `minAnnualSalary` (type: `integer`):

Keep only jobs whose annualized salary (top of the range) is at least this amount, in the job's own currency (e.g. 80000 for USD 80K in the US). Jobs without a salary are removed when this is set.

## `includeCompanies` (type: `array`):

Keep only jobs whose company name contains one of these words (case-insensitive), e.g. "Amazon", "Siemens".

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

Drop jobs whose company name contains one of these words (case-insensitive), e.g. staffing agencies like "Robert Half".

## `depth` (type: `string`):

Google shows only ~10 jobs per search. We go deeper by re-running each search with Google's own filter phrases (full time, part time, posted this week…) and merging the results without duplicates. Single: 1 request, ~10 jobs. Standard: up to 9 requests, typically 40–70 jobs. Deep: up to 21 requests, typically 80–130+ jobs on broad searches. You pay per job returned, not per request.

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

Stop a search once this many unique jobs are found. A search is one query × one location.

## `maxJobsTotal` (type: `integer`):

Hard cap across the whole run. 0 = no cap (you can also set a maximum cost per run in Apify).

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

Remember every job this Actor has returned (in a named key-value store in your account) and skip jobs already returned by earlier runs with the same state key. Ideal for scheduled daily alerts: you only pay for new jobs. Jobs are remembered for 90 days.

## `stateKey` (type: `string`):

Name of the memory used by "Only new jobs". Use a different key per monitor (e.g. "nurses-chicago") so monitors don't hide each other's jobs.

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

Full job description as plain text.

## `includeDescriptionHtml` (type: `boolean`):

Also return the description with its original line breaks/markup.

## `includeHighlights` (type: `boolean`):

Google's structured highlight bullets, when the job has them.

## `outputFormat` (type: `string`):

Full: every field, lists as arrays (best for API/JSON). Compact: no description, highlights or link lists — small and fast. Flat: lists joined into text so CSV/Excel/Google Sheets exports have one row per job.

## `applyLinkFilter` (type: `string`):

Google merges similar postings, so a job's link list can include links to other jobs. Standard: drop links whose URL clearly names a different job (the job's own posting link is always kept). Strict: keep only the job's own posting link plus links whose URL confirms the same title or employer — fewer links, fewer wrong ones. Off: return every link Google lists, each labelled with its match status.

## `verifyPrimaryApplyLink` (type: `boolean`):

Open each job's primary apply link and confirm the page title matches the job (switching to the next good link if not). Slower; many job boards block automated visits, which are reported as "blocked" rather than failed.

## `stripTrackingParams` (type: `boolean`):

Remove utm\_\* parameters Google adds to apply links.

## `dedupeAcrossQueries` (type: `boolean`):

If the same job (same title, company and location) is found by two of your searches, return it once.

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

How many Google requests run at the same time.

## `requestTimeoutSecs` (type: `integer`):

Give up on one Google request after this long and retry.

## `maxRetries` (type: `integer`):

Retries with exponential backoff for timeouts, blocks and bad pages. If a request still fails, the run continues and keeps everything found so far.

## `healthCheck` (type: `boolean`):

For scheduled monitoring. After the searches, score the results against quality thresholds (jobs per search, field fill rates, failed requests, live check of a sample of primary apply links). The run is marked FAILED, with the reasons in the status message, if any threshold is missed. A report is saved to the HEALTH record in the key-value store.

## `healthThresholds` (type: `object`):

Override any default threshold, e.g. {"minSearchesWith8PlusPct": 90, "minDescriptionPct": 90, "minPrimaryLinkConfirmedPct": 80}. Keys: minSearchesWith8PlusPct, minTitleCompanyPct, minDescriptionPct, minApplyUrlPct, minPostedDatePct, minEmploymentTypePct, minSalaryParsedPct, maxFailedRequestPct, minPrimaryLinkConfirmedPct, minReadableLinkChecks.

## `healthLinkSample` (type: `integer`):

How many random primary apply links the health check opens to confirm the page matches the job. These checks use no proxy and add no proxy cost.

## Actor input object example

```json
{
  "queries": [
    "software engineer",
    "registered nurse",
    "warehouse associate"
  ],
  "locations": [
    "Austin, TX",
    "Chicago, IL"
  ],
  "startUrls": [
    {
      "url": "https://www.google.com/search?q=registered+nurse+jobs+chicago&udm=8&hl=en&gl=us"
    }
  ],
  "country": "US",
  "language": "auto",
  "datePosted": "any",
  "remoteFilter": "any",
  "employmentType": "any",
  "salaryOnly": false,
  "depth": "standard",
  "maxJobsPerQuery": 100,
  "maxJobsTotal": 0,
  "onlyNewJobs": false,
  "stateKey": "default",
  "includeDescription": true,
  "includeDescriptionHtml": false,
  "includeHighlights": true,
  "outputFormat": "full",
  "applyLinkFilter": "standard",
  "verifyPrimaryApplyLink": false,
  "stripTrackingParams": true,
  "dedupeAcrossQueries": true,
  "maxConcurrency": 10,
  "requestTimeoutSecs": 60,
  "maxRetries": 3,
  "healthCheck": false,
  "healthLinkSample": 40
}
```

# Actor output Schema

## `results` (type: `string`):

All scraped jobs, one item per job: title, company, location, normalized salary, ISO posted date, employment type, description, highlights, filtered apply links and Google job ID. Opens on the Overview view; Salary, Apply links and Details views are also available.

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

JSON summary of the run: country, language, depth, per-search counts and notes, skipped searches, SERP requests, failed requests, duplicates removed, apply links dropped and run time.

# 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",
        "registered nurse"
    ],
    "locations": [
        "Austin, TX"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("bightsystems/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",
        "registered nurse",
    ],
    "locations": ["Austin, TX"],
}

# Run the Actor and wait for it to finish
run = client.actor("bightsystems/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",
    "registered nurse"
  ],
  "locations": [
    "Austin, TX"
  ]
}' |
apify call bightsystems/google-jobs-scraper --silent --output-dataset

```

## MCP server setup

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