---
title: "Google Jobs scraper"
description: "Scrape Google Jobs results as JSON: job title, employer, location, the board it came from and the listing link. One API request from Python, Node.js, CLI or MCP."
canonical: https://www.zenrows.com/scrape/google-jobs
updated: 2026-10-20
---

# Google Jobs scraper

The Zenrows Google Jobs scraper turns a Google Jobs search URL into structured JSON: job title, employer, location, source job board, listing link and listing ID for every card. You send the URL, Fetch in Adaptive Stealth Mode opens the page, and Extract returns the fields. Run it from an API call, the CLI or an AI agent.

- Site: google.com/jobs
- Returns: Job titles, employers, locations and source boards

## Run it: one Google Jobs search in, JSON out

```json Input
{
  "url": "https://www.google.com/search?q=data+engineer+jobs+near+new+york&udm=8",
  "mode": "auto",
  "extract": "auto",
  "proxy_country": "us"
}
```

```json Output
{
  "query": { "text": "data engineer jobs near new york" },
  "organic_results": [
    {
      "title": "Data Engineer 2",
      "source_name": "The Farmer's Dog",
      "displayed_url": "New York, NY • via LinkedIn",
      "url": "https://www.google.com/search?ibp=htl;jobs&q=data+engineer+jobs+near+new+york&htidocid=H286iP695WReUJ3DAAAAAA%3D%3D&hl=en-US…"
    },
    {
      "title": "Sr. Data Engineer (HYBRID)",
      "source_name": "Equinox",
      "displayed_url": "New York, NY • via Equinox Careers",
      "url": "https://www.google.com/search?ibp=htl;jobs&q=data+engineer+jobs+near+new+york&htidocid=XsPLmVnapu_P8az2AAAAAA%3D%3D&hl=en-US…"
    },
    {
      "title": "Data Engineer, Marketing Insights and Technology",
      "source_name": "Amazon Advertising LLC",
      "displayed_url": "New York, NY • via Indeed",
      "url": "https://www.google.com/search?ibp=htl;jobs&q=data+engineer+jobs+near+new+york&htidocid=Kt91Ombs2PDBKdUNAAAAAA%3D%3D&hl=en-US…"
    }
  ]
}
```

Request and output of a live call to a Google Jobs search on Oct 20, 2026, trimmed for length. A field is returned when the page shows it.

## What data can you scrape from Google Jobs?

Google Jobs collects postings from company career sites and job boards into one list, and labels each card with the employer, the location and the site it came from. The scraper returns each card as one record, in page order.

| Field | What it holds | Example |
|---|---|---|
| `query.text` | The search Google answered, with the role and place | `data engineer jobs near new york` |
| `organic_results[]` | One record per job card, in the order Google shows them | `{ "title": "Data Engineer 2", … }` |
| `organic_results[].title` | The job title as posted | `Data Engineer III - Digital and Technology Partners - Hybrid/Remote` |
| `organic_results[].source_name` | The hiring company | `Mount Sinai Health Systems` |
| `organic_results[].displayed_url` | Location and the board or careers site the listing comes from, separated by `• via` | `New York, NY • via ZipRecruiter` |
| `organic_results[].url` | The Google Jobs link that opens this listing's details | `https://www.google.com/search?ibp=htl;jobs&…&htidocid=…` |
| `htidocid` in `organic_results[].url` | Google's ID for the listing, the key to deduplicate on | `H286iP695WReUJ3DAAAAAA==` |
| `organic_results[].snippet` | A description excerpt, when the card shows one | `null` |
| `organic_results[].date_published` | The posting date, when the card shows one | `null` |

The board in `displayed_url` is often the useful part: in the sample, one search for data engineering roles in New York returned listings via LinkedIn, Indeed, ZipRecruiter, JobLeads and the employers' own careers sites, side by side. For one board in depth, use the [LinkedIn Jobs scraper](/scrape/linkedin-jobs) or the [Indeed scraper](/scrape/indeed); for regular web results, the [Google Search scraper](/scrape/google-search).

## How to scrape Google Jobs with Zenrows

1. **Create a free Zenrows account** and copy your API key from the dashboard.
2. **Build the jobs URL.** `https://www.google.com/search?q=` plus the role and place, then `&udm=8`, which opens Google's jobs view instead of the regular results. "data engineer jobs near new york" works better than a bare job title.
3. **Request from the job market's country.** Google answers for the searcher's location, so a request that lands in another country gets that country's results. For US roles, request from the US.
4. **Send it with the default configuration**: Adaptive Stealth Mode to open the page and Extract to return the fields. Read `parsed` from the response.
5. **Run every role and city in Batch**, one URL each, with an ID like `data-engineer-nyc` so results group cleanly.

```python Python
# pip install requests
import json
import requests

params = {
    "apikey": "YOUR_ZENROWS_API_KEY",
    "url": "https://www.google.com/search?q=data+engineer+jobs+near+new+york&udm=8",
    "mode": "auto",          # Adaptive Stealth Mode
    "extract": "auto",       # structured JSON
    "proxy_country": "us",   # Google Jobs for the US market
}
response = requests.get("https://api.zenrows.com/v1/", params=params, timeout=180)
response.raise_for_status()

data = response.json()["parsed"]
for job in data["organic_results"]:
    location, _, board = job["displayed_url"].partition(" • via ")
    print(job["title"], "|", job["source_name"], "|", location, "|", board)
```

```javascript Node.js
// Node 18+ (built-in fetch)
const params = new URLSearchParams({
  apikey: "YOUR_ZENROWS_API_KEY",
  url: "https://www.google.com/search?q=data+engineer+jobs+near+new+york&udm=8",
  mode: "auto",
  extract: "auto",
  proxy_country: "us",
});

const response = await fetch(`https://api.zenrows.com/v1/?${params}`, {
  signal: AbortSignal.timeout(180_000),
});
if (!response.ok) throw new Error(`${response.status}: ${await response.text()}`);

const { parsed } = await response.json();
for (const job of parsed.organic_results) {
  const id = new URL(job.url).searchParams.get("htidocid");
  console.log(id, job.title, job.source_name);
}
```

```bash cURL
curl "https://api.zenrows.com/v1/?apikey=YOUR_ZENROWS_API_KEY&url=https%3A%2F%2Fwww.google.com%2Fsearch%3Fq%3Ddata%2Bengineer%2Bjobs%2Bnear%2Bnew%2Byork%26udm%3D8&mode=auto&extract=auto&proxy_country=us"

# Only title, employer and location line
curl "https://api.zenrows.com/v1/?apikey=YOUR_ZENROWS_API_KEY&url=https%3A%2F%2Fwww.google.com%2Fsearch%3Fq%3Ddata%2Bengineer%2Bjobs%2Bnear%2Bnew%2Byork%26udm%3D8&mode=auto&extract=auto&proxy_country=us&extract_fields=organic_results.title%2Corganic_results.source_name%2Corganic_results.displayed_url"
```

```python Python SDK
# pip install zenrows
from zenrows import ZenRowsClient

client = ZenRowsClient("YOUR_ZENROWS_API_KEY")
response = client.get(
    "https://www.google.com/search?q=data+engineer+jobs+near+new+york&udm=8",
    params={"mode": "auto", "extract": "auto", "proxy_country": "us"},
)
for job in response.json()["parsed"]["organic_results"]:
    print(job["source_name"], job["title"])
```

## Input

The scraper takes one Google Jobs URL per request. The role and place live in the query; the options below control how Zenrows opens the page. In a Batch job they apply to every task.

| Parameter | Type | Default | Description |
|---|---|---|---|
| `url` | string | required | A Google search URL with `udm=8`: `https://www.google.com/search?q=<role>+jobs+near+<place>&udm=8` |
| `mode` | string | `auto` | Adaptive Stealth Mode: starts with the lightest setup and adds rendering and protected-site handling only when the page needs them |
| `extract` | string | `auto` | Returns the job cards as JSON in `parsed`, next to the raw `html` |
| `extract_fields` | string | all fields | Comma-separated paths to keep, such as `organic_results.title,organic_results.source_name` |
| `proxy_country` | string | any | Two-letter country code of the job market. Google Jobs answers with that country's listings |
| `wait_for` | string | none | A CSS selector to wait for before the page is read |
| `session_id` | integer | none | Keep the same session across several requests, up to 10 minutes |

```json Batch input
{
  "type": "regular",
  "status": "closed",
  "zenrows_params": {
    "mode": "auto",
    "extract": "auto",
    "proxy_country": "us"
  },
  "tasks": [
    { "url": "https://www.google.com/search?q=data+engineer+jobs+near+new+york&udm=8", "external_id": "data-engineer-nyc" },
    { "url": "https://www.google.com/search?q=data+engineer+jobs+near+austin+tx&udm=8", "external_id": "data-engineer-austin" },
    { "url": "https://www.google.com/search?q=machine+learning+engineer+jobs+near+new+york&udm=8", "external_id": "ml-engineer-nyc" }
  ]
}
```

## Output

Every response is JSON with two keys: `html`, the page as Google served it, and `parsed`, the structured list of jobs. The sample is trimmed to four listings from a real search.

```json Job results
{
  "query": { "text": "data engineer jobs near new york" },
  "organic_results": [
    {
      "title": "Senior Data Solutions Engineer",
      "source_name": "1-800-FLOWERS.COM, INC.",
      "displayed_url": "New York, NY • via LinkedIn",
      "snippet": null,
      "date_published": null,
      "url": "https://www.google.com/search?ibp=htl;jobs&q=data+engineer+jobs+near+new+york&htidocid=wkUV0Ay-JWLd8yfeAAAAAA%3D%3D&hl=en-US…"
    },
    {
      "title": "Data Engineer, Infrastructure",
      "source_name": "Aaru",
      "displayed_url": "New York, NY • via ZipRecruiter",
      "snippet": null,
      "date_published": null,
      "url": "https://www.google.com/search?ibp=htl;jobs&q=data+engineer+jobs+near+new+york&htidocid=DLgEwN6Dpsdjf35pAAAAAA%3D%3D&hl=en-US…"
    },
    {
      "title": "Remote-First Staff Data Engineer: End-to-End Data Stack",
      "source_name": "Moxie",
      "displayed_url": "Anywhere • via JobLeads",
      "snippet": null,
      "date_published": null,
      "url": "https://www.google.com/search?ibp=htl;jobs&q=data+engineer+jobs+near+new+york&htidocid=88UfpmgGGSFLEzLtAAAAAA%3D%3D&hl=en-US…"
    },
    {
      "title": "Data Engineer II, Analytics & Modeling",
      "source_name": "CLEAR - Corporate",
      "displayed_url": "New York, NY • via Indeed",
      "snippet": null,
      "date_published": null,
      "url": "https://www.google.com/search?ibp=htl;jobs&q=data+engineer+jobs+near+new+york&htidocid=l1Msrj1oU1tNTOIGAAAAAA%3D%3D&hl=en-US…"
    }
  ]
}
```

Split `displayed_url` on `• via` to get the location and the board as two columns, and read `htidocid` from `url` as the listing's key. Remote roles show `Anywhere` as the location. Need a spreadsheet? Load `organic_results` into pandas with `pd.json_normalize(...)` and write CSV. Need the page for an LLM? Ask for Markdown instead of JSON.

## Scrape Google Jobs from your coding agent

Claude Code, Cursor, Codex and any MCP client can search Google Jobs and sort the listings from a prompt.

The agent sends the same Fetch and Extract request a developer would and reads the job cards as JSON. Connect the Zenrows MCP server once, or install the CLI and let the agent call it in the terminal.

```bash Connect the MCP server (Claude Code)
claude mcp add zenrows -e ZENROWS_API_KEY=YOUR_ZENROWS_API_KEY -- npx -y @zenrows/mcp
```

```json MCP config (Cursor, Claude Desktop, Windsurf)
{
  "mcpServers": {
    "zenrows": {
      "command": "npx",
      "args": ["-y", "@zenrows/mcp"],
      "env": { "ZENROWS_API_KEY": "YOUR_ZENROWS_API_KEY" }
    }
  }
}
```

```bash Or run the CLI
npx -y @zenrows/cli init
zenrows extract "https://www.google.com/search?q=data+engineer+jobs+near+new+york&udm=8" --proxy-country us
```

```text Then ask your agent
Use Zenrows to search Google Jobs from the US for "data engineer jobs near
new york" and "analytics engineer jobs near new york". Return one table
with title, company, location and the board each listing comes from,
and count how many listings each board supplies.
```

## Scrape Google Jobs at scale with Batch and Crawl

**Batch** runs a grid of roles and cities as one job: one URL per pair, the country set once, and your own ID on each result. Recruiting analytics teams run it daily and key every listing on `htidocid`, so a listing seen on several days or in several searches is counted once.

**Crawl** follows the links from a start URL and returns every page it reaches on that site. On Google Jobs, start it on a jobs search and keep links that contain `ibp=htl;jobs`, which are the listing links on the page. When you need the original postings, request the employer or board URLs directly with Fetch, or use the board's own scraper.

```bash Batch (cURL)
curl -X POST "https://async.api.zenrows.com/v1/jobs" \
  -H "X-API-Key: YOUR_ZENROWS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "regular",
    "status": "closed",
    "zenrows_params": { "mode": "auto", "extract": "auto", "proxy_country": "us" },
    "tasks": [
      { "url": "https://www.google.com/search?q=data+engineer+jobs+near+new+york&udm=8", "external_id": "data-engineer-nyc" },
      { "url": "https://www.google.com/search?q=data+engineer+jobs+near+austin+tx&udm=8", "external_id": "data-engineer-austin" }
    ]
  }'

# Read the results when the job finishes
curl "https://async.api.zenrows.com/v1/jobs/JOB_ID/results?status=successful" \
  -H "X-API-Key: YOUR_ZENROWS_API_KEY"
```

```bash Batch (CLI)
# searches.jsonl: one {"url": "https://www.google.com/search?q=...&udm=8", "external_id": "...", "extract": "auto"} per line
zenrows batch create searches.jsonl --proxy-country us --wait
zenrows batch results <job-id> --out google-jobs.jsonl
```

```bash Crawl (cURL)
curl -X POST "https://api.zenrows.com/v1/crawls" \
  -H "X-API-Key: YOUR_ZENROWS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://www.google.com/search?q=data+engineer+jobs+near+new+york&udm=8",
    "depth": 1,
    "max_pages": 50,
    "include_patterns": ["ibp=htl;jobs"]
  }'
```

**Scheduled jobs** run the same Batch job on a timetable, on Zenrows' side: every few hours, once a day, at set times on chosen days, or once at a future date. Each run keeps its own results, a webhook can tell your app when a run finishes, and you can pause or resume the schedule at any time, with no cron server to keep alive.

```bash Scheduled job (cURL)
# Runs every day on Zenrows' side; each run keeps its own results
curl -X POST "https://async.api.zenrows.com/v1/jobs" \
  -H "X-API-Key: YOUR_ZENROWS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
      "type": "scheduled",
      "status": "closed",
      "schedule": {
        "rate": {
          "every": 1,
          "unit": "day"
        }
      },
      "zenrows_params": {
        "mode": "auto",
        "extract": "auto",
        "proxy_country": "us"
      },
      "tasks": [
        {
          "url": "https://www.google.com/search?q=data+engineer+jobs+near+new+york&udm=8",
          "external_id": "google-jobs"
        }
      ]
    }'

# Or at set times: "schedule": { "calendar": { "times_of_day": ["09:00"],
#   "cadence": { "weekly": { "days": ["mon", "thu"] } } }, "timezone": "Europe/Madrid" }
```

## What teams do with Google Jobs data

| Use case | Data they pull | Who runs it |
|---|---|---|
| Labor market analysis | Listing counts per role, city and employer, tracked daily | Economists, analysts and workforce planners |
| Hiring signals for sales | Companies posting roles that point to a buying need, such as data engineering hires | Sales, RevOps and account research teams |
| Competitor hiring | Titles and locations a rival is hiring for, as a read on its plans | Strategy and competitive intelligence teams |
| Job aggregation | Listings from many boards in one feed, keyed on the Google listing ID | Job sites, niche boards and career apps |
| Board coverage audits | Which boards and careers sites supply listings for each search | Job board operators and recruitment marketers |
| Job search agents | Live listings an agent can filter and summarize for a candidate | AI and product teams |

## Tips for scraping Google Jobs

- **Always add `udm=8`.** It opens the jobs view. Without it, the same query returns regular web results with job board pages mixed in.
- **Put the place in the query.** "jobs near new york" or "jobs in austin tx" sets the area Google searches, and the request country sets the market. Use both.
- **Key on `htidocid`.** The listing ID in the `url` identifies the job, not its position on the page. Use it to deduplicate across days and queries.
- **Split the location line.** `displayed_url` reads like `New York, NY • via Indeed`. Splitting on `• via` gives you a location column and a source column.
- **Cut wide searches into narrow ones.** One page shows a limited set of cards, so run several specific queries (role by city, or role by seniority) instead of one broad one.
- **Store the run date.** Cards often carry no posting date, so the first day you see an `htidocid` is your best record of when it appeared.

## Frequently asked questions

### Is it legal to scrape Google Jobs?

Collecting public job listings from Google Jobs, such as titles, employers and locations shown to every visitor, is common practice for labor market research and job aggregation. Listings rarely hold personal data, but when they do, laws such as the GDPR and the CCPA apply, so keep only what you have a legitimate reason to use. Check Google's terms and the source boards' terms for your use case, and ask your lawyer if you are unsure.

### Is there an official Google Jobs API?

No. Google offers no API for the job listings it shows in Search. Employers get into Google Jobs by adding JobPosting structured data to their own pages, and Google Cloud Talent Solution is a search API for a company's own job inventory, not for Google's results. To read the listings Google shows, teams scrape the jobs view.

### What Google Jobs pages can I scrape?

Google search pages in the jobs view ( google.com/search?q=…&udm=8 ), for any role and place, in any country Google Jobs serves. Each request returns the job cards on the page with title, employer, location, source and the listing link.

### Where do Google Jobs listings come from?

From company careers sites and job boards. Google shows the source on each card, and the scraper returns it in displayed_url after • via : LinkedIn, Indeed, ZipRecruiter, JobLeads and employers' own careers pages all appeared in one New York search.

### How do I scrape Google Jobs with Python?

Send a GET request to https://api.zenrows.com/v1/ with your API key, the jobs URL, Adaptive Stealth Mode and Extract, and the country of the job market, as in the Python example above. parsed["organic_results"] is a list of dictionaries, one per job. The zenrows package on PyPI wraps the same request.

### How many jobs can I scrape?

One request returns the cards on one jobs page. For more, split the search into roles, cities and seniority levels, and send all of those URLs in one Batch job, which accepts more than 10,000 URLs.

### Can AI agents scrape Google Jobs with Zenrows?

Yes. Connect the Zenrows MCP server to Claude, Cursor, Codex or any MCP client, or give your agent the Zenrows CLI. Describe the role and place, and the agent calls Fetch and Extract, then filters the listings for you.

### Can I scrape Google Jobs on a schedule?

Yes. Create a scheduled Batch job with your Google Jobs URLs and the same settings as a single request, and Zenrows runs it on its side: every few hours, once a day, at set times on chosen days, or once at a future date. Each run keeps its own results, a webhook can notify your app when a run finishes, and the schedule can be paused or resumed at any time.

### Which integrations work with the Google Jobs scraper?

Zenrows works with n8n, Make, Zapier, Clay, Dify, LangChain, LlamaIndex, CrewAI and Mastra, plus the Python, Node.js and Go SDKs and any HTTP client. A Clay table can flag accounts that are hiring for a role, and a daily workflow can append new listings to a sheet. See the integrations page for the full list.

### What format does the data come in?

JSON by default, with the job cards in parsed and the original page in html . You can also ask for the page as Markdown or plain text for an LLM to read.

All scrapers: https://www.zenrows.com/scrape. Start free: https://app.zenrows.com/register. Docs: https://docs.zenrows.com
