<?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: Laurent HALBRUN</title>
    <description>The latest articles on DEV Community by Laurent HALBRUN (@laurent_halbrun_0dbdc876c).</description>
    <link>https://dev.to/laurent_halbrun_0dbdc876c</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%2F4040692%2F097ea4d9-f571-4c8c-828d-b869f86f06ad.png</url>
      <title>DEV Community: Laurent HALBRUN</title>
      <link>https://dev.to/laurent_halbrun_0dbdc876c</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/laurent_halbrun_0dbdc876c"/>
    <language>en</language>
    <item>
      <title>How to check if a token is a honeypot before your bot buys it</title>
      <dc:creator>Laurent HALBRUN</dc:creator>
      <pubDate>Tue, 28 Jul 2026 22:20:18 +0000</pubDate>
      <link>https://dev.to/laurent_halbrun_0dbdc876c/how-to-check-if-a-token-is-a-honeypot-before-your-bot-buys-it-165e</link>
      <guid>https://dev.to/laurent_halbrun_0dbdc876c/how-to-check-if-a-token-is-a-honeypot-before-your-bot-buys-it-165e</guid>
      <description>&lt;p&gt;Your bot spots a fresh pair with volume ripping. It buys. The transaction confirms. Green candle. You go to take profit and… the sell reverts. Or it goes through but you receive 1% of what you should. You just bought a &lt;strong&gt;honeypot&lt;/strong&gt; — a token you can buy but can't sell.&lt;/p&gt;

&lt;p&gt;If you're building an autonomous trading agent, a sniper, or any bot that touches tokens it didn't hand-pick, this is the single failure mode that wipes accounts fastest. Here's how these traps actually work, why the "just read the contract" advice doesn't scale, and how to gate every buy behind a single pre-trade check.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a honeypot actually is
&lt;/h2&gt;

&lt;p&gt;A honeypot isn't magic. It's a token contract that makes &lt;strong&gt;buying&lt;/strong&gt; work normally so liquidity and hype build up, while quietly making &lt;strong&gt;selling&lt;/strong&gt; impossible or worthless for everyone except the deployer. Common mechanisms:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;~100% sell tax&lt;/strong&gt; — the transfer function siphons almost the entire amount on a sell. You "can" sell; you just get dust back.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Transfer blacklist / allowlist&lt;/strong&gt; — &lt;code&gt;_transfer&lt;/code&gt; reverts unless &lt;code&gt;from&lt;/code&gt; is the owner. Everyone can buy, only the deployer can sell.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;tradingEnabled&lt;/code&gt; gate&lt;/strong&gt; — trading is toggled on to attract buyers, then flipped off.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Max-tx / max-wallet limits&lt;/strong&gt; set so low a normal sell always reverts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pausable / mintable / proxy-upgradeable&lt;/strong&gt; — even a "clean" contract today can be upgraded into a trap tomorrow.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The nasty part: none of these look alarming to a human skimming the token page. The buy tax can be 0%. The chart can look organic. The trap only fires on the sell path.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why "just read the contract" doesn't scale
&lt;/h2&gt;

&lt;p&gt;The advice you'll hear is "read the Solidity." Three problems:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Most malicious contracts aren't verified&lt;/strong&gt;, or are verified with obfuscated / misleading source.&lt;/li&gt;
&lt;li&gt;Even with source, the trap is often &lt;strong&gt;hidden in modifiers or an external call&lt;/strong&gt; — a &lt;code&gt;require(canTransfer(from))&lt;/code&gt; where &lt;code&gt;canTransfer&lt;/code&gt; reads a mapping the deployer controls. Nothing in &lt;code&gt;_transfer&lt;/code&gt; itself looks wrong.&lt;/li&gt;
&lt;li&gt;Your bot has &lt;strong&gt;milliseconds&lt;/strong&gt;, not minutes. You can't manually audit a contract per trade.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Static reading catches the lazy scams and misses the good ones.&lt;/p&gt;

&lt;h2&gt;
  
  
  The two signals that actually work
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Transaction simulation.&lt;/strong&gt; Simulate a buy &lt;em&gt;and a sell&lt;/em&gt; against current chain state (an &lt;code&gt;eth_call&lt;/code&gt; / state-override or a forked EVM). If the simulated sell reverts, or the amount out is a fraction of the amount in, you have your answer — regardless of what the source says. This catches the trap by its behavior, not its code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Known-pattern security data.&lt;/strong&gt; Databases like GoPlus continuously classify contracts for honeypot markers, buy/sell tax, blacklist functions, ownership, open-source status, holder distribution, and proxy/upgrade risk. Cross-referencing these catches known bad patterns instantly.&lt;/p&gt;

&lt;p&gt;Do both and you cover behavior &lt;em&gt;and&lt;/em&gt; reputation. The problem: standing up a simulation node per chain and wiring the data sources is real infrastructure — for eight chains, it's a project in itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  One call instead of that project
&lt;/h2&gt;

&lt;p&gt;I got tired of maintaining that plumbing, so I bundled it into a single endpoint that returns a clear verdict. Here's a &lt;strong&gt;live&lt;/strong&gt; call against USDC on Ethereum:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="s1"&gt;'https://residential-scraper-crypto-company-data.p.rapidapi.com/v1/crypto/security?address=0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48&amp;amp;chain=ethereum'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'x-rapidapi-key: YOUR_KEY'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'x-rapidapi-host: residential-scraper-crypto-company-data.p.rapidapi.com'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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;"found"&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;"address"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"chain"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ethereum"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"verdict"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"OK"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"isHoneypot"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"buyTaxPct"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"sellTaxPct"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"isOpenSource"&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;"holderCount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;8213785&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"flags"&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="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"tokenName"&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 Coin"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"tokenSymbol"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"USDC"&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 flagged token looks like this instead:&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;"found"&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;"verdict"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"AVOID"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"isHoneypot"&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;"buyTaxPct"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"sellTaxPct"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;99&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"isOpenSource"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"holderCount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;41&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"flags"&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;"honeypot"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"high_sell_tax"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"not_open_source"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"blacklist_function"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"tokenName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"SafeMoonInu2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"tokenSymbol"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"SMI2"&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;The &lt;code&gt;verdict&lt;/code&gt; collapses everything into four buckets so your bot can branch on one field:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;OK&lt;/code&gt; — no red flags&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;CAUTION&lt;/code&gt; — minor concerns (some tax, low holders)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;HIGH_RISK&lt;/code&gt; — serious markers, size down or skip&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;AVOID&lt;/code&gt; — honeypot / rug markers, do not touch&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Wiring it into a pre-trade guard
&lt;/h2&gt;

&lt;p&gt;The quickest way is the &lt;a href="https://www.npmjs.com/package/honeypot-guard" rel="noopener noreferrer"&gt;&lt;code&gt;honeypot-guard&lt;/code&gt;&lt;/a&gt; npm package — it wraps this endpoint, payment included:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;honeypot-guard
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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;assertSafeToBuy&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="s2"&gt;honeypot-guard&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;assertSafeToBuy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;base&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// throws if honeypot / rug&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;executeBuy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;              &lt;span class="c1"&gt;// only runs when it's safe&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or call the API directly:&lt;/p&gt;

&lt;p&gt;The whole point is to make it a hard gate in front of every buy:&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;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;safeToBuy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;chain&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="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s2"&gt;`https://residential-scraper-crypto-company-data.p.rapidapi.com/v1/crypto/security?address=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;amp;chain=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;chain&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="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;x-rapidapi-key&lt;/span&gt;&lt;span class="dl"&gt;'&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;RAPIDAPI_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;x-rapidapi-host&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;residential-scraper-crypto-company-data.p.rapidapi.com&lt;/span&gt;&lt;span class="dl"&gt;'&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="nx"&gt;s&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;res&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;return&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;verdict&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;OK&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;verdict&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;CAUTION&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// in your trade loop:&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;safeToBuy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;base&lt;/span&gt;&lt;span class="dl"&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;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Skipping &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;: failed safety check`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;executeBuy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It covers &lt;strong&gt;8 chains&lt;/strong&gt; — Ethereum, Base, Arbitrum, Optimism, Polygon, BSC, Avalanche, Solana — behind one key, so a multi-chain sniper doesn't need eight integrations.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Try it live (free, no wallet):&lt;/strong&gt; paste a token address and get the verdict → &lt;a href="https://api.x-402.online/honeypot" rel="noopener noreferrer"&gt;api.x-402.online/honeypot&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There's a &lt;strong&gt;free tier&lt;/strong&gt; to wire it into your bot and see the verdicts on real tokens: &lt;a href="https://rapidapi.com/Ghost11777/api/residential-scraper-crypto-company-data" rel="noopener noreferrer"&gt;the endpoint is here&lt;/a&gt;. For autonomous agents that pay per call, the same route is also available as a pay-per-call x402 endpoint (USDC on Base) — no subscription, no key management.&lt;/p&gt;

&lt;p&gt;If you're running a bot right now: what's your current pre-trade check — simulation, a security API, or nothing but vibes? Curious what's actually catching traps for people in production.&lt;/p&gt;

</description>
      <category>crypto</category>
      <category>web3</category>
      <category>security</category>
      <category>api</category>
    </item>
    <item>
      <title>Scraping Leboncoin &amp; SeLoger past DataDome — what actually worked (and the honest cost)</title>
      <dc:creator>Laurent HALBRUN</dc:creator>
      <pubDate>Thu, 23 Jul 2026 22:26:03 +0000</pubDate>
      <link>https://dev.to/laurent_halbrun_0dbdc876c/scraping-leboncoin-seloger-past-datadome-what-actually-worked-and-the-honest-cost-1mi6</link>
      <guid>https://dev.to/laurent_halbrun_0dbdc876c/scraping-leboncoin-seloger-past-datadome-what-actually-worked-and-the-honest-cost-1mi6</guid>
      <description>&lt;p&gt;If you've tried to scrape &lt;strong&gt;Leboncoin&lt;/strong&gt; or &lt;strong&gt;SeLoger&lt;/strong&gt; (two of France's biggest sites), you've met &lt;strong&gt;DataDome&lt;/strong&gt; — and probably lost. Here's what I found after actually shipping it, including the parts nobody says out loud.&lt;/p&gt;

&lt;h2&gt;
  
  
  The wall: DataDome guards &lt;em&gt;everything&lt;/em&gt;
&lt;/h2&gt;

&lt;p&gt;First myth to kill: there's no clever back door. I tested the obvious one — Leboncoin's &lt;strong&gt;internal mobile API&lt;/strong&gt; (&lt;code&gt;api.leboncoin.fr/finder/search&lt;/code&gt;), the JSON endpoint the app uses. From a residential IP, with and without an app key:&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="err"&gt;HTTP&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;403&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;→&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"https://geo.captcha-delivery.com/captcha/?..."&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;DataDome sits in front of the web pages &lt;em&gt;and&lt;/em&gt; the internal API. A plain residential IP + stealth browser passes lighter anti-bot (Google Maps, Amazon, Pages Jaunes) but &lt;strong&gt;not&lt;/strong&gt; DataDome's hard mode. Don't waste a week on undetected-chromedriver here.&lt;/p&gt;

&lt;h2&gt;
  
  
  What actually works: solver-backed residential unlocker
&lt;/h2&gt;

&lt;p&gt;The only reliable path is a service that (a) provides a residential IP and (b) solves the DataDome challenge. I routed the request through one, asking for a French residential proxy + JS render + antibot, and got a clean &lt;strong&gt;200&lt;/strong&gt; with the full page:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;HTTP 200 | 1.05 MB | not blocked | __NEXT_DATA__ present
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The honest tradeoff: &lt;strong&gt;it's not free.&lt;/strong&gt; A premium+antibot+JS-render request burns real credits (~$0.005–0.02 each). If someone tells you DataDome is beatable for free at scale, they're selling you a cookie that expires in a few hours.&lt;/p&gt;

&lt;h2&gt;
  
  
  Don't parse the DOM — read the embedded JSON
&lt;/h2&gt;

&lt;p&gt;Once you're through, both sites hand you structured data if you know where to look:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Leboncoin&lt;/strong&gt; is a Next.js app: the ads are in &lt;code&gt;__NEXT_DATA__&lt;/code&gt; at &lt;code&gt;props.pageProps.searchData.ads&lt;/code&gt; — each with &lt;code&gt;list_id&lt;/code&gt;, &lt;code&gt;subject&lt;/code&gt;, &lt;code&gt;price&lt;/code&gt;, &lt;code&gt;location&lt;/code&gt;, &lt;code&gt;owner.type&lt;/code&gt; (private/pro), &lt;code&gt;images&lt;/code&gt;, &lt;code&gt;attributes&lt;/code&gt;. No brittle CSS selectors.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;m&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&amp;lt;script id="__NEXT_DATA__"&lt;/span&gt;&lt;span class="se"&gt;[^&lt;/span&gt;&lt;span class="sr"&gt;&amp;gt;&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;*&amp;gt;&lt;/span&gt;&lt;span class="se"&gt;([\s\S]&lt;/span&gt;&lt;span class="sr"&gt;*&lt;/span&gt;&lt;span class="se"&gt;?)&lt;/span&gt;&lt;span class="sr"&gt;&amp;lt;&lt;/span&gt;&lt;span class="se"&gt;\/&lt;/span&gt;&lt;span class="sr"&gt;script&amp;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;ads&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pageProps&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;searchData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ads&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;SeLoger&lt;/strong&gt; renders cards you can read with &lt;code&gt;data-testid&lt;/code&gt; (&lt;code&gt;sl.explore.card-container&lt;/code&gt;, &lt;code&gt;sl.explore-card-price&lt;/code&gt;). One gotcha that cost me a 422: SeLoger's &lt;code&gt;inseeCodes&lt;/code&gt; param &lt;strong&gt;zero-pads the commune to 4 digits&lt;/strong&gt; — INSEE &lt;code&gt;33063&lt;/code&gt; (Bordeaux) becomes &lt;code&gt;330063&lt;/code&gt;. Miss that and you get "could not get content".&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The result
&lt;/h2&gt;

&lt;p&gt;Clean, structured listings from both:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Leboncoin "iphone" Bordeaux → 3976 results
  60 € | iPhone 11 | private | 2026-07-23
 130 € | iPhone 11 - Garantie | pro

SeLoger Bordeaux → median 5657 €/m²
  Appartement 4p 163m² | 1 370 000 € | 8422 €/m²
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I wrapped both as pay-per-result actors so you don't have to manage the unlocker, cookies or the SeLoger code quirk:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Leboncoin: &lt;a href="https://apify.com/x402farm/leboncoin-scraper" rel="noopener noreferrer"&gt;https://apify.com/x402farm/leboncoin-scraper&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;SeLoger: &lt;a href="https://apify.com/x402farm/seloger-scraper" rel="noopener noreferrer"&gt;https://apify.com/x402farm/seloger-scraper&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;The wider farm (residential scraping + French business data): &lt;a href="https://api.x-402.online/start" rel="noopener noreferrer"&gt;https://api.x-402.online/start&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Happy to answer questions on the DataDome specifics in the comments.&lt;/p&gt;

</description>
      <category>webscraping</category>
      <category>javascript</category>
      <category>api</category>
      <category>datadome</category>
    </item>
    <item>
      <title>Scraping French sites that block datacenter IPs — what actually worked (Pages Jaunes, Google Maps)</title>
      <dc:creator>Laurent HALBRUN</dc:creator>
      <pubDate>Thu, 23 Jul 2026 17:49:54 +0000</pubDate>
      <link>https://dev.to/laurent_halbrun_0dbdc876c/scraping-french-sites-that-block-datacenter-ips-what-actually-worked-pages-jaunes-google-maps-p5n</link>
      <guid>https://dev.to/laurent_halbrun_0dbdc876c/scraping-french-sites-that-block-datacenter-ips-what-actually-worked-pages-jaunes-google-maps-p5n</guid>
      <description>&lt;p&gt;Some sites just refuse to be scraped from the cloud. Point a headless browser at them from AWS/GCP and you get a bot wall, a captcha, or an empty results feed. The fix that finally worked for me wasn't a smarter parser — it was &lt;strong&gt;where the request came from&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem: datacenter IPs are a dead giveaway
&lt;/h2&gt;

&lt;p&gt;Two French sources I needed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Pages Jaunes&lt;/strong&gt; (the French Yellow Pages) — anti-bot (DataDome), and the phone number is lazy-loaded so a naive scrape only sees name + address.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Google Maps&lt;/strong&gt; — aggressively rate-limits and blocks cloud IP ranges; the results feed often loads empty from a datacenter.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;From a cloud IP, both mostly failed. From a &lt;strong&gt;residential IP&lt;/strong&gt;, both worked.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fix 1 — route the browser through a residential IP
&lt;/h2&gt;

&lt;p&gt;Running real Chromium through a residential IP (not a cloud range) passes the DataDome challenge on Pages Jaunes and lets the Google Maps results feed actually populate. Same code, different exit IP — that's the whole trick for the network layer.&lt;/p&gt;

&lt;p&gt;Note: not every anti-bot falls to this. Pages Jaunes' DataDome is passable this way; some others (Leboncoin, SeLoger) run a much harder challenge that a plain render won't beat. Test before you assume.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fix 2 — read JSON-LD, don't fight the lazy-load
&lt;/h2&gt;

&lt;p&gt;On Pages Jaunes the phone is deliberately hidden until a JS interaction, so scraping the search page gives you &lt;code&gt;phone: null&lt;/code&gt;. But each business detail page ships a &lt;code&gt;&amp;lt;script type="application/ld+json"&amp;gt;&lt;/code&gt; block with the real number:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;m&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/"telephone"&lt;/span&gt;&lt;span class="se"&gt;\s&lt;/span&gt;&lt;span class="sr"&gt;*:&lt;/span&gt;&lt;span class="se"&gt;\s&lt;/span&gt;&lt;span class="sr"&gt;*"&lt;/span&gt;&lt;span class="se"&gt;([^&lt;/span&gt;&lt;span class="sr"&gt;"&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;+&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="sr"&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;phone&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;m&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/^&lt;/span&gt;&lt;span class="se"&gt;\+&lt;/span&gt;&lt;span class="sr"&gt;33/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&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;Read the structured data instead of the rendered DOM. On a Bordeaux "avocat" search this recovered &lt;strong&gt;100% of phone numbers&lt;/strong&gt; (5/5) vs ~0% from the search page alone.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fix 3 — Google Maps is a SPA, so wait + scroll
&lt;/h2&gt;

&lt;p&gt;Google Maps loads its results feed via XHR after the initial HTML. If you grab &lt;code&gt;page.content()&lt;/code&gt; too early you get an empty shell. You have to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Set the Google consent cookies (&lt;code&gt;SOCS&lt;/code&gt; / &lt;code&gt;CONSENT&lt;/code&gt;) so you skip the consent interstitial.&lt;/li&gt;
&lt;li&gt;Wait for &lt;code&gt;div[role="feed"]&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Scroll the feed until you have enough cards, then read them.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&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="nx"&gt;n&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;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;evaluate&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelectorAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;div[role="feed"] a[href*="/maps/place/"]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="nx"&gt;max&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;evaluate&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;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;f&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;div[role="feed"]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;scrollTop&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;scrollHeight&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;waitForTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1300&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;Each card gives you name (the link's &lt;code&gt;aria-label&lt;/code&gt;), rating (&lt;code&gt;.MW4etd&lt;/code&gt;), reviews (&lt;code&gt;.UY7F9&lt;/code&gt;), category (&lt;code&gt;.W4Efsd&lt;/code&gt;), phone and website. Clean B2B leads — name + phone + website + rating — the kind cloud scrapers miss because the feed never loads for them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Turned it into pay-per-result actors
&lt;/h2&gt;

&lt;p&gt;Rather than keep this to myself, I packaged them as Apify actors — pay per result, no subscription:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Pages Jaunes B2B leads&lt;/strong&gt; (optional verified phone from JSON-LD): &lt;a href="https://apify.com/x402farm/pages-jaunes-leads" rel="noopener noreferrer"&gt;https://apify.com/x402farm/pages-jaunes-leads&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Google Maps local businesses&lt;/strong&gt;: &lt;a href="https://apify.com/x402farm/google-maps-leads" rel="noopener noreferrer"&gt;https://apify.com/x402farm/google-maps-leads&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Happy to answer questions on the residential-IP approach or the JSON-LD phone trick in the comments.&lt;/p&gt;

</description>
      <category>webscraping</category>
      <category>api</category>
      <category>javascript</category>
      <category>apify</category>
    </item>
    <item>
      <title>I built 53 pay-per-call APIs that AI agents can pay for autonomously (x402 + USDC on Base)</title>
      <dc:creator>Laurent HALBRUN</dc:creator>
      <pubDate>Wed, 22 Jul 2026 01:59:37 +0000</pubDate>
      <link>https://dev.to/laurent_halbrun_0dbdc876c/i-built-53-pay-per-call-apis-that-ai-agents-can-pay-for-autonomously-x402-usdc-on-base-11a1</link>
      <guid>https://dev.to/laurent_halbrun_0dbdc876c/i-built-53-pay-per-call-apis-that-ai-agents-can-pay-for-autonomously-x402-usdc-on-base-11a1</guid>
      <description>&lt;p&gt;AI agents are getting good at &lt;em&gt;deciding&lt;/em&gt; what data they need. What they're bad at is the human paperwork around getting it: creating accounts, clicking email confirmations, pasting API keys, entering a credit card. Every one of those is a wall an autonomous agent can't climb on its own.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.x402.org/" rel="noopener noreferrer"&gt;x402&lt;/a&gt; removes that wall. It's an open payment protocol: the server answers &lt;code&gt;402 Payment Required&lt;/code&gt;, the client pays a few cents in USDC on-chain, and the request goes through — no account, no key, no human. So I built a farm of APIs designed to be consumed exactly that way.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's in it
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Base URL:&lt;/strong&gt; &lt;code&gt;https://api.x-402.online/&lt;/code&gt; — the root returns a machine-readable JSON catalog, and there's an &lt;code&gt;/openapi.json&lt;/code&gt; + &lt;code&gt;/llms.txt&lt;/code&gt; for agents.&lt;/p&gt;

&lt;p&gt;53 endpoints, all pay-per-call in USDC (from &lt;strong&gt;$0.005&lt;/strong&gt;), settled on &lt;strong&gt;Base&lt;/strong&gt; (also accepts Polygon &amp;amp; Arbitrum):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Web tooling&lt;/strong&gt; — clean-markdown extraction, JS-rendered HTML, screenshots, PDF, link/meta extraction&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;French business &amp;amp; open data&lt;/strong&gt; — company lookup (SIREN/SIRET), full KYB dossiers, financial scores, real-estate AVM &amp;amp; investment scorecards, BODACC, cadastre, DVF sale prices, geocoding, weather, holidays, IBAN validation…&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;UK company data&lt;/strong&gt; — Companies House profiles, officers, PSC/beneficial owners, one-call KYB verdict&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;US public companies&lt;/strong&gt; — SEC EDGAR profiles, annual financials, filings, one-call financial snapshot&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The friction &lt;em&gt;is&lt;/em&gt; the value: these wrap sources that need signup, keys, or heavy parsing (XBRL, registries) and expose them as one clean, payable call.&lt;/p&gt;

&lt;h2&gt;
  
  
  How an agent uses it
&lt;/h2&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;wrapFetchWithPaymentFromConfig&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="s2"&gt;@x402/fetch&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ExactEvmScheme&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="s2"&gt;@x402/evm&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;privateKeyToAccount&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="s2"&gt;viem/accounts&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;account&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;privateKeyToAccount&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;AGENT_KEY&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;fetchPaid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;wrapFetchWithPaymentFromConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;schemes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;network&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;eip155:8453&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;client&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ExactEvmScheme&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;account&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="c1"&gt;// The wrapper handles the 402 -&amp;gt; sign -&amp;gt; retry automatically.&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetchPaid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://api.x-402.online/v1/us/snapshot?ticker=AAPL&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;res&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The agent's wallet just needs USDC on Base. No gas needed on the client side — the facilitator settles.&lt;/p&gt;

&lt;h2&gt;
  
  
  It's live and already settling
&lt;/h2&gt;

&lt;p&gt;Real on-chain payments are going through today (verifiable on &lt;a href="https://basescan.org/" rel="noopener noreferrer"&gt;BaseScan&lt;/a&gt;). If you're building agents that need company data, web extraction, or open-data lookups without the account/key dance, point them at &lt;code&gt;https://api.x-402.online/&lt;/code&gt; and let them pay their own way.&lt;/p&gt;

&lt;p&gt;Feedback welcome — happy to add endpoints agents actually ask for.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>web3</category>
      <category>api</category>
    </item>
  </channel>
</rss>
