# Google Jobs Scraper — Google for Jobs Listings, Apply Links (`steadyfetch/google-jobs-scraper`) Actor

Google Jobs: about 90–130 job cards per search where Google has that many, not the first ten. Every row: title, company, location, apply links, full description; salary where Google posts one. From $1.00 per 1,000 job listings; a search that returns listings adds a $0.004 fee.

- **URL**: https://apify.com/steadyfetch/google-jobs-scraper.md
- **Developed by:** [Steadyfetch Team](https://apify.com/steadyfetch) (community)
- **Categories:** Jobs, Lead generation, Automation
- **Stats:** 7 total users, 4 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?

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 — Google for Jobs Listings, Apply Links

**A Google Jobs scraper that returns the whole Google for Jobs panel, not the first ten
results — from $1.00 per 1,000 job listings.** One search returns about **90–130 job cards**
where Google has that many; niche searches return every card Google has. One row per posting:
title, company, location, apply links and the full description, with salary where Google posts
one.

**Never pay for a job we didn't deliver.** Set a row cap and a deadline and the run stops exactly
there — no runaway bills.

**Using an AI agent?** Pin this actor in Apify's MCP server with one link: `https://mcp.apify.com?tools=steadyfetch/google-jobs-scraper`

- **Actor id:** `steadyfetch/google-jobs-scraper`
- **Input:** `{ "queries": ["software engineer jobs in austin"] }` — the one field to set.
- **Cap the bill:** set `maxTotalChargeUsd` on the run (a run option, not Actor input), e.g. `0.50` — the run stops when it reaches it.

**From $1.00 per 1,000 job listings** — all-inclusive pay per event, charged only on delivery.
**Each search that returns at least one listing** also carries one search fee of **$0.004**; a search that returns nothing — empty,
blocked, or never fetched because a cap bound first — pays no search fee.

**Running the same search again?** A listing already delivered to your account is never charged a
second time (a run started with a scoped API token needs key-value store permission for that) —
see [Never pay for the same listing twice](#never-pay-for-the-same-listing-twice).

**Just want to see it work?** Run it with no input at all (`{}` from the API or an AI agent) and
you get a real 5-listing sample — "software engineer" in Austin, TX on Google Jobs US — charged like any run,
at most $0.03 on the Apify free plan and less on paid plans. Set only the filters — a country, a location, a row cap — and no job searches, and
that same sample runs under them (your own location, or a country other than the US, replaces Austin), charged like any run; a limit larger than the sample's own is
capped at it, and one extra uncharged row names which settings were yours. In the console, Start
with the example searches already in the form and you get those searches for real, up to your row
cap.

![Google Jobs Scraper input form in the Apify console: job searches, location and country](https://api.apify.com/v2/key-value-stores/wciMDNpNgBh87mGUp/records/google-jobs-scraper--input-form.png)

> **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

Five US "software engineer" cards from a real run, in the Apify console's dataset table — the source board, posted date, employment type and the full description, one row per job:

![Dataset table of a real Google Jobs Scraper run: five US software engineer job cards with source board, posted date, employment type and description columns](https://api.apify.com/v2/key-value-stores/wciMDNpNgBh87mGUp/records/google-jobs-scraper--dataset-table.png)

And one delivered row in full:

```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
Set:        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;
                            up to 10,000 per run)
            maxRunSeconds  (integer, default 900 — clean stop, never a timeout kill;
                            30 to 3,600 seconds)
            includeSeen    (boolean, default false — OFF: a listing already delivered to
                           your account is skipped, uncharged. ON: it comes back marked
                           repeat:true with firstSeenRunId, still uncharged)
            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
            search         once per search that returns at least one listing ($0.004; a search that returns
                           nothing pays none)
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}'
```

### Use it as an API endpoint (Standby)

You can also call this actor like an API: send a job search and get the listings back in the same response, with no run to start and no dataset to read. The address is `https://steadyfetch--google-jobs-scraper.apify.actor`, and the actor's **Endpoints** tab lets you try it from the browser.

```bash
curl -H "Authorization: Bearer <YOUR_APIFY_TOKEN>" \
  "https://steadyfetch--google-jobs-scraper.apify.actor/?queries=software%20engineer&country=US&maxItems=5"
```

Or POST the same input a run takes:

```bash
curl -X POST -H "Authorization: Bearer <YOUR_APIFY_TOKEN>" -H "Content-Type: application/json" \
  -d '{"queries": ["nurse jobs in chicago"], "country": "US", "maxItems": 50}' \
  "https://steadyfetch--google-jobs-scraper.apify.actor/"
```

The answer is JSON: `rows` (one per listing, with the same fields as a row in a run's dataset — apply links and the full description included — plus the same uncharged rows a run writes for a search that found nothing or a setting that needs a word), `receipt` (the run's closing summary row), `delivered`, `charged`, and `notes` for anything you should know, in plain words. You are charged exactly as in a run — per job listing delivered, plus the search fee for each search that returns at least one listing — and a search that returns nothing is never charged. If the charge for a request cannot be completed, its listings are not sent. While this actor is not running new searches, a request gets the same uncharged row a run gets, and nothing is charged.

Good to know: one request takes up to 3 searches and 150 listings (1 search and 20 listings on the Apify free plan) and has about three minutes to answer. The endpoint has no account memory: it does not skip listings your account already has, and it does not take `resumeFromDatasetId` or `includeSeen`, so every request is answered and charged on its own. For a long list of searches, or a daily pull that skips repeats uncharged, run the actor on a Schedule as usual.

### 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, from the 123 in the list: the US
and Canada, all of Latin America, all of Sub-Saharan Africa, and the countries Google's job
search lists in Europe, Asia, Oceania and the Middle East and North Africa (the console's
Country dropdown carries every one). **US and GB are verified end to end**; the other listed
countries are passed to Google the same way and the jobs panel works the same way. The code, the
three-letter code, the country's name in English or in its own language, and everyday spellings
("U.S.", England, en-GB, Maroc, KSA) are all read — a spelling that is not the code itself gets
one uncharged row saying how it was read. A country outside the list runs no search, is not
charged, and its row lists the countries you can pick. 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, no result fee and no search fee |
| 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 |
| A search never fetched because a cap or deadline bound first | `STOPPED_AT_LIMIT` | SUCCEEDED — no search fee, listed in `resumeCursor` |
| A listing your account already has from an earlier run | — | skipped, never charged twice — and a search of only those pays no search fee (needs key-value store permission on a scoped token) |

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. The
search fee follows the same rule: it is charged only on a search that
returned at least one listing, it sits on that search's own row with `charged: true`, and the
summary row's `chargedEvents` counts it next to the listings.

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.

### Never pay for the same listing twice

Every run remembers the listings it delivered for your account, in a key-value store called
`google-jobs-watch-account` in your own Apify account. Run the same searches again and what you
already have is **skipped before it takes a row slot**: no row, no charge, and no bite out of your
row cap. The store lives in your own Apify account, so a run started with a scoped API token in
restricted-access mode needs key-value store **Read, Write and Create** permission (or Actor runs
set to **Full access**) — without it the run says so on its status line and on an uncharged first
row, and it cannot skip what you already have (Write is what lets the run remember what it
delivered; without it every run pays again). The status line says how many (*18 already in your
account, skipped, not charged*) and `OUTPUT.skippedSeen` holds the number. The **search fee**
follows the same rule: a search that returns only listings you already have returned you nothing
new, so it pays no fee.

Want one complete dataset per run instead of only what changed? Turn on **Include listings you
already have** (`includeSeen: true`) and those rows come back anyway — marked `repeat: true` with
`firstSeenAt` and `firstSeenRunId` naming the run that first delivered them, `charged: false`.

- **The store lives in your account**, one record (`SEEN`). Open it in *Storage → Key-value
  stores* to see everything the actor has delivered you, or delete the record to start over.
  This actor runs with limited permissions and touches nothing else in your account.
- **Entries older than 90 days count as new again**, so a listing you collected last quarter is
  collected (and charged) again if it is still live.
- **Only delivered rows are remembered.** A blocked panel, an empty search or a page we could not
  read leaves nothing behind, so the next run tries it again.
- **If the store cannot be read on some run**, the run still runs: it delivers and charges as
  usual and the status line says the repeat check was unavailable, so you know a repeat may have
  been charged that once. If the reason is the token's own permissions, the status line says that
  instead and names the permission to grant — set it under *Settings → API & Integrations* on the
  token, or start the run from the console.

### Limits are hard limits

`maxItems` and `maxRunSeconds` stop the run exactly where you set them. **Ask for more
than this actor can do and the run still starts.** One run delivers up to 10,000 listings
and runs 30 to 3,600 seconds. A larger (or smaller) number is not an error: the run
continues at the nearest limit and writes one extra uncharged row saying what you asked
for and what bound it — you are never left with no run at all.
The run still
finishes **successfully**, the summary row names which limit bound, and `resumeCursor`
lists the searches that were not reached. A time limit ends the *collecting*, never the
delivering: a panel already fetched when the clock runs out is still delivered in full, so
a run never pays for listings it does not hand you. 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?**
Yes — like any run, and at most $0.03 on the Apify free plan (less on paid plans). With no
input at all the run is a real 5-listing sample
("software engineer" in Austin, TX on Google Jobs US) and you pay for the listings it delivers plus the
one search fee for the search that returned them. If Google does not
answer at that moment you get one uncharged row saying so, never an empty result. Set only filters
and no job searches and the same sample runs under them, charged the same way.

**What does a search cost?**
$0.004 per search that returns at least one listing, on top of the
per-listing price for what it delivers. A search that returns nothing pays no search fee, and
a search the run never fetched — because your cap or your deadline bound first, this run reached
its fetch limit, or our side could not fetch more on this run — pays nothing at all. The same search is never charged its fee twice, even across a run
that was moved between servers.

***

### 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.

The one-link pin is at the top of this page.

### 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, nothing charged for starting a run, charged only on delivery. Google Jobs adds one search fee of $0.004 per search that returns listings; a search that returns nothing pays nothing.

| Source | Actor |
|---|---|
| Indeed | [Indeed Jobs Scraper — Indeed API Alternative, Salary, Vacancies](https://apify.com/steadyfetch/indeed-jobs-scraper) |
| A company's own career site (paste a domain) | [Career Page Scraper — Company Jobs by Domain, 9 ATS Boards](https://apify.com/steadyfetch/company-jobs-by-domain) |
| Glassdoor, with the employer rating | [Glassdoor Jobs Scraper — Glassdoor API Alternative + Ratings](https://apify.com/steadyfetch/glassdoor-jobs-scraper) |
| Several boards at once, de-duplicated | [Multi Job Board Scraper — Career Pages, Listings Aggregator](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,
nothing charged for starting a run, charged only on delivery (three actors add a small delivery-conditional fee: keyword volume's fresh lookup, profile posts' profile lookup, Google Jobs' search fee — each README states it).

| 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 usually reply within a couple of hours, always within a day. Feature requests welcome.

If it earned its keep, a rating helps other buyers find it, and saving the actor keeps it one click away.

# Changelog

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

# 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. A Google Jobs search link pasted here is read as the search it names — its words, and its country when Country is left at its default — with one uncharged note row saying how it was read; a link carrying a filter this actor cannot apply is refused on its own row instead. No result fee for a search that returns nothing, a blocked or removed listing, or a listing this account already has. An Indeed search is steadyfetch/indeed-jobs-scraper; a company's own careers page is steadyfetch/company-jobs-by-domain. Called with no input at all (an empty {} from the API or an agent), the run is a 5-listing sample — "software engineer" in Austin, TX on Google Jobs US — charged like any run, and setting only the filters below leaves that sample running under them.

## `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 Jobs results to ask for — 123 in the list: the US, Canada, all of Latin America and Sub-Saharan Africa, and the listed countries of Europe, Asia, Oceania and the Middle East and North Africa. The code, the three-letter code (IND), the name in English or its own language (Deutschland) and spellings like "U.S." or en-GB are read, with one uncharged row saying how. A country outside the list runs no search and is not charged.

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

Hard cap on delivered job listings across every search in this run. This actor delivers up to 10,000 listings in one run; ask for more and the run continues at 10,000, with one uncharged row saying so.

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

The run stops cleanly before this many seconds and reports what is left, instead of being killed by a timeout. A run is given between 30 and 3,600 seconds; ask for more or less and the run continues at whichever of the two is nearer, with one uncharged row saying so.

## `includeSeen` (type: `boolean`):

OFF (default): a listing already delivered to your account is skipped before it takes a row slot — not delivered again, not charged, not counted against your row cap. ON: those rows come back anyway, marked `repeat: true` with `firstSeenAt` and `firstSeenRunId` naming the run that first delivered them, and still not charged. Use it when you want one complete dataset per run rather than only what changed.

## `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,
  "includeSeen": false
}
```

# 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; a search that returned listings carries its one search fee on this row.

## `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/TnigeyW2gFylbDgAV/openapi.json
