<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: krammer liu</title>
    <description>The latest articles on DEV Community by krammer liu (@krammer).</description>
    <link>https://dev.to/krammer</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4240475%2Ffda27e58-6bae-4d96-85c2-5366f528e671.jpg</url>
      <title>DEV Community: krammer liu</title>
      <link>https://dev.to/krammer</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/krammer"/>
    <language>en</language>
    <item>
      <title>I made Claude Code read job posts against my resume every morning (and it never clicks Apply)</title>
      <dc:creator>krammer liu</dc:creator>
      <pubDate>Fri, 13 Nov 2026 01:24:16 +0000</pubDate>
      <link>https://dev.to/krammer/i-made-claude-code-read-job-posts-against-my-resume-every-morning-and-it-never-clicks-apply-dke</link>
      <guid>https://dev.to/krammer/i-made-claude-code-read-job-posts-against-my-resume-every-morning-and-it-never-clicks-apply-dke</guid>
      <description>&lt;p&gt;Looking for a job has two slow parts. Reading 30 new postings a day to find the 3 that fit. Then changing your resume for each one.&lt;/p&gt;

&lt;p&gt;Both parts are reading and matching text. An LLM is good at that. So I wrote a &lt;a href="https://docs.claude.com/en/docs/claude-code" rel="noopener noreferrer"&gt;Claude Code&lt;/a&gt; skill that does this every morning:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. Get new jobs
   │
2. Score each job against your resume
   │
3. Check Gmail: did you already apply?
   │ no
4. Pick resume points to highlight
   │
5. Write a cover letter        (next post)
   │
6. You click Apply             (always you)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Steps 1 to 4 are in this post. Step 6 stays with you. More on that below.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you get
&lt;/h2&gt;

&lt;p&gt;One Markdown file per day. This is part of a real one (2026-11-12, Seattle, backend roles, with a sample resume). I wrapped the long lines:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;# Job hunt: 2026-11-12
&lt;/span&gt;
28 new jobs. 8 to apply (score 7+), 0 already applied, 20 below 7.

&lt;span class="gu"&gt;### 1. Senior Backend Engineer, FinTech Lending Platforms
###    at Coinbase, Inc. (9/10)
&lt;/span&gt;
&lt;span class="k"&gt;- &lt;/span&gt;Why it fits:
  &lt;span class="k"&gt;- &lt;/span&gt;Senior backend; requires Go or Java and cloud-native
    tools; resume has Go on AWS
  &lt;span class="k"&gt;- &lt;/span&gt;Lending and real-time tracking in distributed systems;
    resume has payments platform
&lt;span class="k"&gt;- &lt;/span&gt;Gaps:
  &lt;span class="k"&gt;- &lt;/span&gt;Lending and risk domain
&lt;span class="k"&gt;- &lt;/span&gt;Highlight on your resume:
  &lt;span class="k"&gt;- &lt;/span&gt;Go services on AWS (Example Payments)
  &lt;span class="k"&gt;- &lt;/span&gt;Kafka pipeline for payment events, 2M events per second
  &lt;span class="k"&gt;- &lt;/span&gt;On-call lead for the payments platform
&lt;span class="k"&gt;- &lt;/span&gt;Apply: https://...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The 20 jobs below 7 go into one table with a single reason each ("Graduate role for 2025 CS graduates", "Requires a Master's degree; resume has a B.S."). You can see what was dropped and why.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it works
&lt;/h2&gt;

&lt;p&gt;The skill is a &lt;code&gt;SKILL.md&lt;/code&gt; and two small Python scripts. Code and LLM each do what they are good at:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Step&lt;/th&gt;
&lt;th&gt;Who does it&lt;/th&gt;
&lt;th&gt;Why&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Get jobs, drop the ones you saw&lt;/td&gt;
&lt;td&gt;&lt;code&gt;fetch_jobs.py&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Same input, same output. No reason to ask a model.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Score 0-10, reasons, gaps&lt;/td&gt;
&lt;td&gt;Claude&lt;/td&gt;
&lt;td&gt;Needs reading and judgment.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Check Gmail&lt;/td&gt;
&lt;td&gt;Claude + Gmail tool&lt;/td&gt;
&lt;td&gt;Needs judgment: a job alert is not an application.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Resume points&lt;/td&gt;
&lt;td&gt;Claude&lt;/td&gt;
&lt;td&gt;Only points that are already in the resume.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Check the scores, write the list&lt;/td&gt;
&lt;td&gt;&lt;code&gt;report.py&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Catches a missing score or an unchecked top job, then Claude fixes it.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The scoring rubric is in the skill, so scores mean the same thing every day:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;0-3   wrong role or a hard blocker
      (seniority off by 2+ levels, location,
       missing skill family)
4-6   same role family, 1-2 core requirements missing
7-8   meets the core requirements,
      at most 1 nice-to-have missing
9-10  meets core and most nice-to-haves,
      and the level fits
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;report.py&lt;/code&gt; is the part I like most. Claude writes the scores to a JSON file. The script refuses it if a job has no score, a score is not 0-10, a top job was not checked in Gmail, or a top job has no resume points. It prints the problems, Claude fixes them, and it runs again. The jobs go into &lt;code&gt;seen.json&lt;/code&gt; only after the list is written, so a failed run loses nothing.&lt;/p&gt;

&lt;p&gt;The jobs come from Google Jobs (the job panel in Google Search). Google has no API for it, so step 1 uses my &lt;a href="https://apify.com/krammer/google-jobs-scraper?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=job-hunt" rel="noopener noreferrer"&gt;Google Jobs Scraper&lt;/a&gt; on Apify. &lt;a href="https://dev.to/krammer/how-to-get-google-jobs-listings-as-json-with-python-with-salary-data-4mp9"&gt;The first post&lt;/a&gt; shows that part.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why it does not click Apply
&lt;/h2&gt;

&lt;p&gt;I thought about it. I decided no, for three reasons:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Your accounts.&lt;/strong&gt; LinkedIn and Indeed do not allow bots to apply. You can lose the account you need most right now.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Every form is different.&lt;/strong&gt; Workday, Greenhouse, Lever and company forms all ask different questions. That is more work than steps 1 to 5 together, and it breaks often.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It does not help.&lt;/strong&gt; Sending 200 weak applications is worse than sending 5 good ones. The slow part was never clicking the button.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So the skill picks and prepares. You read the list over coffee and apply to the 2 or 3 that are worth it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Things I learned from real data
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Use &lt;code&gt;datePosted: 7d&lt;/code&gt;, not &lt;code&gt;24h&lt;/code&gt;.&lt;/strong&gt; I started with "jobs from the last 24 hours". Google returned 30 results per search, but only 1 was from the last 24 hours. The date filter runs on the results Google returns, so you pay for the same pages and keep much less. With &lt;code&gt;7d&lt;/code&gt; and &lt;code&gt;seen.json&lt;/code&gt; you get the same "only new jobs" effect and many more jobs.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Update (2026-11-13):&lt;/strong&gt; the Actor now sends the date filter to Google too, and you pay only for result pages that put at least one job into your dataset. A test run with &lt;code&gt;24h&lt;/code&gt; ("registered nurse", Chicago) returned 25 jobs, all from the last 24 hours, on 4 pages. &lt;code&gt;7d&lt;/code&gt; with &lt;code&gt;seen.json&lt;/code&gt; is still what the skill uses, because Google sometimes lists a job a few days after it was posted.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;The company name is sometimes a reposting site.&lt;/strong&gt; One job said "MeeBoss", but the text said TestSprite. Another said "Nubeero Limited" for a Stripe job. So the skill tells Claude to search Gmail for the employer named in the description, not the company field.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Many apply links are reposting sites.&lt;/strong&gt; In this run, 7 of the 8 jobs to apply to had only reposting links. The script ranks links (company page or ATS first, big job boards next, reposts last) and marks a job when it found only reposts, so you know to look for it on the company careers page.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Search Gmail by subject.&lt;/strong&gt; My first query matched newsletters and LinkedIn salary reports. &lt;code&gt;"&amp;lt;employer&amp;gt;" subject:(application OR applying OR applied)&lt;/code&gt; works much better.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/krammer0/google-jobs-scraper-examples.git
&lt;span class="nb"&gt;cd&lt;/span&gt; google-jobs-scraper-examples
cp -r job-hunt ~/.claude/skills/job-hunt
mkdir -p ~/job-hunt
cp job-hunt/job-hunt.example.json ~/job-hunt/job-hunt.json
cp my-resume.md ~/job-hunt/resume.md
pip install apify-client
&lt;span class="kd"&gt;export&lt;/span&gt; &lt;span class="nv"&gt;APIFY_TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&amp;lt;&lt;/span&gt;your token&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
claude &lt;span class="s2"&gt;"/job-hunt"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Edit &lt;code&gt;queries&lt;/code&gt;, &lt;code&gt;locations&lt;/code&gt; and &lt;code&gt;notes&lt;/code&gt; in &lt;code&gt;job-hunt.json&lt;/code&gt; first. No Gmail tool in Claude Code? Set &lt;code&gt;"maxGmailChecks": 0&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Cost: one search of 30 jobs is about 3 to 4 result pages, $0.028 to $0.09 per page on Apify. Claude runs in your own Claude Code, so there is no extra API key.&lt;/p&gt;

&lt;p&gt;Next post: step 5, a cover letter and form answers for the top jobs. Tell me in the comments what you want it to do.&lt;/p&gt;

</description>
      <category>automation</category>
      <category>career</category>
      <category>python</category>
      <category>ai</category>
    </item>
    <item>
      <title>How to get Google Jobs listings as JSON with Python (with salary data)</title>
      <dc:creator>krammer liu</dc:creator>
      <pubDate>Thu, 12 Nov 2026 07:33:57 +0000</pubDate>
      <link>https://dev.to/krammer/how-to-get-google-jobs-listings-as-json-with-python-with-salary-data-4mp9</link>
      <guid>https://dev.to/krammer/how-to-get-google-jobs-listings-as-json-with-python-with-salary-data-4mp9</guid>
      <description>&lt;p&gt;When you search "data engineer jobs in Austin" on Google, you get the job panel: postings from LinkedIn, Indeed, company career pages and many smaller boards, merged in one list. This is Google Jobs (also called Google for Jobs).&lt;/p&gt;

&lt;p&gt;It is a very good data source. The problem: &lt;strong&gt;Google does not offer an API for it.&lt;/strong&gt; (Google Cloud Talent Solution is a different product. It searches &lt;em&gt;your own&lt;/em&gt; job postings, not Google's.)&lt;/p&gt;

&lt;p&gt;So the usual options are:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Scrape it yourself.&lt;/strong&gt; Google blocks automated requests fast. You need proxies, retries, and parsing code that you fix every time Google changes the page.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use a SERP API subscription.&lt;/strong&gt; It works, but most plans are monthly subscriptions, and you get the raw text: &lt;code&gt;"$150K–$200K a year"&lt;/code&gt;, &lt;code&gt;"3 days ago"&lt;/code&gt;. You still parse it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use a ready-made scraper and pay per use.&lt;/strong&gt; This is what this post shows.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The 10-line version
&lt;/h2&gt;

&lt;p&gt;I built a &lt;a href="https://apify.com/krammer/google-jobs-scraper?utm_source=devto&amp;amp;utm_medium=article" rel="noopener noreferrer"&gt;Google Jobs Scraper&lt;/a&gt; on Apify. You give it search queries and locations; it returns one JSON item per job posting.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip install apify-client
&lt;span class="kd"&gt;export&lt;/span&gt; &lt;span class="nv"&gt;APIFY_TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&amp;lt;&lt;/span&gt;your token&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;   &lt;span class="c"&gt;# free account: Apify Console &amp;gt; Settings &amp;gt; API &amp;amp; Integrations&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;apify_client&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;ApifyClient&lt;/span&gt;

&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ApifyClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"APIFY_TOKEN"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;run&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;actor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"krammer/google-jobs-scraper"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;run_input&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"queries"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"data engineer"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;"locations"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Austin, TX"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;"country"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"us"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;           &lt;span class="c1"&gt;# us, gb, in, de&lt;/span&gt;
    &lt;span class="s2"&gt;"datePosted"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"7d"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;        &lt;span class="c1"&gt;# any, 24h, 3d, 7d, 30d&lt;/span&gt;
    &lt;span class="s2"&gt;"maxResultsPerSearch"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;# 1 to 200&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;job&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"defaultDatasetId"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;iterate_items&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;job&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"|"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;job&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"company"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"|"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;job&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"salaryMin"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;job&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"salaryMax"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What you get back
&lt;/h2&gt;

&lt;p&gt;One item per posting (abridged, company name replaced):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;"job_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"RtLv7vlPTbn2E40SAAAAAA=="&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"Staff Software Engineer - Card Technology"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"company"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"Example Corp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"location"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"New York, NY (+3 others)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"Example Corp is hiring a Staff Software Engineer ..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"apply_options"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"Example Corp Careers"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;"link"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"https://careers.example.com/jobs/12345"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"salary"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"$150K–$200K a year"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"posted"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"3 days ago"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"salaryMin"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;150000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"salaryMax"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"salaryCurrency"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"USD"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"salaryPeriod"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"year"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"postedDate"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"2026-11-05"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"jobTypes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"FULLTIME"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"workMode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The raw Google text is kept (&lt;code&gt;salary&lt;/code&gt;, &lt;code&gt;posted&lt;/code&gt;), and the Actor also parses it into fields you can sort and filter on: &lt;code&gt;salaryMin&lt;/code&gt;, &lt;code&gt;salaryMax&lt;/code&gt;, &lt;code&gt;salaryCurrency&lt;/code&gt;, &lt;code&gt;salaryPeriod&lt;/code&gt;, &lt;code&gt;postedDate&lt;/code&gt;, &lt;code&gt;jobTypes&lt;/code&gt; and &lt;code&gt;workMode&lt;/code&gt; (remote or hybrid). Postings that do not show a salary on Google have &lt;code&gt;null&lt;/code&gt; there.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three things to build with it
&lt;/h2&gt;

&lt;p&gt;The full scripts are in this repo: &lt;a href="https://github.com/krammer0/google-jobs-scraper-examples" rel="noopener noreferrer"&gt;krammer0/google-jobs-scraper-examples&lt;/a&gt;. &lt;code&gt;search_jobs()&lt;/code&gt; below is the 10-line call above, wrapped in a function (&lt;a href="https://github.com/krammer0/google-jobs-scraper-examples/blob/main/examples/common.py" rel="noopener noreferrer"&gt;&lt;code&gt;common.py&lt;/code&gt;&lt;/a&gt;).&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Compare salaries across cities
&lt;/h3&gt;

&lt;p&gt;Search one role in several cities and take the median of the yearly ranges:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;statistics&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;median&lt;/span&gt;

&lt;span class="n"&gt;jobs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;search_jobs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"data engineer"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"New York, NY"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Austin, TX"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Seattle, WA"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;yearly&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;jobs&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"salaryPeriod"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"year"&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"salaryCurrency"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"USD"&lt;/span&gt;
          &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"salaryMin"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"salaryMax"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;city&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"searchLocation"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;yearly&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;js&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;yearly&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"searchLocation"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;city&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;city&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;js&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;median&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"salaryMin"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;js&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;median&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"salaryMax"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;js&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Find the companies that are hiring
&lt;/h3&gt;

&lt;p&gt;Recruiters and sales teams use this to see who is hiring for what, and where:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;collections&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;Counter&lt;/span&gt;

&lt;span class="n"&gt;jobs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;search_jobs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"registered nurse"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Chicago, IL"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;company&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nc"&gt;Counter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"company"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;jobs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;most_common&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;company&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Daily job alerts without duplicates
&lt;/h3&gt;

&lt;p&gt;Run with &lt;code&gt;datePosted: "24h"&lt;/code&gt; once a day and keep the &lt;code&gt;job_id&lt;/code&gt; values you have already seen:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;fresh&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;search_jobs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"python developer"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"London"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;country&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"gb"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;datePosted&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"24h"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
         &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"job_id"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;seen&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can schedule the run in Apify Console, or call it from cron, n8n or an AI agent (the Actor works with the Apify MCP server).&lt;/p&gt;

&lt;h2&gt;
  
  
  What it costs
&lt;/h2&gt;

&lt;p&gt;You pay per Google results page (up to 10 jobs): $0.028 to $0.09 per page, depending on your Apify plan. Searches that return nothing are free. For example, 50 jobs is about 5 pages, so $0.45 or less on the Free plan, and Apify's free plan includes monthly credit to try it.&lt;/p&gt;

&lt;p&gt;Supported countries right now: United States, United Kingdom, India and Germany.&lt;/p&gt;

&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Actor: &lt;a href="https://apify.com/krammer/google-jobs-scraper?utm_source=devto&amp;amp;utm_medium=article" rel="noopener noreferrer"&gt;apify.com/krammer/google-jobs-scraper&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Example scripts: &lt;a href="https://github.com/krammer0/google-jobs-scraper-examples" rel="noopener noreferrer"&gt;github.com/krammer0/google-jobs-scraper-examples&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you need another country or another field, leave a comment.&lt;/p&gt;

</description>
      <category>python</category>
      <category>webscraping</category>
      <category>api</category>
      <category>automation</category>
    </item>
  </channel>
</rss>
