Google Jobs Scraper - Aggregated Postings + Apply Links avatar

Google Jobs Scraper - Aggregated Postings + Apply Links

Pricing

$2.49 / 1,000 job postings

Go to Apify Store
Google Jobs Scraper - Aggregated Postings + Apply Links

Google Jobs Scraper - Aggregated Postings + Apply Links

Pricing

$2.49 / 1,000 job postings

Rating

0.0

(0)

Developer

Lantern Lab

Lantern Lab

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

4 days ago

Last modified

Categories

Share

Google Jobs Scraper

Give it job search terms and a place, and get Google Jobs' public postings back: the title, the company, the location, the site Google aggregated the posting from, the posting date, the salary and employment type Google shows, the description from its detail panel, and every apply link. No Google login, no cookies, no account of your own. $2.49 per 1,000 job postings, no start fee. See Pricing.

Input:

{
"queries": ["software engineer", "data analyst"],
"location": "New York, NY",
"country": "us",
"language": "en",
"maxJobs": 10
}

Output — one row per posting (recordType: "job"); per-term status rows and the run summary are free. Sample from a real run; Google's opaque identifiers and the description shortened:

{
"recordType": "job",
"jobId": "1X83OtFWneacwscFAAAAAA==",
"title": "Software Engineer, Agentic",
"companyName": "Uber",
"location": "New York, NY",
"via": "Built In NYC",
"postedAtText": "3 days ago",
"postedAt": "2026-10-21T13:04:11.000Z",
"postedAtEstimated": true,
"salaryText": "$171K–$190K a year",
"jobType": "Full-time",
"description": "About the Role. Agentic Pods is building a new model for how Engineering partners…",
"descriptionTruncated": false,
"applyLinks": [
{ "title": "Apply on Built In NYC", "url": "https://www.google.com/goto?url=CAESzAEB6zswFc7obAdOABwiv3E…" },
{ "title": "Apply on Indeed", "url": "https://www.google.com/goto?url=CAESvQEB6zswFTPynKCUVlncPtw…" }
],
"url": "https://www.google.com/search?q=software+engineer+New+York%2C+NY&ibp=htl;jobs&hl=en&gl=us&htidocid=1X83OtFWneacwscFAAAAAA%3D%3D",
"query": "software engineer",
"searchLocation": "New York, NY",
"rank": 1,
"language": "en",
"country": "us",
"partial": false,
"partialReason": null,
"collectedAt": "2026-10-24T13:04:11.942Z"
}

Limits:

  • Public data only. The postings Google shows on its own jobs panel; no login, no cookies, no candidate or recruiter data. Google serves ten postings per search term and offers no way to page past them, so coverage grows with more search terms and more locations, not with a higher maxJobs.
  • Every search term gets a status row saying what happened. A term Google answered with nothing is an empty result; a term cut short by a limit, a refusal or an unrendered page is partial with the reason — the two are never confused.
  • Your limits stop collection and appear in the row: maxJobs, the maxUpstreamRequests request ceiling and the maximum charge per run (see Pricing).

What it does

  • Searches each term separately, in the place you give, and labels every row with the term that produced it, so one run can cover many roles and cities.
  • Returns every apply link Google lists for a posting — often six or more sites — with Google's own label for each.
  • Deduplicates across the whole run: a posting that matches several of your terms is written once and charged once.
  • Keeps the source identifier (jobId), the platform's own posting date (postedAtText exactly as Google words it, plus postedAt as an ISO estimate) and our collection time (collectedAt) as separate fields.
  • Reads the posted/salary/employment-type chips by their icon rather than by their English label, so a German or Russian search fills the same fields as an English one.
  • Says what happened per search term. A term Google answered with nothing is recorded as an empty result; a term that was cut short by a limit, a refusal or a page Google would not render is marked partial: true with the reason.
  • Caps itself: maxUpstreamRequests is a hard ceiling on requests to Google for the whole run, derived from your input when you leave it empty, so a run is never unbounded.
  • Charges only posting rows. Empty results, per-term status rows and the run summary are free.

What it does not do

  • No paging past the ten postings Google serves per search term — there is no such page to fetch.
  • No employer-site scraping: the apply links are Google's own redirects, and the description is the one Google shows on its panel.
  • No candidate, recruiter or applicant data, and no contact details beyond what the employer published inside the posting text.
  • No other job board scraped directly, no login-only listings, no scheduling or monitoring built in (use Apify schedules).

Input

FieldDefaultMeaning
queriesrequiredOne job search term per line, for example software engineer. Each is searched separately.
locationemptyCity, region or country, for example New York, NY. Google Jobs has no separate location field, so this is appended to each term — which is how its own location filter is set.
countryusTwo-letter country code of the Google edition to search (gl): us, de, ru, gb.
languageenInterface language of the search (hl). With en the posting date is also returned as an ISO estimate.
maxJobs10Upper bound on rows per search term. Google serves ten per term, which is also the maximum here.
maxUpstreamRequestsderivedHard ceiling on requests to Google for the whole run. Leave empty and the Actor works one out from your terms.
proxyConfigurationApify Proxy, GOOGLE_SERPThe proxy the requests go through. Keep the default — see Why the Google SERP proxy.

Output

One row per posting, recordType: "job":

FieldMeaning
jobIdGoogle's own identifier for the posting (htidocid). The dedupe key.
titleThe job title as Google shows it.
companyNameThe employer, as Google names it.
locationThe location as Google shows it on the card.
viaThe site Google aggregated the posting from, for example Indeed, Stepstone, hh.ru.
postedAtTextThe posting date exactly as Google words it, for example 3 days ago, vor 7 Tagen.
postedAtThe same date as an ISO 8601 instant, converted from that wording, so an estimate to within the unit Google used. null when the wording is in a language this Actor does not convert. Google publishes no exact timestamp.
postedAtEstimatedtrue whenever postedAt is set, as a reminder that it is derived, not published.
salaryTextThe salary as Google shows it, with the currency symbol it spells out in the chip's label, or null when the posting has none. Never derived.
jobTypeThe employment type as Google shows it, for example Full-time, Vollzeit.
descriptionThe description text from Google's detail panel.
descriptionTruncatedtrue when Google only previewed the description instead of showing all of it, which it does in some countries. The full text is then on the employer's site behind an apply link.
applyLinksEvery apply link Google lists: title is its label for the site, url is Google's apply redirect.
urlThe Google Jobs page for this posting, openable in a browser.
query, searchQuery, searchLocationThe term you gave, what was asked of Google, and the location.
language, countryThe edition that was searched.
rankPosition in that term's results, counting from 1.
partial, partialReasonAlways false / null on a posting row: the row exists because the posting was collected.
collectedAtOur own collection time, separate from the posting date.

One row per search term, recordType: "query_status", never charged:

FieldMeaning
jobsReturned / jobsChargedRows this term wrote and charged.
duplicatesSkippedPostings an earlier term in the run had already collected.
jobsAvailableHow many postings the page held, before your own limits.
serpLimitReachedtrue when Google's own ten-per-term ceiling, not your maxJobs, ended the list.
malformedJobsCards on the page that carried no Google identifier, so they were not written rather than written half-read.
partial / partialReasontrue with upstream_request_cap, charge_limit_reached, blocked, page_not_rendered, upstream_error or not_collected when the term's results are incomplete.
statusTextThe same thing in a sentence, so the dataset explains itself.

The last row is recordType: "RUN_SUMMARY" — postings collected and charged, empty terms, partial terms by reason, terms that hit Google's ceiling, dates converted, descriptions previewed, requests sent, bytes moved, and why collection stopped. The same object is also in the run's key-value store under RUN_SUMMARY.

Partial is not empty

jobsReturned: 0 with partial: false means Google has no posting for that term and place. partial: true means the term's results are incomplete and there may well be more. A run that collected nothing and had partial terms fails rather than reporting an empty success.

Why the Google SERP proxy

Google answers a request for its jobs panel from a datacenter or residential IP with a short page whose only content asks for JavaScript — no postings, no captcha, HTTP 200. Reading that as "no jobs" would be wrong, so this Actor detects it and reports partialReason: "page_not_rendered" instead. The Apify Google SERP proxy group returns the rendered page, which is why it is the default. Leave it on.

Limits and boundaries

  • Ten postings per search term is Google's ceiling, verified for us, de and ru: a request for the second page returns a rendered page with no postings at all, and filtering by posting date returns the same postings again. Use more terms and more locations instead.
  • postedAt is converted from Google's relative wording and is therefore an estimate; postedAtText is the unaltered source value. English wording is converted; other languages keep postedAt: null rather than a guess.
  • In some countries Google renders only a preview of the description. Those rows carry descriptionTruncated: true.
  • The apply links are Google's own redirects, because Google does not render the employer's URL anywhere on the page.
  • Public postings only. No login, no cookies, no account. Personal data is not collected; a contact address inside a posting is text the employer published.

Pricing

Pay per event, no start fee:

EventPriceCharged for
result$0.00249 ($2.49 per 1,000)Each job posting row written. Per-term status rows, empty results and the run summary are free.

Set a maximum charge for the run and the Actor stops before going over it.