Playground Sign in Start free
← All posts

How to Scrape Google Jobs with Python: Filters, Radius and Remote Jobs

To scrape Google Jobs, send a GET request to the Ujeebu SERP API with search_type=jobs and a search term; you get structured job listings as JSON, no headless browser required. A Playwright scraper that worked last week can return an empty page today because Google quietly restructured its jobs widget. The fix is not more parsing logic. It is a dedicated jobs search type that returns structured data directly. By the end of this tutorial you will have working Python code that fetches Google Jobs listings with every filter the API supports, know the exact credit cost per request, and be able to look up a single job by its ID.

How to Scrape Google Jobs with the Ujeebu SERP API

Google Jobs is a structured job-listing surface embedded in Google Search, and it is meaningfully different from a standard organic result. A regular SERP result gives you a title, a URL and a snippet. A Google Jobs result gives you job-specific fields: the job title, the hiring company, a location, a description, the posting date, the employment type, and one or more application links pointing to the original source (the company's careers page, LinkedIn, Indeed, and so on).

Because the data is structured at the source, a SERP API can return it as clean JSON without any CSS selector parsing on your side. That is the key difference from scraping Google's HTML directly. When Google changes the DOM layout of its jobs widget, your pipeline keeps working because the API handles the rendering and extraction. Think of it like the difference between calling a bank's API for your balance versus screen-scraping the bank's website: the API gives you a stable contract, the website gives you a fragile dependency on layout.

Every SERP request through the Ujeebu SERP API costs 5 credits. That rate applies regardless of which search_type you use. See the SERP API docs for the full list of supported search_type values and the response schema for each.

Takeaway: If your workflow needs job titles, companies, locations, descriptions and application links as JSON without managing a browser, the SERP API's jobs search type is the right tool.

Setting Up: Install Requests and Get Your API Key

The only Python dependency is requests. No Playwright, no Selenium, no browser binaries.

pip install requests

Sign up for a free trial at ujeebu.com/pricing: 5,000 credits, no credit card required. At 5 credits per SERP request, that is up to 1,000 requests to explore the API and validate your pipeline before committing to a plan.

Store your key in an environment variable rather than hardcoding it:

export UJEEBU_API_KEY="your_key_here"

Before making any SERP call, verify the key works and check your remaining balance:

import os
import requests

API_KEY = os.environ["UJEEBU_API_KEY"]

resp = requests.get(
    "https://api.ujeebu.com/account",
    headers={"ApiKey": API_KEY},
)
resp.raise_for_status()
account = resp.json()
print(f"Plan: {account['plan']}, Credits used: {account['used']}/{account['quota']}")

The /account endpoint returns plan, quota, used, used_percent, balance and next_billing_date. Build a simple guard around this before your pipeline runs so you know your starting balance and can stop before exhausting the quota.

Takeaway: One pip install and a free trial key are all you need. Check /account first so you know your starting balance.

Try it yourself

Run this on your own pages

Get a free API key and make your first call in minutes. 5,000 free credits for 7 days, no card.

Basic Google Jobs Query in Python

The minimal request needs three things: your API key in the ApiKey header, search_type=jobs, and a search term.

import os
import requests

API_KEY = os.environ["UJEEBU_API_KEY"]

params = {
    "search": "python engineer",
    "search_type": "jobs",
}

resp = requests.get(
    "https://api.ujeebu.com/serp",
    headers={"ApiKey": API_KEY},
    params=params,
)
resp.raise_for_status()
data = resp.json()

# Inspect the top-level keys to understand the response structure
print(data.keys())

# Iterate over whatever listings key your query returns
# Verify the exact key name in the Playground before hardcoding it
for job in data.get("jobs", []):
    print(job)

A successful response is billed at 5 credits. Failed requests are not billed, so a burst against a temporarily unavailable endpoint does not drain your quota.

The best way to understand the exact response structure for your search term is to run the query in the Playground first. The Playground shows the live JSON, so you can confirm which key holds the listings array and what fields each listing contains before you write a parser. Different queries can surface different field sets, which is why inspecting data.keys() and a sample listing object before hardcoding field names is a good habit.

Also store the search term, the location and a timestamp alongside each batch of results. That metadata lets you compare datasets collected at different times or across different cities, which is the foundation of any hiring-market research workflow.

Takeaway: Three parameters get you a working pipeline. Inspect the live JSON in the Playground before hardcoding field names.

Filtering Jobs: Location, Radius, Remote, Date and Employment Type

Filters are where the SERP API earns its keep for production use cases. You can layer all of them in a single request, which means one 5-credit call can replace what would otherwise be a multi-step scraping workflow.

  • location: Scopes results to a geographic area. The SERP API docs list the accepted formats; test your specific city or region string in the Playground to confirm it resolves correctly. More specific queries produce cleaner datasets: a search for "data analyst jobs in Austin" is easier to interpret than "data analyst jobs" with no location.
  • radius: A numeric parameter that widens or narrows the geographic ring around the location. Useful when a city name alone returns too few results or pulls in listings from distant areas you do not want. See the docs for units and accepted values.
  • remote: A boolean that restricts results to remote-flagged listings. Combine with a location to find remote jobs posted by companies in a specific region, or omit location entirely for global remote listings.
  • date_posted: Controls recency. Check the SERP API docs for the accepted values and test your chosen value in the Playground to confirm the coverage it gives for your query. A tight recency filter is the simplest way to build a near-real-time job feed.
  • employment_type: Filters by contract type. The accepted values are listed in the SERP API docs. Pass one value per request and verify the results in the Playground before relying on it in production.

Here is a request that combines all five filters:

import os
import requests

API_KEY = os.environ["UJEEBU_API_KEY"]

params = {
    "search": "python engineer",
    "search_type": "jobs",
    "location": "Austin, TX",
    "radius": 50,
    "remote": True,
    "date_posted": "week",          # check /docs/apis/serp for accepted values
    "employment_type": "FULLTIME",  # check /docs/apis/serp for accepted values
}

resp = requests.get(
    "https://api.ujeebu.com/serp",
    headers={"ApiKey": API_KEY},
    params=params,
)
resp.raise_for_status()
data = resp.json()

# Inspect the response structure before parsing
print(data.keys())
for job in data.get("jobs", []):
    # Confirm field names against your live response in the Playground
    print(job.get("title"), "|", job.get("company_name"), "|", job.get("location"))

Layer filters from broad to narrow: start with search and location, add date_posted to cut noise, then add employment_type and remote only when your use case requires them. Each additional filter reduces result volume, so verify counts at each step.

Note that different providers use different field names for the same data. One API may return company_name, another may return company. One may use jobs_results, another job_results. The Playground shows you exactly what Ujeebu returns for your query, so you are not guessing at field names when you write your parser.

Takeaway: A single request can carry all five filters. Start broad, tighten incrementally, and verify field names in the Playground before writing a parser.

Batch Requests: More Results in One Call

By default, a Google Jobs request returns a limited number of listings. Set batch=True and increase results_count to collect more results in a single API call rather than making multiple paginated requests.

import os
import requests

API_KEY = os.environ["UJEEBU_API_KEY"]

params = {
    "search": "data engineer",
    "search_type": "jobs",
    "location": "New York, NY",
    "batch": True,
    "results_count": 40,
}

resp = requests.get(
    "https://api.ujeebu.com/serp",
    headers={"ApiKey": API_KEY},
    params=params,
)
resp.raise_for_status()
data = resp.json()
jobs = data.get("jobs", [])
print(f"Jobs returned: {len(jobs)}")

Each SERP request costs 5 credits regardless of results_count. Without batch=True, collecting the same number of results requires multiple paginated requests at 5 credits each. Batching reduces the total number of API calls, which matters when you are running a nightly pipeline across dozens of search terms.

The trade-off is latency: a batch request takes longer to complete because the API collects results across pages before responding. If your pipeline is latency-sensitive and the default result count is sufficient, skip batch. If latency matters but you still need more results, consider parallelizing queries across your plan's allowed concurrent requests. You can read your current concurrent_requests limit from the /account endpoint.

Takeaway: Use batch=True when you need more results than the default. It reduces the number of requests and therefore the total credit spend for a given dataset.

Looking Up a Single Job by ID

The SERP API accepts a job_id parameter that lets you request details for a specific listing. If your results include an identifier for a listing, pass it via job_id to fetch the full detail for that posting. This pattern is useful when your pipeline first collects listings in a broad sweep and then enriches only the ones that pass a downstream filter, for example, only jobs that mention a salary range or that match a specific seniority level.

import os
import requests

API_KEY = os.environ["UJEEBU_API_KEY"]

# Step 1: collect listings and inspect the response for an identifier
params = {
    "search": "machine learning engineer",
    "search_type": "jobs",
    "location": "San Francisco, CA",
}
resp = requests.get(
    "https://api.ujeebu.com/serp",
    headers={"ApiKey": API_KEY},
    params=params,
)
resp.raise_for_status()
data = resp.json()

# Inspect the response to find the identifier field name for your query
print(data.keys())
jobs = data.get("jobs", [])

if not jobs:
    print("No listings returned.")
else:
    # Confirm the identifier field name in the Playground before hardcoding
    first_job = jobs[0]
    job_id = first_job.get("job_id", "")
    print(f"Looking up job_id: {job_id}")

    if job_id:
        # Step 2: fetch the full detail for that listing
        detail_resp = requests.get(
            "https://api.ujeebu.com/serp",
            headers={"ApiKey": API_KEY},
            params={"job_id": job_id, "search_type": "jobs"},
        )
        detail_resp.raise_for_status()
        detail = detail_resp.json()

        # Print the full response to understand the detail schema
        print(detail.keys())
        # Then access the fields your query returns
        print(detail.get("title"))
        print(detail.get("company_name"))
        for option in detail.get("apply_options", []):
            print(option.get("title"), "->", option.get("link"))

Each lookup costs the same 5 credits as a listing request. Verify the exact field names in the Playground for your specific query before writing a production parser. The job_id parameter is documented in the SERP API reference.

Takeaway: Collect identifiers in batch, filter in Python, then look up only the listings your downstream logic actually needs. Credit spend stays proportional to the data you use.

Pricing and Credit Math for Job Scraping at Scale

Every SERP request costs 5 credits. The math is simple: divide your monthly credit quota by 5 to get your monthly request capacity.

Plan Monthly price Credits SERP requests per month
Starter $49 300,000 60,000
Growth $119 1,000,000 200,000
Pro $299 3,000,000 600,000
Business $999 15,000,000 3,000,000

At 5 credits per request, the cost per 1,000 Google Jobs requests ranges from $0.82 on the Starter plan down to $0.33 on the Business plan. Annual billing saves 10 percent across all plans. Failed requests are not billed, so a burst of requests against a temporarily unavailable endpoint does not drain your quota.

For context on how this compares with alternatives: ScraperAPI charges 25 credits per SERP request on its plans, which means a Google Jobs query costs five times more credits per call than the equivalent call on Ujeebu at the same plan tier. SerpAPI uses a separate credit system where each search costs 1 credit, but its largest public plan tops out at 30,000 credits per month, making high-volume job scraping significantly more expensive at scale.

For a continuous job-data pipeline, a monthly plan gives the lowest cost per request. For periodic research such as a quarterly hiring-market report, the free trial's 5,000 credits are enough to prototype the full pipeline before you decide on a plan. You can also use top-up packs on any paid plan for burst usage; packs stay valid for varying periods depending on size, so they suit seasonal hiring spikes without locking you into a higher monthly tier.

Takeaway: Divide your target monthly request volume by 5 to find the right plan. Use top-up packs for seasonal spikes rather than upgrading your base plan.

Use Cases: What to Build with Google Jobs Data

The structured JSON that comes back from a Google Jobs query is the input layer for several practical workflows.

  • Hiring market research: Run the same search term across multiple cities and compare listing counts over time. Which markets have the most open data engineering roles this quarter versus last? A simple loop across a list of cities, stored with a timestamp, answers that question without any manual searching.
  • Competitor hiring intelligence: Track which roles a competitor is posting and how frequently. A weekly batch job that searches for a company name plus a job category gives you a signal on where they are investing headcount.
  • Job board monitoring: Aggregate listings from Google Jobs alongside your own sources to catch postings that appear on Google but not on the boards you already monitor.
  • AI recruiting agent workflows: Google Jobs works well as a source discovery layer for an agent. The agent collects listings, filters by title or required skills, summarizes the requirements using a language model, and routes matching roles to candidates or recruiters. The structured JSON makes it straightforward to pass listings directly to a prompt without any HTML cleaning.

For any of these workflows, store the search term, location and a collection timestamp with each batch. That metadata is what lets you compare datasets across time and geography, which is where the analytical value lives.

Is Scraping Google Jobs Allowed? Legal Considerations

This is the question every team asks before going to production, and the honest answer is: it depends on which legal framework you are asking about.

On the contract side, Google's terms of service restrict automated access to its services. Using a SERP API does not make those terms disappear. Review the terms for any service you access programmatically and understand what you are agreeing to.

On the statutory side, the picture is more nuanced. The Ninth Circuit's 2022 ruling in hiQ Labs v. LinkedIn held that scraping publicly accessible data does not violate the Computer Fraud and Abuse Act. That ruling applies specifically to the CFAA and does not override a platform's contractual terms. It does, however, reduce the risk of criminal liability for scraping data that is publicly visible without authentication.

The practical upshot: most engineering and data teams proceed with a SERP API for research, competitive intelligence and internal tooling, accepting the terms-of-service risk as a business decision. If your use case is commercial and at scale, consult a lawyer familiar with data law in your jurisdiction. For a broader treatment of the legal landscape, see our post on whether web scraping is legal.

Takeaway: The CFAA risk is lower after hiQ v. LinkedIn, but terms-of-service risk remains. Understand both before going to production.

Where to Go Next

The SERP API docs list all supported search_type values and the parameters available for each. If you are already collecting job data, you can use the same API key and the same credit pool for other search types. The how to scrape Google search results post covers the web search type in depth, and the Google News tutorial shows the news type with the same Python patterns used here. For location-specific queries, the Google results for a specific city or country guide covers the location parameter in more detail.

Try your query in the Playground, inspect the live JSON to confirm field names, then start the free trial to run it in production: 5,000 credits, no credit card required.


FAQ

What is Google Jobs scraping?

Google Jobs scraping is the process of programmatically collecting structured job listing data from Google's jobs surface in search results. Instead of manually searching and copying listings, you send an authenticated API request and receive job titles, company names, locations, descriptions, posting dates and application links as JSON.

Is scraping Google allowed?

Google's terms of service restrict automated access to its services. The legal risk under the Computer Fraud and Abuse Act is lower after the Ninth Circuit's hiQ Labs v. LinkedIn ruling, but the contractual risk from the terms of service remains. Review the terms and consult counsel before running a production pipeline.

Is it legal to scrape job postings?

It depends on the jurisdiction and the specific site. The hiQ Labs v. LinkedIn ruling reduced CFAA risk for scraping publicly accessible data in the US, but it does not override a site's terms of service. For commercial use at scale, get legal advice specific to your situation.

Is it possible to scrape Google?

Yes. A SERP API handles the proxy rotation, rendering and anti-bot measures that make direct scraping of Google difficult. You send an authenticated API request and receive structured JSON. See the SERP API docs for supported search types.

How many results can I get in one Google Jobs API request?

Use batch=True with an increased results_count to collect more results than the default in a single request. Check the SERP API docs or test in the Playground to see what your specific query returns.

How do I filter Google Jobs results by remote work?

Add remote=True to your request parameters. You can combine it with a location to find remote jobs posted by companies in a specific region, or omit location entirely to get global remote listings. The employment_type parameter lets you further narrow by contract type; see the SERP API docs for accepted values.

Can AI agents use Google Jobs data?

Yes. The structured JSON that comes back from a Google Jobs query is straightforward to pass directly to a language model prompt without any HTML cleaning. A common pattern is to collect listings in batch, filter by title or required skills in Python, and then pass the filtered set to a model for summarization or candidate matching.

How much does it cost to scrape Google Jobs with Ujeebu?

Every SERP request costs 5 credits. On the Starter plan ($49 per month, 300,000 credits) that gives you up to 60,000 requests per month. The free trial includes 5,000 credits with no credit card required, enough for up to 1,000 requests to prototype your pipeline. See the full breakdown at ujeebu.com/pricing.

Try it yourself

Run this on your own pages

Get a free API key and make your first call in minutes. 5,000 free credits for 7 days, no card.

← Back to blog Read the docs →