# Google Jobs Scraper — Full Panel, Salary, Apply Links (`steadyfetch/google-jobs-scraper`) Actor

The full Google Jobs panel for any search — about 90–130 job cards where Google has that many, not the first ten. Title, company, location, source board, posted age, salary when shown, direct apply links and the full description on every row. You are never charged for a job that was not delivered.

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

## Pricing

from $1.00 / 1,000 job listings

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?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Google Jobs Scraper

**The full Google Jobs panel, not the first ten results.** One search returns about
**90–130 job cards** where Google has that many — niche searches return every card Google
has. Set a row cap and a deadline and the run stops exactly there — no runaway bills.

**From $1.00 per 1,000 job listings** — all-inclusive pay per event, no start fee, charged only
on delivery. Never pay for a job we didn't deliver.

> **Unofficial.** This actor is not affiliated with, endorsed by, or sponsored by Google.
> "Google" and all related marks are trademarks of Google LLC. It reads only the public
> jobs panel an ordinary browser sees.

Every row carries `charged` and `missReason`, so you can reconcile the invoice from the
dataset itself without opening the console. **Only rows with `charged: true` were billed.**

***

### Output — a real row

```json
{
  "source": "google-jobs",
  "sourceJobId": "Ati0AB1gZvCYY_O-AAAAAA==",
  "title": "Part-Time Barista/Counter Person 20+ hours",
  "company": "Madison Kitchen",
  "location": { "raw": "Seattle, WA", "city": "Seattle", "region": "WA", "postcode": null, "country": "US" },
  "via": "Poached Jobs",
  "postedRelative": "6 days ago",
  "salaryText": "$21.30 an hour",
  "salary": { "min": 21.3, "max": null, "currency": "USD", "period": "hour" },
  "employmentType": "Part-time",
  "qualification": "No degree mentioned",
  "applyUrl": "https://poachedjobs.com/jobs/seattle-wa-barista-jobs-madison-kitchen-part-time-baristacounter-person-20-hours",
  "applyOptions": [
    { "board": "Poached Jobs", "url": "https://poachedjobs.com/jobs/seattle-wa-barista-jobs-madison-kitchen-part-time-baristacounter-person-20-hours" }
  ],
  "descriptionText": "We're busy and we're hiring! …the job's complete description text…",
  "jobUrl": "https://www.google.com/search?ibp=htl;jobs&q=part+time+barista+jobs+near+seattle&htidocid=Ati0AB1gZvCYY_O-AAAAAA%3D%3D",
  "query": "part time barista jobs near seattle",
  "country": "US",
  "dedupeKey": "gjobs:Ati0AB1gZvCYY_O-AAAAAA==",
  "charged": true,
  "missReason": null
}
```

Plus **one search row per search** — `query`, `country`, `delivered`, `outcome` — and
**one summary row** with `delivered`, `stoppedBy`, `resumeCursor`, `datasetId` (paste it into
`resumeFromDatasetId` to continue this run later), `carriedOver` and `chargedEvents`.

**[Browse the full live example dataset →](https://api.apify.com/v2/datasets/mGLDiFYTtGvZ1Luhw/items?clean=true\&format=json)** — one real run, unedited: five delivered US listings capped at `maxItems: 5`, the search row that reconciles them, and the run's own summary row last. Five listings delivered, five charged.

**Every row ships the real apply links** — the direct URLs on the job boards themselves
(LinkedIn, Indeed, ZipRecruiter, the employer's own site…), in the same order Google's
"Apply" buttons show them — plus the job's **full description text** and the `via` source
board. `jobUrl` opens the exact card on Google Jobs.

### Agent / API paste-block

```
Actor:      steadyfetch/google-jobs-scraper
Required:   queries        (array of job searches, e.g. "software engineer jobs in austin")
Optional:   location       (string — added to searches that don't already name one)
            country        (ISO-2, default "US" — which country's Google to ask)
            maxItems       (integer, default 300 — hard cap for the whole run)
            maxRunSeconds  (integer, default 900 — clean stop, never a timeout kill)
            resumeFromDatasetId (string — an earlier run's dataset ID; every listing already
                           in it is skipped and never charged again)
Charges:    job-listing    once per delivered job listing
Note:       omit a field — or send it as null — to use its default.
Build spec: https://apify.com/steadyfetch/google-jobs-scraper/api
Token:      https://console.apify.com/settings/integrations
```

```bash
curl -X POST "https://api.apify.com/v2/acts/steadyfetch~google-jobs-scraper/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"queries":["software engineer jobs in austin"],"maxItems":50}'
```

### How many jobs one search returns

**About 90–130 in covered regions**, measured across US and UK searches. A niche search
returns fewer because Google has fewer — that is the supply, not a cap. The same job
reached by two of your searches is delivered and charged **once** (Google repeats jobs
across related searches; we de-duplicate on Google's own job id). More rows come from more
searches: vary the phrasing, the location or the country.

### Countries

`country` picks which country's Google results to ask for. **US and GB are verified end to
end**; other codes are passed to Google as-is and the jobs panel works the same way. The
rows come back in English (`hl=en`).

### What you are never charged for

| situation | `missReason` | run status |
|---|---|---|
| Google answered with an access check | `CHALLENGED` | SUCCEEDED — re-runnable, and the row says so |
| Google rate-limited the request | `THROTTLED` | SUCCEEDED — re-runnable |
| Google returned a page we could not read | `SOURCE_UNAVAILABLE` | SUCCEEDED — re-runnable |
| The search ran and its jobs panel was empty | `CLEAN_EMPTY` | SUCCEEDED — a real answer |
| A search we could not read | `USER_INPUT` | SUCCEEDED — the row names the fix |
| Your own `maxItems` / `maxRunSeconds` / cost cap | `STOPPED_AT_LIMIT` | SUCCEEDED — summary row with `resumeCursor` and the `datasetId` to continue from |

We never guess. An access check is reported as an access check, not as "no jobs found" — a
temporary problem is never dressed up as a permanent verdict, and it is never charged.

A search that comes back blocked, rate-limited or unreadable is **fetched again before any of
those rows ships**. That second attempt is on us: a search only reports a miss when the retry
came up short too, and either way there is no result fee.

**This actor may fail when Google changes things.** Failed searches ship as rows with
**`charged: false`**, the run still SUCCEEDS, and the row says whether re-running can help.

### Limits are hard limits

`maxItems` and `maxRunSeconds` stop the run exactly where you set them. The run still
finishes **successfully**, the summary row names which limit bound, and `resumeCursor`
lists the searches that were not reached. To collect the rest without paying again for what you already have, re-run the same input
with a higher cap and `resumeFromDatasetId` set to that run's dataset ID (it is on the
summary row as `datasetId`): every listing already in that dataset is skipped — never
fetched, never charged — and the new summary's `carriedOver` says how many. A dataset ID
that cannot be read stops the run before it fetches anything, uncharged.

They are limits on the **run**, not on a container. If Apify moves your run to another
server part-way through, or you resurrect a finished run, it picks up from what is already
in your dataset: the same job is never delivered twice and never charged twice, and
`maxItems` still means what you typed.

### FAQ

**Where do the apply links point?**
At the job boards themselves — the same destinations as Google's own "Apply on …" buttons,
with every option listed in `applyOptions` and the first one in `applyUrl`.

**Why is `salary` empty on some rows?**
Because Google did not show one. `salaryText` is the exact string Google printed and
`salary` is our parse of it; when the card carries no salary both stay `null` rather than
being estimated.

**Is the posted date exact?**
Google publishes a relative age ("6 days ago"), and that is what `postedRelative` carries.
We do not fabricate a timestamp from it.

**Does `{}` charge anything?**
No job listings are charged. With no searches the run returns uncharged sample rows that
show the exact output shape, and it contacts nothing.

***

### Run it from an AI agent (MCP)

Works out of the box from any MCP client (Claude, Cursor, …) via Apify's MCP server — ask for "nurse jobs Google lists in Texas this week" and point it at this actor.

**Pin this actor in one click** — add this server to your MCP config:

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

### Steadyfetch jobs suite

One actor per source, one job each, the same row shape — so a pipeline built on one source takes the
next without a rewrite. All-inclusive pay per event, no start fee, charged only on delivery.

| Source | Actor |
|---|---|
| Indeed | [Indeed Jobs Scraper — Salary, Apply Links, Exact Country](https://apify.com/steadyfetch/indeed-jobs-scraper) |
| A company's own career site (paste a domain) | [Company Jobs Scraper — Career Pages by Domain, 9 ATS Boards](https://apify.com/steadyfetch/company-jobs-by-domain) |
| Glassdoor, with the employer rating | [Glassdoor Jobs Scraper — Employer Star Rating on Every Row](https://apify.com/steadyfetch/glassdoor-jobs-scraper) |
| Several boards at once, de-duplicated | [Job Board Scraper — Indeed + Career Sites, One Deduped Feed](https://apify.com/steadyfetch/multi-job-board-scraper) |
| Google Jobs, with the full panel and apply links | **this actor** |

**The rest of the steadyfetch shelf** — same contract everywhere: all-inclusive pay per event,
no start fee, charged only on delivery.

| Family | Actors |
|---|---|
| Ad creative intelligence | [Facebook](https://apify.com/steadyfetch/facebook-ads-transcript-scraper) · [Google Ads video](https://apify.com/steadyfetch/google-ads-video-transcript-scraper) · [TikTok](https://apify.com/steadyfetch/tiktok-ads-transcript-scraper) · [LinkedIn](https://apify.com/steadyfetch/linkedin-ads-transcript-scraper) · [Google Ads text & OCR](https://apify.com/steadyfetch/google-ads-creative-text-scraper) |
| Trends & keywords | [Google Trends](https://apify.com/steadyfetch/google-trends-scraper) · [Trends Now](https://apify.com/steadyfetch/google-trends-now-scraper) · [Breakout keywords](https://apify.com/steadyfetch/breakout-keywords-scraper) · [Autocomplete keywords](https://apify.com/steadyfetch/google-keyword-suggest-scraper) · [Keyword volume & CPC](https://apify.com/steadyfetch/keyword-search-volume-scraper) · [Social trends](https://apify.com/steadyfetch/social-trends-scraper) |
| YouTube transcripts | [YouTube videos](https://apify.com/steadyfetch/youtube-transcript-scraper) · [YouTube channels](https://apify.com/steadyfetch/youtube-channel-transcripts) |
| Instagram | [Reel transcripts](https://apify.com/steadyfetch/instagram-reel-transcript-scraper) · [Profile posts](https://apify.com/steadyfetch/instagram-profile-posts) |
| **Jobs** | [Indeed](https://apify.com/steadyfetch/indeed-jobs-scraper) · [Career sites by domain](https://apify.com/steadyfetch/company-jobs-by-domain) · [Glassdoor](https://apify.com/steadyfetch/glassdoor-jobs-scraper) · [Multi-board](https://apify.com/steadyfetch/multi-job-board-scraper) · [Google Jobs](https://apify.com/steadyfetch/google-jobs-scraper) |
| Amazon | [Products](https://apify.com/steadyfetch/amazon-product-scraper) · [Search](https://apify.com/steadyfetch/amazon-search-scraper) · [Bestsellers](https://apify.com/steadyfetch/amazon-bestsellers-scraper) · [Sellers](https://apify.com/steadyfetch/amazon-seller-scraper) |
| Any media file | [Speech to Text · any link or file](https://apify.com/steadyfetch/media-transcriber) |

### Feedback & support

Found an issue? Open it on the **Issues tab** — we respond within one business day. Feature requests welcome. If this actor earned its keep, a rating helps other buyers find it, and saving it keeps it one click away in your console.

# Actor input Schema

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

Job titles or keyword searches, one per line — for example "software engineer jobs in austin" or "registered nurse". Plain keywords work too; a location typed into the search wins over the Location field. Leave the default in place to see uncharged sample rows first.

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

City or region to add to every search that does not already name one (for example "Seattle" or "London"). Leave empty to let each search speak for itself.

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

Which country's Google results to ask for. US and GB are verified end to end; other codes are passed to Google as-is.

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

Hard cap on delivered job listings across every search in this run.

## `maxRunSeconds` (type: `integer`):

The run stops cleanly before this many seconds and reports what is left, instead of being killed by a timeout.

## `resumeFromDatasetId` (type: `string`):

The default dataset ID of an earlier run of this actor. Leave empty to start fresh. If the dataset cannot be read, the run stops before fetching anything and charges nothing, rather than risk billing you twice.

## Actor input object example

```json
{
  "queries": [
    "software engineer jobs in austin",
    "nurse jobs in chicago"
  ],
  "country": "US",
  "maxItems": 300,
  "maxRunSeconds": 900
}
```

# Actor output Schema

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

One row per delivered job listing: title, company, location, source board, posted age, salary when Google shows one, employment type, direct apply links and the full description. Every row carries `charged` and `missReason`, so the invoice reconciles from the dataset itself. Only rows with charged = true were billed.

## `searches` (type: `string`):

One row per search: how many jobs it delivered, or the reason it delivered none, in `missReason`. Searches that were blocked, unreadable or empty are never charged.

## `summary` (type: `string`):

Delivered count, searches run, uncharged misses, what stopped the run, and the charged-event totals.

## `errors` (type: `string`):

Present only when Google answered a search with an access check or an unreadable page: the search and the reason. These are re-runnable, not permanent.

# 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 in austin",
        "nurse jobs in chicago"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("steadyfetch/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 in austin",
        "nurse jobs in chicago",
    ] }

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

```

## MCP server setup

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