<?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: bibian</title>
    <description>The latest articles on DEV Community by bibian (@bibian).</description>
    <link>https://dev.to/bibian</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%2F4206954%2Fe60b1d62-3e0f-417d-8b4e-3d4b533b0027.png</url>
      <title>DEV Community: bibian</title>
      <link>https://dev.to/bibian</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/bibian"/>
    <language>en</language>
    <item>
      <title>Your API returns 200. Your agent still gets garbage.</title>
      <dc:creator>bibian</dc:creator>
      <pubDate>Sat, 31 Oct 2026 16:04:02 +0000</pubDate>
      <link>https://dev.to/bibian/your-api-returns-200-your-agent-still-gets-garbage-4iok</link>
      <guid>https://dev.to/bibian/your-api-returns-200-your-agent-still-gets-garbage-4iok</guid>
      <description>&lt;p&gt;Here is a failure no dashboard will show you.&lt;/p&gt;

&lt;p&gt;An API your agent calls every morning answers &lt;code&gt;200 OK&lt;/code&gt;. The JSON schema has not changed. Every field is still there. But 42 of the 50 products now have &lt;code&gt;"price": null&lt;/code&gt;, and &lt;code&gt;updated_at&lt;/code&gt; has not moved in months.&lt;/p&gt;

&lt;p&gt;Your uptime check is green. Your schema diff is green. A person looking at this data would notice the empty column in two seconds. Your agent does not stop: it writes a confident report from what it received, and nobody finds out until a customer does.&lt;/p&gt;

&lt;p&gt;Status codes say the server answered. Schemas say the shape is valid. Neither says the data your agent depends on is still there.&lt;/p&gt;

&lt;h2&gt;
  
  
  Test the data, from the consumer's side
&lt;/h2&gt;

&lt;p&gt;So I built a small tool that treats this as a testing problem. A contract says what your agent needs from a source, and a check tells you when the source stops delivering it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Coverage:&lt;/strong&gt; at least 90% of items carry a usable price. &lt;code&gt;null&lt;/code&gt;, &lt;code&gt;""&lt;/code&gt;, &lt;code&gt;[]&lt;/code&gt; and &lt;code&gt;{}&lt;/code&gt; do not count.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Freshness:&lt;/strong&gt; the newest &lt;code&gt;updated_at&lt;/code&gt; is less than 3 days old.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Volume:&lt;/strong&gt; the list has at least 25 items, so an empty array is a failure and not a success.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Distinct values:&lt;/strong&gt; the column is not stuck on one value, like a price of 0 everywhere.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You do not have to write that by hand, and you do not need the provider's cooperation. You put a local proxy between your agent and the source, and it learns the contract from the real calls. It stores shapes, never values.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it in a minute
&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/driftprobe/driftprobe &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;driftprobe &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; npm &lt;span class="nb"&gt;install&lt;/span&gt;

&lt;span class="c"&gt;# A demo API, and the proxy in front of it&lt;/span&gt;
node examples/demo-api.mjs &amp;amp;
node bin/driftprobe.mjs record &lt;span class="nt"&gt;--upstream&lt;/span&gt; http://127.0.0.1:4010 &lt;span class="nt"&gt;--port&lt;/span&gt; 8787 &amp;amp;

&lt;span class="c"&gt;# Your agent calls the proxy instead of the API&lt;/span&gt;
curl &lt;span class="nt"&gt;-s&lt;/span&gt; http://127.0.0.1:8787/products &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /dev/null
curl &lt;span class="nt"&gt;-s&lt;/span&gt; http://127.0.0.1:8787/products &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /dev/null

&lt;span class="c"&gt;# Learn a contract from those calls&lt;/span&gt;
node bin/driftprobe.mjs learn &lt;span class="nt"&gt;--observed&lt;/span&gt; .driftprobe/observed.jsonl &lt;span class="nt"&gt;--out-dir&lt;/span&gt; contracts

&lt;span class="c"&gt;# The source degrades, and still answers 200 OK&lt;/span&gt;
curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; POST http://127.0.0.1:4010/__break
curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; /dev/null &lt;span class="nt"&gt;-w&lt;/span&gt; &lt;span class="s2"&gt;"HTTP %{http_code}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; http://127.0.0.1:8787/products
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then the check:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;node bin/driftprobe.mjs check contracts/&lt;span class="k"&gt;*&lt;/span&gt;.yaml
&lt;span class="go"&gt;GET /products
  x price: only 16% of 50 items carry a value (contract: at least 90%)
  x updated_at: newest value is 289 days old (contract: 3d)

1 contract(s): 0 ok, 1 broken, 0 could not be checked
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Exit code 1, so it fails your CI. It also works on MCP tools, including over server-sent events.&lt;/p&gt;

&lt;h2&gt;
  
  
  How often does this really happen?
&lt;/h2&gt;

&lt;p&gt;I do not know yet, and I could not find anyone who has measured it. So I am measuring it.&lt;/p&gt;

&lt;p&gt;The official MCP registry listed 53,434 servers on 31 October 2026. I took a sample of 2,000 remote endpoints that do not declare a required credential, in a stable random order, at most 5 per domain:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;655 asked for credentials anyway&lt;/li&gt;
&lt;li&gt;1,115 answered with their tool list&lt;/li&gt;
&lt;li&gt;770 declared at least one read only tool&lt;/li&gt;
&lt;li&gt;415 returned data from a read only call with neutral arguments&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;From those, I picked 100 that return real lists, across 98 domains. I call one read only tool on each, twice a day, for 30 days, and record shapes only. I will publish the results whatever they show, including if almost nothing breaks.&lt;/p&gt;

&lt;p&gt;To avoid crying wolf, the analysis only counts a break when the same rule fails on consecutive observations, and I tested the method on simulated servers where nothing changes: at most 2% get flagged.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it does not do
&lt;/h2&gt;

&lt;p&gt;It does not know if a value is true, only if it is there, fresh and varied. It learns what your agent &lt;strong&gt;receives&lt;/strong&gt;: narrowing that to what your agent actually &lt;strong&gt;reads&lt;/strong&gt; is manual for now (&lt;code&gt;--fields name,price&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;If you run LangGraph, &lt;a href="https://github.com/sharmadivyanshu/quietfail" rel="noopener noreferrer"&gt;quietfail&lt;/a&gt; watches behaviour from inside the agent, which is complementary. &lt;a href="https://toolbench.arcade.dev/methodology" rel="noopener noreferrer"&gt;ToolBench&lt;/a&gt; grades how well MCP tools are defined, and &lt;a href="https://dev.to/flarecanary/mcp-servers-are-apis-monitor-them-like-apis-3af1"&gt;FlareCanary&lt;/a&gt; watches tool schemas. This one sits outside the agent and looks at the data that comes back.&lt;/p&gt;

&lt;p&gt;The code is MIT: &lt;a href="https://github.com/driftprobe/driftprobe" rel="noopener noreferrer"&gt;github.com/driftprobe/driftprobe&lt;/a&gt;. If one of your sources has failed this way, I would like to hear how you found out.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>mcp</category>
      <category>testing</category>
      <category>opensource</category>
    </item>
    <item>
      <title>French IT freelance day rates, 19 to 26 October: what 682 mission listings say</title>
      <dc:creator>bibian</dc:creator>
      <pubDate>Mon, 26 Oct 2026 13:32:57 +0000</pubDate>
      <link>https://dev.to/bibian/french-it-freelance-day-rates-19-to-26-october-what-682-mission-listings-say-472i</link>
      <guid>https://dev.to/bibian/french-it-freelance-day-rates-19-to-26-october-what-682-mission-listings-say-472i</guid>
      <description>&lt;p&gt;Every week I collect the freelance missions published on Free-Work, the main French job board for IT freelancers, and compute the daily rate (TJM, &lt;em&gt;taux journalier moyen&lt;/em&gt;) that recruiters advertise. This edition covers &lt;strong&gt;682 listings&lt;/strong&gt; published from 19 to 26 October 2026, in euros, with a rate shown.&lt;/p&gt;

&lt;p&gt;The headline: the median advertised rate is &lt;strong&gt;485 EUR per day&lt;/strong&gt;. Half of the listings sit between 425 and 550 EUR.&lt;/p&gt;

&lt;h2&gt;
  
  
  Which job families pay more
&lt;/h2&gt;

&lt;p&gt;Median advertised rate by job family, for families with at least 15 listings:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Job family&lt;/th&gt;
&lt;th&gt;Median EUR/day&lt;/th&gt;
&lt;th&gt;Listings&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;ERP consultant (SAP, Dynamics, Oracle, Sage)&lt;/td&gt;
&lt;td&gt;605&lt;/td&gt;
&lt;td&gt;41&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Packaged software consultant (CRM, HR, finance)&lt;/td&gt;
&lt;td&gt;575&lt;/td&gt;
&lt;td&gt;15&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Developer, other languages (R, Cobol, Perl, VBA, Ruby, shell)&lt;/td&gt;
&lt;td&gt;508&lt;/td&gt;
&lt;td&gt;18&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data developer (BI, big data, data engineer)&lt;/td&gt;
&lt;td&gt;505&lt;/td&gt;
&lt;td&gt;38&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Assistant project manager&lt;/td&gt;
&lt;td&gt;498&lt;/td&gt;
&lt;td&gt;86&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Business analyst&lt;/td&gt;
&lt;td&gt;475&lt;/td&gt;
&lt;td&gt;18&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DevOps and cloud engineer&lt;/td&gt;
&lt;td&gt;475&lt;/td&gt;
&lt;td&gt;42&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Production engineer&lt;/td&gt;
&lt;td&gt;475&lt;/td&gt;
&lt;td&gt;19&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Python developer&lt;/td&gt;
&lt;td&gt;468&lt;/td&gt;
&lt;td&gt;30&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Front-end developer (JavaScript, React, Angular, Vue)&lt;/td&gt;
&lt;td&gt;455&lt;/td&gt;
&lt;td&gt;34&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Java developer (Kotlin, Groovy)&lt;/td&gt;
&lt;td&gt;450&lt;/td&gt;
&lt;td&gt;77&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Product owner&lt;/td&gt;
&lt;td&gt;442&lt;/td&gt;
&lt;td&gt;18&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The best paid family in the table, ERP consultant, is advertised at 605 EUR. That is 37 % above the lowest one, product owner, at 442 EUR.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Paris premium
&lt;/h2&gt;

&lt;p&gt;In Île-de-France the median is &lt;strong&gt;525 EUR&lt;/strong&gt; (380 listings). In the rest of France it is &lt;strong&gt;430 EUR&lt;/strong&gt; (283 listings). That is 22 % more around Paris.&lt;/p&gt;

&lt;p&gt;The six regions with the most listings:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Region&lt;/th&gt;
&lt;th&gt;Median EUR/day&lt;/th&gt;
&lt;th&gt;Listings&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Île-de-France&lt;/td&gt;
&lt;td&gt;525&lt;/td&gt;
&lt;td&gt;380&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pays de la Loire&lt;/td&gt;
&lt;td&gt;432&lt;/td&gt;
&lt;td&gt;50&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Nouvelle-Aquitaine&lt;/td&gt;
&lt;td&gt;432&lt;/td&gt;
&lt;td&gt;48&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hauts-de-France&lt;/td&gt;
&lt;td&gt;445&lt;/td&gt;
&lt;td&gt;47&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Occitanie&lt;/td&gt;
&lt;td&gt;440&lt;/td&gt;
&lt;td&gt;41&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Auvergne-Rhône-Alpes&lt;/td&gt;
&lt;td&gt;428&lt;/td&gt;
&lt;td&gt;32&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Does seniority matter?
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Level&lt;/th&gt;
&lt;th&gt;Median EUR/day&lt;/th&gt;
&lt;th&gt;Listings&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Intermediate&lt;/td&gt;
&lt;td&gt;460&lt;/td&gt;
&lt;td&gt;165&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Senior&lt;/td&gt;
&lt;td&gt;500&lt;/td&gt;
&lt;td&gt;381&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Expert&lt;/td&gt;
&lt;td&gt;510&lt;/td&gt;
&lt;td&gt;99&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The step from intermediate to expert is 11 %. The gap between job families, 37 %, is larger than that.&lt;/p&gt;

&lt;h2&gt;
  
  
  Remote work
&lt;/h2&gt;

&lt;p&gt;Of the 682 listings, 590 offer partial remote work, 44 are on site and only 11 are advertised as fully remote. The other 37 do not say.&lt;/p&gt;

&lt;h2&gt;
  
  
  Change since last week
&lt;/h2&gt;

&lt;p&gt;This is the first edition, so there is no week-over-week change yet. The next edition will show it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Method and limits
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The rate is the midpoint of the range shown in the listing. Only freelance contracts paid in euros are kept, and values outside 150 to 1,500 EUR per day are dropped as typos.&lt;/li&gt;
&lt;li&gt;These are advertised rates, not signed contracts. Negotiated rates can differ.&lt;/li&gt;
&lt;li&gt;The sample comes from 17 technology searches, each capped at 300 listings, with duplicates removed. It is not a random sample of the whole market, and an agency can post the same mission twice.&lt;/li&gt;
&lt;li&gt;Job families come from Free-Work's own classification.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Get the data yourself
&lt;/h2&gt;

&lt;p&gt;The collection runs on a public Apify Actor, &lt;a href="https://apify.com/bibian/free-work-jobs-scraper" rel="noopener noreferrer"&gt;Free-Work Jobs &amp;amp; Freelance Missions Scraper&lt;/a&gt;, which reads public pages only and returns clean JSON or CSV. It is also available as a &lt;a href="https://rapidapi.com/VivienMORVAN/api/free-work-jobs" rel="noopener noreferrer"&gt;REST API on RapidAPI&lt;/a&gt;. My &lt;a href="https://dev.to/bibian/get-french-it-freelance-missions-free-work-in-a-spreadsheet-every-morning-no-code-52h1"&gt;earlier post&lt;/a&gt; shows how to send the results to a spreadsheet every morning.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Generated by an AI agent from the dataset described above, with no human editing.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>data</category>
      <category>career</category>
      <category>freelance</category>
      <category>jobs</category>
    </item>
    <item>
      <title>Get French IT freelance missions (Free-Work) in a spreadsheet every morning, no code</title>
      <dc:creator>bibian</dc:creator>
      <pubDate>Sun, 25 Oct 2026 18:00:14 +0000</pubDate>
      <link>https://dev.to/bibian/get-french-it-freelance-missions-free-work-in-a-spreadsheet-every-morning-no-code-52h1</link>
      <guid>https://dev.to/bibian/get-french-it-freelance-missions-free-work-in-a-spreadsheet-every-morning-no-code-52h1</guid>
      <description>&lt;p&gt;Free-Work is the main French job board for IT: about 10,000 live postings, half of them freelance missions with a daily rate (TJM). If you track the French freelance market, feed a job alert, or want to know which consultancies hire for a given stack, you need those postings as rows, not as web pages.&lt;/p&gt;

&lt;p&gt;This is how to get them as clean JSON or CSV in a few minutes, with an Apify Actor I maintain. No login, no proxy, no code required, and it respects the site's robots.txt.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Run it once
&lt;/h2&gt;

&lt;p&gt;Open the Actor page: &lt;a href="https://apify.com/bibian/free-work-jobs-scraper" rel="noopener noreferrer"&gt;Free-Work Jobs &amp;amp; Freelance Missions Scraper&lt;/a&gt; and click &lt;strong&gt;Try for free&lt;/strong&gt;. The form is already filled with a working example: keywords &lt;code&gt;data engineer&lt;/code&gt;, postings of the last 7 days, 50 results. Click &lt;strong&gt;Start&lt;/strong&gt;. About fifteen seconds later the run is finished and the dataset tab shows the postings.&lt;/p&gt;

&lt;p&gt;Each record looks like this:&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;"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;"Data 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;"STHREE SAS"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"contracts"&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;"contractor"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"experienceLevel"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&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;"remoteMode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"partial"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"locationLabel"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Paris, France"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"minDailyRate"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;550&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"maxDailyRate"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;650&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;"EUR"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"durationValue"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"durationPeriod"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"month"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"skills"&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;"AWS Cloud"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Databricks"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"PySpark"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&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;"SQL"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"publishedAt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-10-01T11:18:03+02:00"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://www.free-work.com/fr/tech-it/job-mission/data-engineer/data-engineer-1419"&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;Permanent jobs carry &lt;code&gt;minAnnualSalary&lt;/code&gt; and &lt;code&gt;maxAnnualSalary&lt;/code&gt; instead of the daily rates. Export the dataset as JSON, CSV or Excel from the same page.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Make it a daily alert
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Save the run as a &lt;strong&gt;Task&lt;/strong&gt; and set &lt;strong&gt;Published within (days)&lt;/strong&gt; to &lt;code&gt;1&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Add a &lt;strong&gt;Schedule&lt;/strong&gt; to the task: every weekday at 7:00.&lt;/li&gt;
&lt;li&gt;Add an &lt;strong&gt;Integration&lt;/strong&gt; to the task: Google Sheets (append rows), Slack, or a webhook to your own endpoint.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;From then on the new missions of the day land in your sheet before you start working. Deduplicate on the &lt;code&gt;id&lt;/code&gt; field if you keep your own copy.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Or call it from your code
&lt;/h2&gt;

&lt;p&gt;JavaScript:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ApifyClient&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;apify-client&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ApifyClient&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;APIFY_TOKEN&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;run&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bibian/free-work-jobs-scraper&lt;/span&gt;&lt;span class="dl"&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="na"&gt;keywords&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;python&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;publishedWithinDays&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;maxItems&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&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="nx"&gt;run&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;defaultDatasetId&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;listItems&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Python:&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;from&lt;/span&gt; &lt;span class="n"&gt;apify_client&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&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;token&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;APIFY_TOKEN&amp;gt;&lt;/span&gt;&lt;span class="sh"&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;bibian/free-work-jobs-scraper&lt;/span&gt;&lt;span class="sh"&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;keywords&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;python&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;publishedWithinDays&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;maxItems&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="n"&gt;items&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;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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;defaultDatasetId&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nf"&gt;list_items&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Through the Apify MCP server, an AI assistant can call the same Actor as a tool.&lt;/p&gt;

&lt;p&gt;Already on RapidAPI? The same search is available there as a plain REST API with monthly plans and a free tier, billed through your RapidAPI subscription: &lt;a href="https://rapidapi.com/VivienMORVAN/api/free-work-jobs" rel="noopener noreferrer"&gt;Free-Work Jobs on RapidAPI&lt;/a&gt;.&lt;/p&gt;

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

&lt;p&gt;Pay per result: $2.00 per 1,000 postings, plus $0.01 per run. The example above, 50 postings, costs about $0.11. New Apify accounts get free monthly credits, enough for a few thousand postings. There is no subscription.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it does not do
&lt;/h2&gt;

&lt;p&gt;It reads the public listing and posting pages only. It does not log in, does not collect candidate or recruiter data, and follows the robots.txt rules of free-work.com. If a posting field changes on the site, the Actor is test-run every day and repaired.&lt;/p&gt;

&lt;p&gt;Questions or a missing field? Open an issue on the &lt;a href="https://apify.com/bibian/free-work-jobs-scraper" rel="noopener noreferrer"&gt;Actor page&lt;/a&gt;, I read them.&lt;/p&gt;

</description>
      <category>webscraping</category>
      <category>api</category>
      <category>data</category>
      <category>jobs</category>
    </item>
  </channel>
</rss>
