<?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: Juan de Pablos</title>
    <description>The latest articles on DEV Community by Juan de Pablos (@jdpg23).</description>
    <link>https://dev.to/jdpg23</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%2F4013556%2Fafecf364-21c0-4af4-b497-a6d26bb43422.png</url>
      <title>DEV Community: Juan de Pablos</title>
      <link>https://dev.to/jdpg23</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jdpg23"/>
    <language>en</language>
    <item>
      <title>US Federal Contract Opportunities as Clean JSON (SAM.gov Without the Pain)</title>
      <dc:creator>Juan de Pablos</dc:creator>
      <pubDate>Mon, 13 Jul 2026 08:00:18 +0000</pubDate>
      <link>https://dev.to/jdpg23/us-federal-contract-opportunities-as-clean-json-samgov-without-the-pain-2e3b</link>
      <guid>https://dev.to/jdpg23/us-federal-contract-opportunities-as-clean-json-samgov-without-the-pain-2e3b</guid>
      <description>&lt;p&gt;Every business day the US federal government publishes hundreds of new contract opportunities on &lt;a href="https://sam.gov" rel="noopener noreferrer"&gt;SAM.gov&lt;/a&gt;. It is the single largest buyer in the world, and unlike most B2B pipelines, everything is public: what they want to buy, when responses are due, who the contracting officer is, and whether the contract is reserved for small businesses.&lt;/p&gt;

&lt;p&gt;The problem is not access. The problem is that actually working with SAM.gov means refreshing a slow portal, fighting saved-search emails that arrive late, or wrestling an official API that returns deeply nested records, cryptic set-aside codes and HTML-polluted descriptions.&lt;/p&gt;

&lt;p&gt;I got tired of it and built a feed that does one thing: give me the opportunities matching my filters as clean JSON, with the fields I actually need for a go/no-go decision. This post shows what it returns and how to use it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a useful opportunity record looks like
&lt;/h2&gt;

&lt;p&gt;Here is a real record from a &lt;code&gt;cybersecurity&lt;/code&gt; keyword scan (trimmed for length):&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;"HHS/OCI West Publishing Clear Software or EQUAL"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"solicitationNumber"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"7571TEHHSOCIWPCS"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"noticeType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Solicitation"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"publishDate"&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-07-02T15:19:03.194+00: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;"responseDeadline"&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-07-06T16:30:00-04: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;"daysUntilDeadline"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"naicsCodes"&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;"541511"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"setAside"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"NONE"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"placeOfPerformance"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Silver Spring, MD, USA"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"contactName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Raja Seshadri"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"contactEmail"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"raja.seshadri@hhs.gov"&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://sam.gov/opp/a57a5914739646ccbd70cbbecac11b5b/view"&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;Three details do most of the work here:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;daysUntilDeadline&lt;/code&gt; is computed against now.&lt;/strong&gt; SAM.gov gives you a timestamp; what you actually want to know is "is this alive, and how urgent is it". A record with &lt;code&gt;daysUntilDeadline: 1&lt;/code&gt; goes to the top of today's list. Anything negative never reaches you.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;setAside&lt;/code&gt; is a label, not a code.&lt;/strong&gt; The raw API speaks in codes like &lt;code&gt;SBA&lt;/code&gt;, &lt;code&gt;8A&lt;/code&gt;, &lt;code&gt;WOSBC&lt;/code&gt;, &lt;code&gt;SDVOSBC&lt;/code&gt;. The feed maps them to what they mean, so you can filter for "total small business set-aside" or "service-disabled veteran-owned" without a lookup table. If you qualify for a set-aside category, these are the opportunities where your win rate is structurally higher, because most of the market is excluded by law.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The contracting officer is right there.&lt;/strong&gt; Name and email, from the official record. For subcontracting or capability questions, that contact is the difference between a bid and a shot in the dark.&lt;/p&gt;

&lt;h2&gt;
  
  
  Running a scan
&lt;/h2&gt;

&lt;p&gt;The feed runs as an Apify Actor, so you can call it from anywhere that can make an HTTP request:&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="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s2"&gt;"https://api.apify.com/v2/acts/jdepablos~samgov-opportunities-feed/run-sync-get-dataset-items?timeout=300"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$APIFY_TOKEN&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "keywords": "cybersecurity",
    "dueWithinDays": 30,
    "setAsides": ["SBA"],
    "maxOpportunities": 25
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can filter by keywords, NAICS codes, notice types (solicitations vs sources-sought vs award notices), deadline window and set-aside categories. Descriptions come back with the HTML stripped.&lt;/p&gt;

&lt;p&gt;Schedule that daily and you have a private deal-flow feed for exactly your niche, hitting your inbox or your CRM before you would ever find the opportunity by browsing.&lt;/p&gt;

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

&lt;p&gt;Pay per event: $0.004 per opportunity delivered. A daily scan returning 25 matches costs 10 cents. There is no subscription, and runs that return nothing cost you nothing beyond Apify's tiny platform fee. Compare that with the enterprise gov-con intelligence suites, which start in the hundreds per month and mostly resell the same public data.&lt;/p&gt;

&lt;h2&gt;
  
  
  Who this is for
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Small businesses with set-aside eligibility.&lt;/strong&gt; Filter for your categories and stop competing with everyone.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Subcontractors.&lt;/strong&gt; Sources-sought notices tell you which primes are hunting for partners, weeks before an award.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Anyone building agents.&lt;/strong&gt; The Actor is exposed as an MCP tool, so an AI agent can query live federal opportunities mid-conversation. Ask it "find cybersecurity contracts closing this month with small-business set-asides" and it has the answer in one call.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Actor is here: &lt;a href="https://apify.com/jdepablos/samgov-opportunities-feed" rel="noopener noreferrer"&gt;US Contract Opportunities Feed on Apify&lt;/a&gt;. Runnable examples for this and 16 other data tools live in my &lt;a href="https://github.com/JdPG23/data-actors-cookbook" rel="noopener noreferrer"&gt;Data Actors Cookbook&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you work gov-con and there is a field or filter you are missing, tell me in the comments. The last two features I shipped on another feed came straight from a reader request.&lt;/p&gt;

</description>
      <category>government</category>
      <category>api</category>
      <category>data</category>
      <category>business</category>
    </item>
    <item>
      <title>Scraping Booking.com Without Touching the Search Box (City Landings + the Apollo Cache)</title>
      <dc:creator>Juan de Pablos</dc:creator>
      <pubDate>Sat, 11 Jul 2026 08:00:14 +0000</pubDate>
      <link>https://dev.to/jdpg23/scraping-bookingcom-without-touching-the-search-box-city-landings-the-apollo-cache-b7f</link>
      <guid>https://dev.to/jdpg23/scraping-bookingcom-without-touching-the-search-box-city-landings-the-apollo-cache-b7f</guid>
      <description>&lt;p&gt;Booking.com does not want bots, and it shows. Every plain HTTP request, any client, any proxy, gets absorbed by AWS WAF with a 202 that never resolves into content. There is no getting around a real browser here, which makes this the most expensive scrape in my portfolio. What kept the project alive was discovering that the two hard parts, search and data extraction, both have quiet side doors.&lt;/p&gt;

&lt;h2&gt;
  
  
  Don't touch the search box
&lt;/h2&gt;

&lt;p&gt;My first version drove the UI like a user: open the homepage, type the city, click search. It was a disaster. The autocomplete is timing-sensitive, the results panel loads lazily, and about a third of runs ended with zero hotels for no visible reason. Debugging flaky UI automation is the worst kind of debugging because every failure looks different.&lt;/p&gt;

&lt;p&gt;The side door: Booking maintains SEO landing pages per city with stable URLs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://www.booking.com/city/{countryCode}/{citySlug}.{lang}.html
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Navigate straight there and the page arrives populated with a hotel list. No typing, no clicking, no autocomplete race. Runs went from flaky to boring, which is the highest compliment a scraper can receive.&lt;/p&gt;

&lt;p&gt;To build the URL from a free-text city name I geocode with Open-Meteo's public API (no key): city name in, country code and a sluggable name out.&lt;/p&gt;

&lt;h2&gt;
  
  
  The data is not in the DOM
&lt;/h2&gt;

&lt;p&gt;Second surprise: scraping the rendered page gets you almost nothing coherent. Prices and hotel names live in visually adjacent but structurally unrelated containers, and the class names are minified soup.&lt;/p&gt;

&lt;p&gt;The actual data source is Booking's Apollo GraphQL cache, embedded in a &lt;code&gt;&amp;lt;script type="application/json"&amp;gt;&lt;/code&gt; block with a &lt;code&gt;ROOT_QUERY&lt;/code&gt; key. Every hotel on the page is there as a typed object: name, review score, coordinates, and the price in three formats.&lt;/p&gt;

&lt;p&gt;About those formats: &lt;code&gt;roundedValue&lt;/code&gt; and &lt;code&gt;value&lt;/code&gt; are display strings like "€ 128". The numeric price is at &lt;code&gt;minPrice.AMOUNT&lt;/code&gt;. If you parse the display strings you inherit every currency symbol and thousands separator problem for free. Take the number the frontend itself uses.&lt;/p&gt;

&lt;h2&gt;
  
  
  Know what price you are looking at
&lt;/h2&gt;

&lt;p&gt;One honesty note that shapes the whole product: city landing prices are indicative nightly rates, not date-specific quotes. Booking shows "from X per night" without you having picked dates. For market-level questions (median price level of a city, how hotel A sits against the market) that is exactly what you want. For "what does a room cost on August 14" you need a second navigation with dates, which is a different, more expensive product.&lt;/p&gt;

&lt;p&gt;My feed stamps &lt;code&gt;priceBasis: "indicative-nightly"&lt;/code&gt; on every response so nobody mistakes one for the other. Label your price basis; blurred numbers look precise and mean nothing.&lt;/p&gt;

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

&lt;p&gt;The result is &lt;a href="https://apify.com/jdepablos/booking-hotel-price-analyzer" rel="noopener noreferrer"&gt;booking-hotel-price-analyzer&lt;/a&gt; on Apify. One call gives you a city's hotel market: median and percentile prices, supply depth, and the hotel list behind the numbers.&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="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s2"&gt;"https://api.apify.com/v2/acts/jdepablos~booking-hotel-price-analyzer/run-sync-get-dataset-items?token=&lt;/span&gt;&lt;span class="nv"&gt;$APIFY_TOKEN&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"location": "Madrid"}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Browser-based, so it costs more per scan than my HTTP actors, and it is priced accordingly. A scan that returns no hotels is never charged.&lt;/p&gt;

&lt;p&gt;MCP config for agents:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://mcp.apify.com?tools=jdepablos/booking-hotel-price-analyzer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Takeaways
&lt;/h2&gt;

&lt;p&gt;When a site fights HTTP clients, the browser is the entry fee, but you still choose how much of the UI you depend on. Direct navigation to stable URLs beats interactive flows every time you can find them. And before you fight the DOM, check for an embedded state blob: Apollo caches, &lt;code&gt;__NEXT_DATA__&lt;/code&gt;, Redux snapshots. Frontends increasingly ship their data model in one script tag, cleaner than any HTML you will ever parse.&lt;/p&gt;

&lt;p&gt;Hotel folks: if you need date-specific quotes or a market I have not covered, tell me in the comments and it goes on the roadmap.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>api</category>
      <category>javascript</category>
      <category>ai</category>
    </item>
    <item>
      <title>Tracking New Patent Filings Without an API Key (Google Patents Has a Hidden XHR Endpoint)</title>
      <dc:creator>Juan de Pablos</dc:creator>
      <pubDate>Thu, 09 Jul 2026 08:00:17 +0000</pubDate>
      <link>https://dev.to/jdpg23/tracking-new-patent-filings-without-an-api-key-google-patents-has-a-hidden-xhr-endpoint-4ec7</link>
      <guid>https://dev.to/jdpg23/tracking-new-patent-filings-without-an-api-key-google-patents-has-a-hidden-xhr-endpoint-4ec7</guid>
      <description>&lt;p&gt;I wanted a simple thing: a weekly feed of new patent filings matching a keyword, so I could watch what competitors in a given technology are up to. The official routes all said no. The USPTO Open Data Portal returned 401 without an API key. The bulk data endpoint answered 503 for days. PatentsView had just moved to key-only access with an approval queue.&lt;/p&gt;

&lt;p&gt;Then I opened DevTools on Google Patents and found that the whole site runs on one undocumented XHR endpoint. No key, no login, worldwide coverage.&lt;/p&gt;

&lt;h2&gt;
  
  
  The endpoint
&lt;/h2&gt;

&lt;p&gt;When you search on patents.google.com, the frontend calls:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://patents.google.com/xhr/query?url=q%3D(solid%2Bstate%2Bbattery)%26sort%3Dnew
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;url&lt;/code&gt; parameter is itself a URL-encoded query string. Decode it and you get the same syntax the search box uses: &lt;code&gt;q=(solid+state+battery)&amp;amp;sort=new&lt;/code&gt;, plus operators like &lt;code&gt;assignee=Nintendo&lt;/code&gt; or &lt;code&gt;country=US&lt;/code&gt;. The response is JSON with &lt;code&gt;results.cluster[0].result[]&lt;/code&gt;, each entry carrying the publication number, title, snippet, assignee, inventor and dates.&lt;/p&gt;

&lt;p&gt;It works with plain HTTP requests. I have been running it on a schedule for a couple of days now without drama, which is more than I can say for my week with the official APIs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three traps I hit so you don't have to
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Server-side date filters return zero.&lt;/strong&gt; The query syntax accepts &lt;code&gt;after=publication:20260101&lt;/code&gt;, and on the website it works. Through the XHR endpoint the same filter quietly returns an empty result set. Not an error, just nothing. The fix is to drop the filter, fetch pages sorted by newest, and filter &lt;code&gt;publication_date&lt;/code&gt; client-side. Annoying, but reliable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. &lt;code&gt;sort=new&lt;/code&gt; is not actually sorted.&lt;/strong&gt; I assumed I could stop paginating once I saw a date older than my window. Wrong: results within &lt;code&gt;sort=new&lt;/code&gt; are shuffled inside a rough two-month band, so an old date does not mean the rest of the page is old too. If you early-exit on the first stale date, you silently lose fresh publications. You have to walk the whole window and let the client-side filter do its job.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. The index lags reality by about two months.&lt;/strong&gt; A patent published last week will usually not be in Google Patents yet. If you set your watch window to 30 days, most weeks return nothing and you conclude the tool is broken. Ninety days is the sweet spot: you see everything, slightly deferred. For patents that trade-off is usually fine, since the filings themselves are 18 months old by the time they publish.&lt;/p&gt;

&lt;p&gt;There is also the ordinary anti-abuse stuff: burst requests from datacenter IPs get 503s, so pace your calls and use decent proxies. And the JSON fields arrive with &lt;code&gt;&amp;lt;b&amp;gt;&lt;/code&gt; highlight tags around your search terms, which you need to strip before the data is clean.&lt;/p&gt;

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

&lt;p&gt;I packaged all of this as an Apify Actor, &lt;a href="https://apify.com/jdepablos/patent-watch" rel="noopener noreferrer"&gt;patent-watch&lt;/a&gt;. One call:&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="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s2"&gt;"https://api.apify.com/v2/acts/jdepablos~patent-watch/run-sync-get-dataset-items?token=&lt;/span&gt;&lt;span class="nv"&gt;$APIFY_TOKEN&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"term": "solid state battery", "daysBack": 90}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can watch a technology (&lt;code&gt;term&lt;/code&gt;), a company (&lt;code&gt;assignee: "Nintendo"&lt;/code&gt;), or both at once, up to 20 watches per run. Pricing is per watch that returns hits; a quiet week costs nothing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wiring it into an AI agent (MCP)
&lt;/h2&gt;

&lt;p&gt;One line of config through Apify's MCP server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://mcp.apify.com?tools=jdepablos/patent-watch
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your agent gets a "what's new in patents for X?" tool with structured output. Put it on a weekly schedule and the agent writes your competitive R&amp;amp;D brief for you.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I took away
&lt;/h2&gt;

&lt;p&gt;Official APIs are the right first choice, and I always probe them before touching anything else. But when the official route is keyed, queued or down, the data-bearing endpoint behind a public website is often better documented by its own network tab than the sanctioned API is by its docs. Just treat it with respect: pace your requests, filter client-side when the server lies to you, and never trust a sort order you have not verified.&lt;/p&gt;

&lt;p&gt;If you watch patents for competitive intelligence and there is a field or filter you miss, tell me in the comments. The last feature request I got on one of these feeds turned into a shipped field plus a bug fix within a day, so they do not land in a void.&lt;/p&gt;

</description>
      <category>api</category>
      <category>webdev</category>
      <category>ai</category>
      <category>datascience</category>
    </item>
    <item>
      <title>Catawiki Publishes Its Expert Estimates in the Page JSON. I Built a Deal Finder on Top</title>
      <dc:creator>Juan de Pablos</dc:creator>
      <pubDate>Wed, 08 Jul 2026 08:00:20 +0000</pubDate>
      <link>https://dev.to/jdpg23/catawiki-publishes-its-expert-estimates-in-the-page-json-i-built-a-deal-finder-on-top-2k5h</link>
      <guid>https://dev.to/jdpg23/catawiki-publishes-its-expert-estimates-in-the-page-json-i-built-a-deal-finder-on-top-2k5h</guid>
      <description>&lt;p&gt;Catawiki is an auction site where every lot gets reviewed by a category expert who writes an estimated value range. That estimate is the single most useful number on the page, and it turns out Catawiki ships it to your browser in structured JSON, for every lot, before you even bid.&lt;/p&gt;

&lt;p&gt;Compare the current bid against the expert estimate across a whole category and you have a shopping list of undervalued lots. I built a bot that does exactly that. Here is what I learned about the site on the way.&lt;/p&gt;

&lt;h2&gt;
  
  
  The data is in &lt;strong&gt;NEXT_DATA&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Catawiki is a Next.js app, so each page embeds its state in a &lt;code&gt;&amp;lt;script id="__NEXT_DATA__"&amp;gt;&lt;/code&gt; block. No hidden API, no reverse engineering, just parse the JSON out of the HTML.&lt;/p&gt;

&lt;p&gt;Two levels matter:&lt;/p&gt;

&lt;p&gt;The search page carries &lt;code&gt;pageProps.searchLots.lots&lt;/code&gt;, 24 lots per page. Handy for discovery, but there is a catch: search results do not include prices or estimates. Just IDs, titles and images.&lt;/p&gt;

&lt;p&gt;The lot page is where everything lives: current bid, the expert's low and high estimate, whether the reserve price has been met, bid history, closing time. One fetch per lot, so a category scan is one search request plus N lot requests. I run them at concurrency 4 and the site has never complained.&lt;/p&gt;

&lt;h2&gt;
  
  
  The pivot I didn't plan
&lt;/h2&gt;

&lt;p&gt;My original idea was to analyze completed auctions and build price history. Dead end: Catawiki's public search only exposes live lots. Once a lot closes it disappears from search, and there is no public archive to walk.&lt;/p&gt;

&lt;p&gt;So the product pivoted from "price history" to "live deal finder": scan open lots, compute how far the current bid sits below the expert estimate, and surface the gap. The site constraint decided the product, which happens more often than product plans admit.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fresh auctions look absurd, and that's fine
&lt;/h2&gt;

&lt;p&gt;First real run: Game Boy lots showing bids 85 to 99 percent below the expert estimate. Too good to be true? Sort of. Auctions that just opened have starting bids of one euro against estimates of hundreds. The gap is real but everyone bidding knows it will close.&lt;/p&gt;

&lt;p&gt;The actionable filter is time: a lot ending within 24 hours with the bid still 40 percent under estimate is a genuinely interesting signal. A lot with six days left is noise. So the bot takes &lt;code&gt;endingWithinHours&lt;/code&gt; as its main input, and that single filter separates a gimmick from a tool.&lt;/p&gt;

&lt;p&gt;One practical note: fetching through catawiki.com/es/ pins all amounts to EUR, which saves you currency detection entirely.&lt;/p&gt;

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

&lt;p&gt;Packaged as an Apify Actor, &lt;a href="https://apify.com/jdepablos/catawiki-deal-finder" rel="noopener noreferrer"&gt;catawiki-deal-finder&lt;/a&gt;:&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="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s2"&gt;"https://api.apify.com/v2/acts/jdepablos~catawiki-deal-finder/run-sync-get-dataset-items?token=&lt;/span&gt;&lt;span class="nv"&gt;$APIFY_TOKEN&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"query": "game boy", "endingWithinHours": 24, "minDiscountPct": 30}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You get each lot with its current bid, the expert estimate range, the discount percentage, reserve status and closing time. Pricing is per auction scan, and a scan that returns nothing is never charged.&lt;/p&gt;

&lt;p&gt;For AI agents there is an MCP route too:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://mcp.apify.com?tools=jdepablos/catawiki-deal-finder
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;"Any watches closing today at half the expert estimate?" becomes a tool call your agent can run every morning.&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaways
&lt;/h2&gt;

&lt;p&gt;Next.js sites are gifts: the state you want is usually sitting in &lt;code&gt;__NEXT_DATA__&lt;/code&gt;, typed and complete, no DOM parsing needed. But check what the public surface actually exposes before you promise a product. I planned an archive analysis and the archive did not exist. The expert estimate data was better anyway: it is an opinion you can trade against, published in JSON.&lt;/p&gt;

&lt;p&gt;If you hunt auctions and want a filter or a category I have not covered, comments are open.&lt;/p&gt;

</description>
      <category>api</category>
      <category>webdev</category>
      <category>javascript</category>
      <category>ai</category>
    </item>
    <item>
      <title>One API Call to Price Any Luxury Watch (Median, Percentiles, Market Depth)</title>
      <dc:creator>Juan de Pablos</dc:creator>
      <pubDate>Sun, 05 Jul 2026 23:13:12 +0000</pubDate>
      <link>https://dev.to/jdpg23/one-api-call-to-price-any-luxury-watch-median-percentiles-market-depth-4odn</link>
      <guid>https://dev.to/jdpg23/one-api-call-to-price-any-luxury-watch-median-percentiles-market-depth-4odn</guid>
      <description>&lt;p&gt;Every watch-price tool I found gives you the same thing: a pile of raw listings. Scrape Chrono24, get 60 JSON objects, and now &lt;em&gt;you&lt;/em&gt; write the median logic, &lt;em&gt;you&lt;/em&gt; filter out the "strap only" listings poisoning your stats, &lt;em&gt;you&lt;/em&gt; figure out that the first cards on the page are promoted ads for a different model entirely.&lt;/p&gt;

&lt;p&gt;I wanted the opposite: &lt;strong&gt;one call in, one market analysis out.&lt;/strong&gt; So I built it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What "analyzed" means
&lt;/h2&gt;

&lt;p&gt;Ask for &lt;code&gt;"Rolex Submariner 16610"&lt;/code&gt; and you get back a single structured answer:&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;"analysis"&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;"n"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;46&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;"median"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;9150&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"p10"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;8000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"p25"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;8690&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"p75"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;11400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"p90"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;13465&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"priceBasis"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"asking"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"marketDepth"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1181&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"certifiedSharePct"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;21.7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"dealerCountryMix"&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="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"country"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"IT"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"count"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&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="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"country"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ES"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"count"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;7&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="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"confidence"&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"&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="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;Plus the cleaned comps behind every number, so you can audit the math.&lt;/p&gt;

&lt;p&gt;The cleaning is where the real work happens. Before any statistics run, the pipeline drops:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Promoted cards.&lt;/strong&gt; On chrono24.com they can be up to two thirds of the page, and they are often a &lt;em&gt;different model&lt;/em&gt; than what you searched.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Accessories.&lt;/strong&gt; A "strap for Submariner" contains every token of your query and costs 95% less. Keyword matching fails here: in watch land, "Blue Dial" and "Box &amp;amp; Papers" are descriptors of a complete watch, not accessory listings. The filter is positional instead: a title that &lt;em&gt;starts&lt;/em&gt; with "strap", or matches "bezel &lt;strong&gt;for&lt;/strong&gt; ...", is an accessory; a title that merely &lt;em&gt;mentions&lt;/em&gt; a bezel is not.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Parts, defective, replica listings and duplicates.&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Skip the cleaning and your median moves by hundreds of euros. I measured it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Honest data: asks, not sales
&lt;/h2&gt;

&lt;p&gt;Every price here is a live &lt;strong&gt;asking price&lt;/strong&gt; (&lt;code&gt;priceBasis: "asking"&lt;/code&gt; is stamped on every response). Asking prices tell you where the market is priced &lt;em&gt;today&lt;/em&gt;: what you'd pay right now, how deep supply is (&lt;code&gt;marketDepth&lt;/code&gt;), how spread the market is (&lt;code&gt;p10&lt;/code&gt; to &lt;code&gt;p90&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;What they don't tell you is what buyers actually pay. For that you want confirmed sales, like eBay sold listings. Comparing the two gives you the ask-vs-sold gap, which is where negotiation lives. (I run a &lt;a href="https://apify.com/jdepablos/ebay-sold-price-appraiser" rel="noopener noreferrer"&gt;sold-side appraiser&lt;/a&gt; for exactly that; point both at the same model and you have both sides of the market.)&lt;/p&gt;

&lt;p&gt;Tools that blur this distinction produce numbers that look precise and mean nothing. Label your price basis.&lt;/p&gt;

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

&lt;p&gt;The analyzer runs on Apify as &lt;a href="https://apify.com/jdepablos/chrono24-watch-analyzer" rel="noopener noreferrer"&gt;chrono24-watch-analyzer&lt;/a&gt;. One HTTP call:&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="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s2"&gt;"https://api.apify.com/v2/acts/jdepablos~chrono24-watch-analyzer/run-sync-get-dataset-items?token=&lt;/span&gt;&lt;span class="nv"&gt;$APIFY_TOKEN&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"query": "Omega Speedmaster Professional", "domain": "chrono24.com"}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Batch mode takes up to 25 models per run. Six Chrono24 domains are supported (&lt;code&gt;.com&lt;/code&gt;/&lt;code&gt;.es&lt;/code&gt;/&lt;code&gt;.de&lt;/code&gt;/&lt;code&gt;.fr&lt;/code&gt;/&lt;code&gt;.it&lt;/code&gt;/&lt;code&gt;.co.uk&lt;/code&gt;), which sets the currency.&lt;/p&gt;

&lt;p&gt;Pricing is per analysis ($0.02, comps included), and an analysis that returns no data is never charged. That last part took real plumbing: the actor distinguishes "this query genuinely has zero listings" from "the site soft-blocked this request", retries the latter with a different proxy, and only bills when you get numbers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wiring it into an AI agent (MCP)
&lt;/h2&gt;

&lt;p&gt;If you're building agents, this is one line of config via Apify's MCP server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://mcp.apify.com?tools=jdepablos/chrono24-watch-analyzer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your agent gets a &lt;code&gt;chrono24-watch-analyzer&lt;/code&gt; tool that answers "what's this watch worth on the open market?" with structured, self-describing data. No pagination, no post-processing, no HTML. The output schema ships with the actor, so the agent knows what &lt;code&gt;marketDepth&lt;/code&gt; and &lt;code&gt;certifiedSharePct&lt;/code&gt; mean without you writing glue.&lt;/p&gt;

&lt;p&gt;Agent use cases I'm already seeing work:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Deal triage&lt;/strong&gt;: agent receives a watch listing (marketplace, auction, classifieds), pulls the ask distribution, flags anything under &lt;code&gt;p25&lt;/code&gt; for human review.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Collection valuation&lt;/strong&gt;: batch-run 25 references, sum medians, track weekly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Negotiation prep&lt;/strong&gt;: &lt;code&gt;median&lt;/code&gt; and &lt;code&gt;p10&lt;/code&gt; in hand before you make an offer.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What I learned building it
&lt;/h2&gt;

&lt;p&gt;Two bugs worth sharing so you don't repeat them:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Regex money parsing&lt;/strong&gt;: my price regex &lt;code&gt;\d{1,3}(?:[.,\s]\d{3})*&lt;/code&gt; silently truncated "5590 €" to "559". The first alternative matched a prefix and stopped. Chrono24 omits decimals and thousands separators under 10k; eBay always emits decimals, so the same bug sat latent in another parser for weeks. Make the thousands group mandatory (&lt;code&gt;+&lt;/code&gt;) and fall through to plain &lt;code&gt;\d+&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keyword-based accessory filters break on watches&lt;/strong&gt;: "Box &amp;amp; Papers" is a &lt;em&gt;good&lt;/em&gt; sign in a watch listing, not an accessory. Domain vocabulary matters; test your filters against real cards before trusting your medians.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Questions, feature requests (multi-page sampling and reference-number input are next), weird edge cases: comments are open.&lt;/p&gt;

</description>
      <category>api</category>
      <category>webdev</category>
      <category>ai</category>
      <category>javascript</category>
    </item>
    <item>
      <title>I Built a Bot That Finds Underpriced Game Consoles on Vinted (Using Real eBay Sold Prices)</title>
      <dc:creator>Juan de Pablos</dc:creator>
      <pubDate>Sun, 05 Jul 2026 08:00:34 +0000</pubDate>
      <link>https://dev.to/jdpg23/i-built-a-bot-that-finds-underpriced-game-consoles-on-vinted-using-real-ebay-sold-prices-ecn</link>
      <guid>https://dev.to/jdpg23/i-built-a-bot-that-finds-underpriced-game-consoles-on-vinted-using-real-ebay-sold-prices-ecn</guid>
      <description>&lt;p&gt;Every reseller knows the feeling: you spot a Game Boy Micro for €100 on Vinted, you're &lt;em&gt;pretty sure&lt;/em&gt; it flips for more on eBay, but by the time you've checked sold listings, filtered out the broken ones and done the fee math, someone else bought it.&lt;/p&gt;

&lt;p&gt;The problem isn't finding listings — it's &lt;strong&gt;valuing them fast&lt;/strong&gt;. So I automated the whole loop: scan live listings on Vinted, Wallapop and Milanuncios, value each item against &lt;strong&gt;confirmed eBay sold prices&lt;/strong&gt; (not asking prices!), subtract every fee, and return only the listings with real profit in them. This tutorial shows you how to run it yourself in 5 minutes, across Spain, France, Germany, Italy or the UK.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why most "deal finding" fails
&lt;/h2&gt;

&lt;p&gt;Three traps kill naive flipping bots:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Asking prices lie.&lt;/strong&gt; eBay is full of Game Boy Micros &lt;em&gt;listed&lt;/em&gt; at €400 that never sell. The only truth is &lt;code&gt;Sold listings&lt;/code&gt; — what buyers actually paid. Any valuation built on active listings is fantasy.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The junk poisons the math.&lt;/strong&gt; "GBA cartridge lot", "Game Boy Micro &lt;em&gt;stand&lt;/em&gt;", "for parts, not working" — if these enter your average, a €2.50 accessory looks like a 2,000% margin deal. (Ask me how I know.)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fees eat the spread.&lt;/strong&gt; Buyer protection (~5% + €0.70 on Vinted), shipping both ways, ~13% marketplace fees when you resell. A "€50 profit" is often €12 after honest math.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The &lt;a href="https://apify.com/jdepablos/second-hand-deal-scanner-es" rel="noopener noreferrer"&gt;Second-hand Deal Scanner EU&lt;/a&gt; Actor handles all three: sold-only valuations, aggressive junk/accessory/price-sanity filters (every rejection is counted so you can audit it), and a fee model you can override.&lt;/p&gt;

&lt;h2&gt;
  
  
  Run your first scan
&lt;/h2&gt;

&lt;p&gt;Grab a free &lt;a href="https://apify.com" rel="noopener noreferrer"&gt;Apify&lt;/a&gt; API token, then:&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;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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://api.apify.com/v2/acts/jdepablos~second-hand-deal-scanner-es/run-sync-get-dataset-items?timeout=300&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="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&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="na"&gt;Authorization&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Bearer &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="s2"&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;Content-Type&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;application/json&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="na"&gt;body&lt;/span&gt;&lt;span class="p"&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;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Game Boy Micro&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;country&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ES&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;        &lt;span class="c1"&gt;// or FR, DE, IT, UK&lt;/span&gt;
      &lt;span class="na"&gt;marginMinPct&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;     &lt;span class="c1"&gt;// only show ≥25% estimated margin&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="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;A real result from my test runs:&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;"query"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Game Boy Micro"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"valuation"&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;"median"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;217.47&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"n"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;184&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"confidence"&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"&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="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;"deals"&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="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"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;"vinted"&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;"Game Boy micro en perfecto estado"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"price"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;100&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.vinted.es/items/..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"economics"&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;"estBuyTotal"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;108.70&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"estResaleNet"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;162.92&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"estProfit"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;54.22&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"estMarginPct"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;49.65&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;"deal"&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="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;"meta"&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;"listingsScanned"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;89&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"filteredOut"&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;"titleMismatch"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;21&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"accessory"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"junk"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"suspiciouslyCheap"&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="w"&gt; &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="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;Read that valuation line: &lt;strong&gt;184 confirmed sales, median €217.47, high confidence&lt;/strong&gt;. The console at €100 has ~€54 of profit &lt;em&gt;after&lt;/em&gt; buyer protection, shipping and resale fees. And look at &lt;code&gt;filteredOut&lt;/code&gt; — 31 listings were rejected (wrong product, accessories, too-cheap-to-be-real). That's the boring machinery that makes the number trustworthy.&lt;/p&gt;

&lt;h2&gt;
  
  
  Build a daily watchlist
&lt;/h2&gt;

&lt;p&gt;The real power move: a watchlist of items you know well, scanned every morning.&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;"items"&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="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"query"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Game Boy Micro"&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="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"query"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"PlayStation Vita OLED"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"priceMax"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;150&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="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"query"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"iPhone 13 128gb"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"marginMinPct"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;20&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="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"query"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Lego 75192"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"priceMax"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;500&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="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"country"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ES"&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;Schedule it in the Apify Console (Actor → Schedule → daily 8:00), wire the webhook to Telegram or Slack, and you'll wake up to a ranked list of real opportunities. Each item scan costs &lt;strong&gt;$0.04 flat&lt;/strong&gt; — a 10-item daily watchlist is ~$12/month, or roughly the profit of one decent flip.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cross-border mode
&lt;/h2&gt;

&lt;p&gt;Because valuation and scanning are decoupled, you can hunt arbitrage &lt;em&gt;between&lt;/em&gt; markets: scan French Vinted but value against German eBay (&lt;code&gt;country: "FR"&lt;/code&gt;, &lt;code&gt;marketplaceForComps: "ebay.de"&lt;/code&gt;). Retro games routinely price 15-30% apart between EU markets — that spread is the whole business model for some resellers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Let your AI agent do the shopping research
&lt;/h2&gt;

&lt;p&gt;The Actor doubles as an &lt;strong&gt;MCP tool&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;"mcpServers"&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;"deal-scanner"&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;"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://mcp.apify.com?tools=jdepablos/second-hand-deal-scanner-es"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"headers"&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;"Authorization"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bearer &amp;lt;APIFY_TOKEN&amp;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="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="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;Then: &lt;em&gt;"Is there any Game Boy Micro under €120 in Spain right now that flips for 30%+? Show the math."&lt;/em&gt; The agent runs the scan and reasons over &lt;code&gt;economics&lt;/code&gt; — no parsing on your side.&lt;/p&gt;

&lt;h2&gt;
  
  
  Honest limitations
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Estimates are estimates: condition, completeness (box/charger) and haggling move the final number. The valuation includes the comp URLs so you can verify before buying.&lt;/li&gt;
&lt;li&gt;Wallapop and Milanuncios are Spain-only; outside Spain the scanner uses the local Vinted.&lt;/li&gt;
&lt;li&gt;Zero deals on most days for most items is &lt;em&gt;correct behavior&lt;/em&gt; — markets are mostly efficient, and the scanner is honest about it. The profit is in the exceptions, which is why you automate the watching.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Wrap-up
&lt;/h2&gt;

&lt;p&gt;Stop valuing by gut feel. &lt;a href="https://apify.com/jdepablos/second-hand-deal-scanner-es" rel="noopener noreferrer"&gt;Try the Deal Scanner&lt;/a&gt; with a query you know well — the first scan costs 4 cents and will probably teach you something about your market. More copy-paste examples (including the standalone &lt;a href="https://apify.com/jdepablos/ebay-sold-price-appraiser" rel="noopener noreferrer"&gt;eBay Sold Price Appraiser&lt;/a&gt; that powers the valuations) live in the &lt;a href="https://github.com/JdPG23/data-actors-cookbook" rel="noopener noreferrer"&gt;cookbook repo&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>sidehustle</category>
      <category>api</category>
      <category>automation</category>
      <category>node</category>
    </item>
    <item>
      <title>How to Track US Startup Funding Rounds in Real Time (Before TechCrunch Writes About Them)</title>
      <dc:creator>Juan de Pablos</dc:creator>
      <pubDate>Fri, 03 Jul 2026 12:39:30 +0000</pubDate>
      <link>https://dev.to/jdpg23/how-to-track-us-startup-funding-rounds-in-real-time-before-techcrunch-writes-about-them-1n24</link>
      <guid>https://dev.to/jdpg23/how-to-track-us-startup-funding-rounds-in-real-time-before-techcrunch-writes-about-them-1n24</guid>
      <description>&lt;p&gt;Every week, &lt;strong&gt;over 1,300 US companies file a funding round with the SEC&lt;/strong&gt; — and most of them never appear in the tech press. If your job involves selling to funded startups, tracking competitors' war chests, or spotting investment trends, you're probably relying on funding newsletters and databases that are days late and hundreds of dollars per seat.&lt;/p&gt;

&lt;p&gt;There's a better way: go to the primary source. In this tutorial you'll build a &lt;strong&gt;real-time startup funding feed from SEC Form D filings&lt;/strong&gt; — the regulatory document every US company must file when it raises private capital. You'll get exact amounts, industries, locations and investor counts, as clean JSON, for a fraction of a cent per round.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Form D beats funding news
&lt;/h2&gt;

&lt;p&gt;When a startup raises money under Regulation D (the exemption used by virtually all US venture rounds), it must file &lt;strong&gt;Form D&lt;/strong&gt; with the SEC within 15 days of the first sale. That filing includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;strong&gt;exact amount sold so far&lt;/strong&gt; — not a journalist's "sources say" estimate&lt;/li&gt;
&lt;li&gt;The total offering size (or whether it's open-ended)&lt;/li&gt;
&lt;li&gt;Industry group, city and state&lt;/li&gt;
&lt;li&gt;Number of investors who participated&lt;/li&gt;
&lt;li&gt;Date of first sale and year of incorporation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Compare that to funding news: TechCrunch covers a tiny, PR-driven slice. Databases like Crunchbase aggregate press and manual research — comprehensive over time, but late and expensive. &lt;strong&gt;Form D is the ground truth both of them chase.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The catch? EDGAR (the SEC's database) is built for lawyers, not for automation. The filings are XML documents scattered across an archive, discoverable only through a quirky full-text search API with hidden rate limits. That's the part we'll automate.&lt;/p&gt;

&lt;h2&gt;
  
  
  The 5-minute setup
&lt;/h2&gt;

&lt;p&gt;We'll use the &lt;a href="https://apify.com/jdepablos/startup-funding-feed" rel="noopener noreferrer"&gt;Startup Funding Feed&lt;/a&gt; Actor — it handles EDGAR's discovery API, XML parsing, rate limits and pagination, and returns one JSON record per filing. It's pay-per-event: &lt;strong&gt;$0.002 per filing returned&lt;/strong&gt; (a full weekly sweep of all US rounds costs ~$2.60; a filtered slice costs cents). Failed fetches are never charged.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a free &lt;a href="https://apify.com" rel="noopener noreferrer"&gt;Apify account&lt;/a&gt; and grab your API token from Settings → API &amp;amp; Integrations.&lt;/li&gt;
&lt;li&gt;That's it. No servers, no cron, no XML parsing.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Your first funding sweep
&lt;/h2&gt;

&lt;p&gt;Let's get every round in California or New York from the last 7 days that already raised at least $1M:&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;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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://api.apify.com/v2/acts/jdepablos~startup-funding-feed/run-sync-get-dataset-items?timeout=300&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="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&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="na"&gt;Authorization&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Bearer &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="s2"&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;Content-Type&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;application/json&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="na"&gt;body&lt;/span&gt;&lt;span class="p"&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;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;daysBack&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;states&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;CA&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;NY&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="na"&gt;minAmountSold&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1000000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;maxFilings&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="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;rounds&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;/code&gt;&lt;/pre&gt;

&lt;/div&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;"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 Robotics, 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;"filedAt"&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-07-02"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"industry"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Other Technology"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"city"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"SAN FRANCISCO"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"stateDescription"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"CALIFORNIA"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"totalOfferingUsd"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;15000000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"totalSoldUsd"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;12500000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"investorsCount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"minInvestmentUsd"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;25000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"yearOfIncorporation"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2024"&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.sec.gov/Archives/edgar/data/..."&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;Every record links back to the official SEC document, so anything surprising is one click from verifiable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Recipe 1: A weekly leads feed for your CRM
&lt;/h2&gt;

&lt;p&gt;Companies that just raised have two things your sales team loves: &lt;strong&gt;budget and urgency&lt;/strong&gt;. A weekly filtered sweep, piped into your CRM, is one of the highest-signal lead sources that exists:&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="c1"&gt;// Monday morning: fintech + healthtech rounds over $2M, nationwide&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;daysBack&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;industries&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;banking&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;insurance&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;health&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;minAmountSold&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2000000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;includeAmendments&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="c1"&gt;// only NEW rounds, not updates&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Filter, dedupe against your CRM by company name, and enrich with your usual tools. The &lt;code&gt;investorsCount&lt;/code&gt; and &lt;code&gt;minInvestmentUsd&lt;/code&gt; fields are great qualifiers: 40 investors at $25k minimum is a party round; 2 investors at $2M is institutional conviction.&lt;/p&gt;

&lt;h2&gt;
  
  
  Recipe 2: Watch your competitive landscape
&lt;/h2&gt;

&lt;p&gt;VCs and corp-dev teams: replace "check Crunchbase weekly" with a filtered feed of your thesis area. Because Form D arrives &lt;strong&gt;before or without press coverage&lt;/strong&gt;, you'll regularly see rounds nobody has written about — which is exactly when a cold email is most welcome.&lt;/p&gt;

&lt;h2&gt;
  
  
  Recipe 3: Ask your AI agent instead
&lt;/h2&gt;

&lt;p&gt;The Actor is exposed as an &lt;strong&gt;MCP tool&lt;/strong&gt;, so agents like Claude can call it directly:&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;"mcpServers"&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;"funding-feed"&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;"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://mcp.apify.com?tools=jdepablos/startup-funding-feed"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"headers"&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;"Authorization"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bearer &amp;lt;APIFY_TOKEN&amp;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="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="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;Then just ask: &lt;em&gt;"Which biotech companies in Massachusetts raised over $5M this month? Give me the SEC links."&lt;/em&gt; The agent picks the filters, runs the feed, and reads the structured output — no glue code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scheduling it (the set-and-forget part)
&lt;/h2&gt;

&lt;p&gt;In the Apify Console, open the Actor → &lt;strong&gt;Schedule&lt;/strong&gt;, pick weekly (Mondays 8:00 work well — Form D filings cluster early in the week), and connect the run to wherever you want the data: a webhook to your CRM, a Google Sheet via the integrations tab, or Slack.&lt;/p&gt;

&lt;p&gt;Total cost of a serious weekly setup: filtered feeds usually return 20-80 filings → &lt;strong&gt;under $0.20 per week&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you can't get from Form D (honesty section)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Valuations&lt;/strong&gt; — Form D discloses amounts raised, not share prices.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Investor names&lt;/strong&gt; — the filing lists officers/directors/promoters, not fund names. (The linked document sometimes reveals more.)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Non-US rounds&lt;/strong&gt; — this is SEC data; it covers US offerings, including foreign issuers that file with the SEC.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For most sales, research and monitoring workflows, none of that matters: the &lt;em&gt;who/where/how much/when&lt;/em&gt; is the signal.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrap-up
&lt;/h2&gt;

&lt;p&gt;You now have a primary-source funding feed that runs itself: official data, exact amounts, days ahead of the press, at pay-per-use prices. Try the &lt;a href="https://apify.com/jdepablos/startup-funding-feed" rel="noopener noreferrer"&gt;Startup Funding Feed on Apify&lt;/a&gt; — the first runs cost pennies, and the &lt;a href="https://github.com/JdPG23/data-actors-cookbook" rel="noopener noreferrer"&gt;cookbook repo&lt;/a&gt; has copy-paste examples for this and seven other official-source data APIs (insider trading, trademarks, clinical trials and more).&lt;/p&gt;

&lt;p&gt;Questions or feature requests? The Issues tab on the Actor page gets answered within a day.&lt;/p&gt;

</description>
      <category>startup</category>
      <category>api</category>
      <category>automation</category>
      <category>node</category>
    </item>
  </channel>
</rss>
