<?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: Noah</title>
    <description>The latest articles on DEV Community by Noah (@noah_1875549ab71ac4ada029).</description>
    <link>https://dev.to/noah_1875549ab71ac4ada029</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%2F4132272%2Fd5b2bb2b-66e7-42a9-b1fb-a80bd324d45a.png</url>
      <title>DEV Community: Noah</title>
      <link>https://dev.to/noah_1875549ab71ac4ada029</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/noah_1875549ab71ac4ada029"/>
    <language>en</language>
    <item>
      <title>How to Get Remote Job Listings From Multiple Boards in One API Call</title>
      <dc:creator>Noah</dc:creator>
      <pubDate>Sat, 19 Sep 2026 02:33:25 +0000</pubDate>
      <link>https://dev.to/noah_1875549ab71ac4ada029/how-to-get-remote-job-listings-from-multiple-boards-in-one-api-call-4fp3</link>
      <guid>https://dev.to/noah_1875549ab71ac4ada029/how-to-get-remote-job-listings-from-multiple-boards-in-one-api-call-4fp3</guid>
      <description>&lt;p&gt;If you've tried to build anything on top of remote job data — a niche job board, a salary tracker, a newsletter, an internal hiring dashboard — you've hit the same wall: every board publishes a different shape, and the same job shows up on three of them.&lt;/p&gt;

&lt;p&gt;This post walks through what that actually looks like, why it's more annoying than it sounds, and how to get one clean feed instead.&lt;/p&gt;

&lt;h2&gt;
  
  
  The three boards worth pulling from
&lt;/h2&gt;

&lt;p&gt;Three remote job sources publish machine-readable feeds you can use without scraping HTML:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;th&gt;Endpoint&lt;/th&gt;
&lt;th&gt;Shape&lt;/th&gt;
&lt;th&gt;Volume&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;RemoteOK&lt;/td&gt;
&lt;td&gt;&lt;code&gt;https://remoteok.com/api&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;JSON array&lt;/td&gt;
&lt;td&gt;~100 current&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Himalayas&lt;/td&gt;
&lt;td&gt;&lt;code&gt;https://himalayas.app/jobs/api&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Cursor-paginated JSON&lt;/td&gt;
&lt;td&gt;99,000+ total&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;We Work Remotely&lt;/td&gt;
&lt;td&gt;&lt;code&gt;https://weworkremotely.com/remote-jobs.rss&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;RSS/XML&lt;/td&gt;
&lt;td&gt;~85 current&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;All three are public and documented. None require a key. That's the good news.&lt;/p&gt;

&lt;h2&gt;
  
  
  The bad news: they disagree about everything
&lt;/h2&gt;

&lt;p&gt;Here's the same job as each board describes it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;RemoteOK:&lt;/strong&gt;&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="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1000001"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"position"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Backend Engineer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"company"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Acme, Inc."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"salary_min"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;120000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"salary_max"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;160000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"tags"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"python"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"django"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"location"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Anywhere in the World"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"epoch"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1758067200&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Himalayas:&lt;/strong&gt;&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="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"guid"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"him-55501"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Backend Engineer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"companyName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Acme"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"minSalary"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;130000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"maxSalary"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;170000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"salaryPeriod"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"annual"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"currency"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"USD"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"employmentType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Full Time"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"seniority"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Senior"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"locationRestrictions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Worldwide"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"pubDate"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1758067200&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;We Work Remotely:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;item&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Acme: Backend Engineer&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;region&amp;gt;&lt;/span&gt;Anywhere in the World&lt;span class="nt"&gt;&amp;lt;/region&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;skills&amp;gt;&lt;/span&gt;Python, Django, PostgreSQL&lt;span class="nt"&gt;&amp;lt;/skills&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;type&amp;gt;&lt;/span&gt;Full-Time&lt;span class="nt"&gt;&amp;lt;/type&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;pubDate&amp;gt;&lt;/span&gt;Wed, 17 Sep 2026 07:35:02 +0000&lt;span class="nt"&gt;&amp;lt;/pubDate&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/item&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three problems fall out of this immediately.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Nothing shares a field name
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;position&lt;/code&gt; vs &lt;code&gt;title&lt;/code&gt; vs a title string with the company glued to the front. &lt;code&gt;company&lt;/code&gt; vs &lt;code&gt;companyName&lt;/code&gt; vs "parse it out of the title yourself." You can map these, but you have to write and maintain three mappings.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Salaries aren't comparable
&lt;/h3&gt;

&lt;p&gt;This is the one that quietly breaks analysis.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;RemoteOK gives bare integers and uses &lt;strong&gt;&lt;code&gt;0&lt;/code&gt; to mean "not disclosed."&lt;/strong&gt; If you average naively, every undisclosed job drags your mean toward zero.&lt;/li&gt;
&lt;li&gt;Himalayas gives a &lt;code&gt;salaryPeriod&lt;/code&gt; that may be &lt;code&gt;hourly&lt;/code&gt; or &lt;code&gt;annual&lt;/code&gt;, and a &lt;code&gt;currency&lt;/code&gt; that may be &lt;code&gt;USD&lt;/code&gt;, something else, or &lt;code&gt;null&lt;/code&gt;. A $60 hourly role and a $60,000 annual role are three orders of magnitude apart and look identical if you ignore the period field.&lt;/li&gt;
&lt;li&gt;We Work Remotely doesn't publish salary at all.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Annualizing hourly pay needs a convention (2,080 hours/year is standard). Converting currencies needs live FX rates — and a stale rate silently corrupts your data in a way that's very hard to notice later.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. The same job appears three times
&lt;/h3&gt;

&lt;p&gt;Companies cross-post. If you merge three feeds naively, your "1,200 remote jobs" is closer to 1,100 real jobs plus a hundred duplicates, and any per-company counting is wrong.&lt;/p&gt;

&lt;p&gt;Deduplicating needs a stable identity across boards. IDs and URLs are per-board, so the only usable key is company + title — and even that needs normalization, because "Acme, Inc." and "Acme" have to match while "Senior Engineer" and "Junior Engineer" must not.&lt;/p&gt;

&lt;h2&gt;
  
  
  Doing it yourself
&lt;/h2&gt;

&lt;p&gt;Entirely possible. Here's the shape of it:&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="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;httpx&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fetch_remoteok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;payload&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;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://remoteok.com/api&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&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;entry&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# The first element is a legal/metadata object, not a job.
&lt;/span&gt;        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;legal&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;position&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;continue&lt;/span&gt;
        &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;title&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;position&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;company&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;company&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="c1"&gt;# 0 means undisclosed, not free
&lt;/span&gt;            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;salary_min&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;salary_min&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="bp"&gt;None&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;p&gt;Budget for these gotchas, each of which cost me a debugging cycle:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;RemoteOK's first array element is metadata&lt;/strong&gt;, not a job. Treat it as one and you emit a junk record every run.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;We Work Remotely returns 403 to obviously-automated user agents&lt;/strong&gt;, even for its public RSS feed. A self-identifying UA silently loses the entire source — and if your logging isn't wired up, you won't notice you're missing a third of your data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Himalayas' cursor can repeat.&lt;/strong&gt; Follow it blindly and you loop forever.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Timestamps come in four formats&lt;/strong&gt;: epoch seconds, epoch milliseconds, ISO-8601, and RFC-2822 from the RSS feed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then there's ongoing maintenance, because feeds change shape without notice.&lt;/p&gt;

&lt;h2&gt;
  
  
  Or use the ready-made one
&lt;/h2&gt;

&lt;p&gt;I packaged all of the above as an Apify actor: &lt;a href="https://apify.com/enhanced_elephant1/remote-jobs-feed" rel="noopener noreferrer"&gt;Remote Jobs Feed&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It pulls all three boards, normalizes them into a single schema, annualizes salaries into a comparable USD field, and merges cross-postings — keeping the most complete copy and listing the other boards under &lt;code&gt;also_on&lt;/code&gt;.&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="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"searchTerms"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"python"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"backend"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"minSalaryUsd"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;120000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"worldwideOnly"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"maxResults"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A typical run: 585 raw records in, 577 unique out after merging cross-postings, in about six seconds.&lt;/p&gt;

&lt;p&gt;One deliberate choice worth calling out: &lt;strong&gt;non-USD salaries are not converted.&lt;/strong&gt; The original figures are preserved and a &lt;code&gt;salary_is_normalized&lt;/code&gt; flag tells you whether a number is comparable. Silently applying a stale exchange rate would look tidier and be worse.&lt;/p&gt;

&lt;h2&gt;
  
  
  Which to pick
&lt;/h2&gt;

&lt;p&gt;Write it yourself if you need one board, or want total control over the schema. It's a day of work plus maintenance.&lt;/p&gt;

&lt;p&gt;Use the actor if you want three boards reconciled and would rather not own the edge cases. It's $4 per 1,000 results, and the first run needs no configuration.&lt;/p&gt;

&lt;p&gt;Either way, the thing to take from this post: &lt;strong&gt;the hard part isn't fetching, it's reconciling.&lt;/strong&gt; Budget for that and you'll be fine.&lt;/p&gt;

</description>
      <category>python</category>
      <category>api</category>
      <category>webdev</category>
      <category>datascience</category>
    </item>
  </channel>
</rss>
