How to Scrape Google for Jobs in 2026 (Python, No Official API)

By Serpent API Team · · 11 min read

Here is the thing nobody tells you up front: there is no official Google Jobs API. You cannot sign up, get a key, and pull job listings from Google in a clean, supported way.

Google had a hosted jobs API once. It was shut down in 2021. What is left is "Google for Jobs" — that big blue widget at the top of search results that gathers postings from across the web into one filterable box.

So if you are building a niche job board, tracking which roles are hiring, or assembling a dataset for a model, you have to get that data the hard way. This guide shows you the honest options, the traps in the DIY route, and a clean Python method that pulls Google for Jobs listings as structured data — across countries — into a CSV or SQLite file.

TL;DR: Google for Jobs has no official read API. You can build a fragile Selenium/Playwright scraper, pay a single-purpose third-party scraper, or call a SERP API that returns the Google for Jobs block as structured JSON. With Serpent, that jobs block surfaces inside the normal Google web search response — query it, parse the listings, and write them to a file. Use the country parameter to collect the same role in many markets. Starts at $0.60 per 10,000 calls with 10 free to try.

What Google for Jobs actually is

Google for Jobs is not a job board Google owns. It is an aggregation layer inside search.

When someone types "marketing jobs near me" or "remote python developer," Google shows a rich box above the normal results. Inside that box are job cards pulled from employer career pages, job boards, and staffing sites — anyone who publishes postings with valid JobPosting structured data.

Each card usually shows the job title, the company, the location, the posting source (the site Google found it on), and how long ago it was posted. Click a card and Google expands a detail panel with more fields and "apply" links back to the original sites.

That widget is what you are after. It is the closest thing to a free, cross-web jobs feed that exists — and it lives inside a regular Google search result page.

Why there is no official API

Google offers no API to read jobs out of its index. This is the single most important fact in this article.

The old Cloud Talent Solution job-search APIs were retired, and the consumer "Google for Jobs API" people imagine never existed as a public data feed. The only official jobs spec Google publishes is the JobPosting schema — and that flows the other way. It is how a publisher pushes their jobs into Google, not how you pull jobs out.

This trips up a lot of developers. They search "google jobs api," find the schema docs, and assume it is a data endpoint. It is not. There is no JSON endpoint you can hit with a key to get listings.

So every real-world solution comes down to one idea: read the Google for Jobs block from the search results page. The only question is how you do that without losing your weekends to maintenance.

Three ways to get the data (compared)

There are three honest routes, and they trade off effort, cost, and reliability differently.

Approach Effort to build Reliability Best for
DIY scraper (Selenium / Playwright) High — render JS, scroll, fight blocks Low — breaks on every Google HTML change Learning, one-off experiments, tiny volume
Single-purpose third-party jobs scraper Low — call their endpoint Medium — depends on that vendor's upkeep Teams that only ever need jobs and nothing else
SERP API (jobs block in the search response) Low — one HTTP call, structured JSON High — access and parsing are maintained for you Anyone who also wants organic, news, local data later

The DIY route is great for understanding the problem. It is a poor choice for anything you need to run on a schedule, because the maintenance never ends.

A SERP API is usually the sweet spot. You get the jobs block as clean JSON, and the same key also returns organic results, news, and local data, so you are not locked into a single narrow tool.

Why DIY scraping breaks

The DIY path looks easy in a tutorial and turns brutal in production. Here is exactly what goes wrong.

The jobs widget is JavaScript-rendered. A plain requests.get() to Google returns a near-empty shell. The job cards are drawn by JavaScript after load, so you need a full browser engine like Selenium or Playwright just to see them.

It uses lazy loading and scrolling. Only a few cards appear at first. The rest load as you scroll inside the widget. Your script has to simulate scrolling, then wait for the DOM to settle — and if it grabs the page mid-update, it captures half the data.

Selectors rot. Google's class names are obfuscated and change often. The CSS or XPath selector that works today silently returns nothing next month. We wrote a whole piece on why scrapers break at 3am — jobs widgets are a textbook case.

You get blocked. Repeated automated hits from one IP trigger CAPTCHAs and bans. Beating that means managing proxies and stealth — a project on its own. We covered the pattern in why proxies get banned by Google.

None of these is impossible. Together, they are a maintenance treadmill. You are not building a job board anymore; you are running an anti-bot arms race.

Skip the arms race. Serpent handles access, blocks and CAPTCHAs so you do not manage a proxy pool or a headless browser. You send a query, you get structured JSON back — including the Google for Jobs block where Google shows it. See the Google SERP API.

The SERP API method in Python

With a SERP API, getting jobs is one HTTP call. The Google for Jobs block surfaces inside Serpent's normal Google web search response, right next to the organic results.

To be clear and honest about what this is: Serpent returns the live Google for Jobs block from the search results page. It is not a private jobs database we host. You get the listings Google itself is showing — title, company, location, posting source, and posted date where those are present.

Here is the basic call. Get a key from your dashboard and send it in the X-API-Key header.

import requests

API_KEY = "sk_live_your_key"
BASE = "https://api.apiserpent.com/api/search"

def search_jobs(query, country="us"):
    resp = requests.get(
        BASE,
        headers={"X-API-Key": API_KEY},
        params={
            "q": query,
            "engine": "google",
            "country": country,
            "language": "en",
        },
        timeout=60,
    )
    resp.raise_for_status()
    return resp.json()

data = search_jobs("python developer jobs")
print(data["query"], "->", data["engine"], data["country"])

That returns the full Google search response as JSON. The job listings live in the jobs block of that response, alongside organic, peopleAlsoAsk, and the rest.

Field names can evolve, so rather than hard-coding a guess, point your parser at the jobs block and read the fields you need. Check the API docs for the exact key and shape in the response. The pattern below is deliberately defensive — it looks for a jobs-like collection and pulls common fields without assuming a rigid layout:

def extract_jobs(data):
    """Pull job listings out of the SERP response, defensively.

    The Google for Jobs block lives inside the search 'results'.
    Field names may evolve, so read the documented key from /docs
    and adapt the keys below to match the live response shape.
    """
    results = data.get("results", {})

    # The jobs block in the SERP response (see /docs for the exact field).
    jobs_block = results.get("jobs") or []

    rows = []
    for job in jobs_block:
        rows.append({
            "title":    job.get("title"),
            "company":  job.get("company") or job.get("companyName"),
            "location": job.get("location"),
            "source":   job.get("source") or job.get("via"),
            "posted":   job.get("posted") or job.get("postedAt"),
        })
    return rows

jobs = extract_jobs(data)
for j in jobs:
    print(j["title"], "@", j["company"], "-", j["location"])

Notice what you did not do: no browser, no scrolling script, no proxy rotation, no selector guessing. One request, structured data, done.

If you want to compare this approach to raw scraping in more detail, our Google search API vs scraping breakdown lays out the trade-offs with numbers.

Collecting jobs across countries

The Google for Jobs widget is localized, so the listings change per country. To build a global picture, send the same query once per market with the country parameter.

This is the single most useful trick for a hiring-trends dataset. The role "data engineer" surfaces very different companies and volumes in the US, the UK, and Germany.

COUNTRIES = ["us", "gb", "de", "ca", "au", "in"]

def collect_global(query):
    all_rows = []
    for cc in COUNTRIES:
        data = search_jobs(query, country=cc)
        for job in extract_jobs(data):
            job["country"] = cc
            all_rows.append(job)
    return all_rows

rows = collect_global("data engineer jobs")
print(f"Collected {len(rows)} listings across {len(COUNTRIES)} countries")

For supported codes, call GET /api/countries or read our walkthrough on tracking across 40+ countries. The same multi-country loop powers rank tracking, news monitoring, and job collection alike.

Storing it: CSV and SQLite

Once you have rows, save them. A CSV is perfect for a quick dataset; SQLite is better if you collect on a schedule and want to dedupe and query over time.

Here is the CSV version — clean enough to hand to an analyst or load into a spreadsheet:

import csv

def save_csv(rows, path="jobs.csv"):
    fields = ["title", "company", "location", "source", "posted", "country"]
    with open(path, "w", newline="", encoding="utf-8") as f:
        writer = csv.DictWriter(f, fieldnames=fields)
        writer.writeheader()
        for r in rows:
            writer.writerow({k: r.get(k, "") for k in fields})
    print(f"Wrote {len(rows)} rows to {path}")

save_csv(rows)

And the SQLite version, which makes it easy to run daily and skip duplicates you already have:

import sqlite3

def save_sqlite(rows, db="jobs.db"):
    con = sqlite3.connect(db)
    con.execute("""
        CREATE TABLE IF NOT EXISTS jobs (
            title TEXT, company TEXT, location TEXT,
            source TEXT, posted TEXT, country TEXT,
            UNIQUE(title, company, location, country)
        )
    """)
    for r in rows:
        con.execute(
            "INSERT OR IGNORE INTO jobs VALUES (?,?,?,?,?,?)",
            (r.get("title"), r.get("company"), r.get("location"),
             r.get("source"), r.get("posted"), r.get("country")),
        )
    con.commit()
    con.close()

save_sqlite(rows)

The UNIQUE constraint plus INSERT OR IGNORE means you can run the script every morning and only new postings get added. That is the backbone of a hiring-trends tracker.

Real use cases

Once jobs are flowing into a table, the use cases write themselves. Three are especially common.

1. A niche job aggregator. Pull a focused set of queries — say "remote rust developer" or "clinical trial nurse" — across a few countries, dedupe, and surface them on a tight, well-curated board. The Google for Jobs block does the cross-web gathering for you.

2. A "which roles are hiring" trends tracker. Collect the same queries daily and count listings over time. Rising counts signal demand; falling counts signal a cooling market. Pair it with the lead-generation patterns we covered to find companies that are actively hiring.

3. ML training data. Job titles, descriptions, and locations make solid training material for classifiers, salary models, and skills-extraction systems. A clean, repeatable collection pipeline beats a one-time messy dump every time.

All three are the same pipeline: query, extract the jobs block, store. You just change the queries and the schedule.

Scraping public search results is generally treated as lower risk than scraping data behind a login, but it is not a blanket yes.

The data in the jobs widget is public and shown to anyone who searches. That helps. But laws vary by country, terms of service matter, and how you store and reuse the data matters more. We go deeper in is scraping Google legal in 2026.

A few sensible guardrails: only keep the fields you actually need, avoid scraping personal data you have no use for, respect reasonable rates, and get legal advice before anything commercial or high volume. None of this is legal advice — it is just how careful teams operate.

Pull Google for Jobs data without the headache

Serpent returns the Google for Jobs block inside a normal Google search response as structured JSON — across 40+ countries, with blocks and CAPTCHAs handled for you. No proxy pool, no headless browser. 10 free searches on signup, from $0.60 down to $0.03 per 10,000 calls, no subscription.

Get Your Free API Key

Explore: Google SERP API · Docs · Pricing

FAQ

Is there an official Google Jobs API?

No. Google shut down its hosted jobs API in 2021. The JobPosting schema is for publishers adding jobs into Google's index, not for reading listings out. To get Google for Jobs data, you scrape the search results or use a SERP API that returns them as structured JSON.

Is scraping Google for Jobs legal?

Scraping public search results is generally treated as lower risk than scraping data behind a login, but it still depends on your jurisdiction and how you use the data. Read each site's terms, avoid personal data you don't need, and consult a lawyer for anything commercial or high volume.

Why does my Google for Jobs scraper keep breaking?

The jobs widget is JavaScript-rendered with lazy loading, so a simple request returns almost nothing. Google also changes its HTML often, which breaks CSS and XPath selectors, and blocks repeated automated traffic. A maintained SERP API absorbs all of that for you.

Can Serpent return Google for Jobs listings?

Yes. The Google for Jobs block surfaces inside Serpent's Google web SERP response, alongside organic results, so you can pull job title, company, location, posting source and posted date where Google shows them. See the docs for the exact field shape.

How do I collect jobs across multiple countries?

Send the same query once per country using the country parameter (for example country=us, country=gb, country=de). Each call returns that country's localized jobs block. Loop over a list of countries and append the rows into one CSV or database table.

How much does it cost to pull Google jobs data?

Serpent's Google search starts at $0.60 per 10,000 calls and drops to $0.03 per 10,000 at scale, with no subscription and 10 free searches on signup. Page depth does not multiply the price, so a deep call costs the same as a shallow one.