<?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: Mohamed Senator</title>
    <description>The latest articles on DEV Community by Mohamed Senator (@s3nafps).</description>
    <link>https://dev.to/s3nafps</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%2F4165217%2F19ec0537-cd23-4719-b434-84ff66d83855.jpg</url>
      <title>DEV Community: Mohamed Senator</title>
      <link>https://dev.to/s3nafps</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/s3nafps"/>
    <language>en</language>
    <item>
      <title>How to scrape Bilibili search, comments and trending boards without logging in</title>
      <dc:creator>Mohamed Senator</dc:creator>
      <pubDate>Wed, 21 Oct 2026 09:05:44 +0000</pubDate>
      <link>https://dev.to/s3nafps/how-to-scrape-bilibili-search-comments-and-trending-boards-without-logging-in-524a</link>
      <guid>https://dev.to/s3nafps/how-to-scrape-bilibili-search-comments-and-trending-boards-without-logging-in-524a</guid>
      <description>&lt;p&gt;&lt;em&gt;Disclosure: I built the Actor used in this tutorial, &lt;a href="https://apify.com/s3nafps/bilibili-scraper" rel="noopener noreferrer"&gt;Bilibili Scraper&lt;/a&gt; on Apify.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjyp81xx81kvxs5wjiho7.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjyp81xx81kvxs5wjiho7.gif" alt="Bilibili Scraper demo" width="720" height="405"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://s3nafps.github.io/media/bilibili-scraper/promo.mp4" rel="noopener noreferrer"&gt;Watch the full video&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Bilibili (哔哩哔哩, "B站") is where a big part of China's gaming, anime, tech and creator conversation happens. If you track a brand, a game launch or a topic in the Chinese market, you want three things from it: which videos match your keywords, what people say in the comments, and what's on the trending boards. Doing that by hand means a Chinese UI, login walls and pagination quirks.&lt;/p&gt;

&lt;p&gt;This tutorial shows how to pull all three as clean JSON with one Actor, no Bilibili account needed.&lt;/p&gt;
&lt;h2&gt;
  
  
  What you get
&lt;/h2&gt;

&lt;p&gt;Every row has the same shape, whatever mode you run:&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;"platform"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bilibili"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"video"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"BV1Wenc6aExW"&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.bilibili.com/video/BV1Wenc6aExW"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&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;"authorName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&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;"authorUrl"&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://space.bilibili.com/130929912"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"createdAt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-10-20T18:01:45+08: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;"views"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;21338&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"likes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;810&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"comments"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;239&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"favorites"&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;"platformMetrics"&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;"danmaku"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;273&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"duration"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;384&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"tags"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"原神UP主激励计划,搞笑,..."&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;"keyword"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&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;That's a real row from a search run for 原神 (Genshin Impact) on Oct 20, 2026. Comments come back as &lt;code&gt;type: "comment"&lt;/code&gt; with a &lt;code&gt;parentId&lt;/code&gt; pointing at the video, and trending items as &lt;code&gt;type: "trend"&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Search videos by keyword
&lt;/h2&gt;



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

&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ApifyClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;YOUR_APIFY_TOKEN&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;run&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;actor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;s3nafps/bilibili-scraper&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;run_input&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;mode&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;search&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;keywords&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;原神&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;maxItems&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sort&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;relevance&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;defaultDatasetId&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nf"&gt;iterate_items&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;views&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;likes&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][:&lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In my test run this returned 88 videos in about 31 seconds.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Pull comments (and replies) for a video
&lt;/h2&gt;

&lt;p&gt;Pass a BV id or a video URL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;run&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;actor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;s3nafps/bilibili-scraper&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;run_input&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;mode&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;comments&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;urls&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;BV12ez4BHEDM&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;maxItems&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;includeReplies&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;commentSort&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;hot&lt;/span&gt;&lt;span class="sh"&gt;"&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;You can also run &lt;code&gt;mode: "details"&lt;/code&gt; with &lt;code&gt;includeComments: true&lt;/code&gt; to get video stats and the top comments in one go.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Read a trending board
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;run&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;actor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;s3nafps/bilibili-scraper&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;run_input&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;mode&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;trending&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;trendingCategory&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;game&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;maxItems&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&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;The game board run took about 14 seconds for 10 items.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Daily brand monitoring with only-new mode
&lt;/h2&gt;

&lt;p&gt;Set &lt;code&gt;onlyNew: true&lt;/code&gt; and give the job a &lt;code&gt;monitorName&lt;/code&gt;. The Actor remembers what it already returned for that monitor, so a daily schedule on Apify only hands you videos you haven't seen yet. Pipe that into Slack, a sheet, or your agent.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;run_input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;mode&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;search&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;keywords&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;你的品牌名&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;onlyNew&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;monitorName&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;my-brand-daily&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;maxItems&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Creator profiles
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;mode: "profiles"&lt;/code&gt; takes &lt;code&gt;space.bilibili.com&lt;/code&gt; links or plain UIDs and returns one profile row each.&lt;/p&gt;

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

&lt;p&gt;It's pay per result, with no subscription. On Apify's free plan it's $2.50 per 1,000 videos or trending items, $1 per 1,000 comments, and $3 per 1,000 profiles, and it gets cheaper on higher Apify plans. So the 88-video search above works out to about $0.22.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use it from an AI agent
&lt;/h2&gt;

&lt;p&gt;Like any Apify Actor, it's callable over Apify's MCP server, so Claude, Cursor or your own agent can search Bilibili and read comments as a tool call.&lt;/p&gt;

&lt;p&gt;Try it here: &lt;a href="https://apify.com/s3nafps/bilibili-scraper" rel="noopener noreferrer"&gt;Bilibili Scraper on Apify&lt;/a&gt;. If a field you need is missing, tell me in the comments.&lt;/p&gt;

</description>
      <category>webscraping</category>
      <category>python</category>
      <category>api</category>
      <category>datascience</category>
    </item>
    <item>
      <title>How to compare 500 keywords in Google Trends on one scale (not 5 at a time)</title>
      <dc:creator>Mohamed Senator</dc:creator>
      <pubDate>Tue, 20 Oct 2026 12:59:09 +0000</pubDate>
      <link>https://dev.to/s3nafps/how-to-compare-500-keywords-in-google-trends-on-one-scale-not-5-at-a-time-50o1</link>
      <guid>https://dev.to/s3nafps/how-to-compare-500-keywords-in-google-trends-on-one-scale-not-5-at-a-time-50o1</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F28xox4tq4l176yivz81k.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F28xox4tq4l176yivz81k.gif" alt="Google Trends Bulk Compare demo" width="760" height="428"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://s3nafps.github.io/media/google-trends-bulk-compare/promo.mp4" rel="noopener noreferrer"&gt;Watch the full video&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you have ever tried to rank a long keyword list with Google Trends, you have hit the wall: the website compares &lt;strong&gt;5 terms at a time&lt;/strong&gt;, and every chart rescales its own biggest term to 100. A "60" in one chart and a "60" in another don't mean the same thing.&lt;/p&gt;

&lt;p&gt;This post shows how to get around that and put hundreds of keywords (up to 500) on &lt;strong&gt;one shared 0–100 scale&lt;/strong&gt;, with a real input, real output and code. Disclosure: the tool I use is my own Apify Actor, &lt;strong&gt;&lt;a href="https://apify.com/s3nafps/google-trends-bulk-compare" rel="noopener noreferrer"&gt;Google Trends Scraper: Bulk Compare 100+ Keywords&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  The 5-keyword problem
&lt;/h2&gt;

&lt;p&gt;Say you want to know which of 20 coffee drinks people search for most. On the Google Trends website you run 4 comparisons of 5. In each one, the top term becomes 100 and everything else is relative to it. So:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You can't compare "matcha" from chart 1 with "oat milk" from chart 4.&lt;/li&gt;
&lt;li&gt;If one huge term (like "coffee") sits in a chart, everything small next to it rounds down to 0 or 1, because Google reports whole numbers.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The manual workaround is to put one shared keyword in every chart and rescale by hand. It works, but it is slow, and it breaks when the shared keyword is much bigger or smaller than the others.&lt;/p&gt;
&lt;h2&gt;
  
  
  How the Actor solves it
&lt;/h2&gt;

&lt;p&gt;It does the linking automatically:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A quick first pass sorts your keywords by size.&lt;/li&gt;
&lt;li&gt;Keywords are compared in groups of 5. Each group shares one keyword with the next group, so neighbours are always of similar size and small keywords keep their precision.&lt;/li&gt;
&lt;li&gt;Every group is rescaled through its shared keyword, and the single biggest value across all your keywords becomes 100.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The result is one ranked table where a score means the same thing in every row.&lt;/p&gt;

&lt;p&gt;If you'd rather measure everything against one reference, such as your brand, set &lt;code&gt;anchorKeyword&lt;/code&gt; and that keyword is included in every comparison instead.&lt;/p&gt;
&lt;h2&gt;
  
  
  Walkthrough
&lt;/h2&gt;
&lt;h3&gt;
  
  
  1. The input
&lt;/h3&gt;

&lt;p&gt;This is the input I ran, the same 20 drinks the Actor's input form is prefilled with:&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;"keywords"&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="s2"&gt;"coffee"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"tea"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"matcha"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"espresso"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"latte"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"cold brew"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"cappuccino"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"green tea"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"chai"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"iced coffee"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"mocha"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"flat white"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"french press"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"decaf"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"oat milk"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"bubble tea"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"kombucha"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"hot chocolate"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"energy drink"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"yerba mate"&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;"geo"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&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;"timeRange"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"today 12-m"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"searchType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"web"&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 input fields:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Field&lt;/th&gt;
&lt;th&gt;Default&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;keywords&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;required&lt;/td&gt;
&lt;td&gt;1 to 500 keywords. Duplicates and blank lines are removed. Any language.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;geo&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;""&lt;/code&gt; (worldwide)&lt;/td&gt;
&lt;td&gt;Country code, for example &lt;code&gt;US&lt;/code&gt;, &lt;code&gt;SA&lt;/code&gt; or &lt;code&gt;AE&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;timeRange&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;today 12-m&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;now 1-H&lt;/code&gt;, &lt;code&gt;now 7-d&lt;/code&gt;, &lt;code&gt;today 3-m&lt;/code&gt;, &lt;code&gt;today 12-m&lt;/code&gt;, &lt;code&gt;today 5-y&lt;/code&gt;, &lt;code&gt;all&lt;/code&gt; (since 2004) and more.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;customTimeRange&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;none&lt;/td&gt;
&lt;td&gt;For example &lt;code&gt;2024-01-01 2024-06-30&lt;/code&gt;. Overrides &lt;code&gt;timeRange&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;searchType&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;web&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;web&lt;/code&gt;, &lt;code&gt;youtube&lt;/code&gt;, &lt;code&gt;news&lt;/code&gt;, &lt;code&gt;images&lt;/code&gt; or &lt;code&gt;shopping&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;category&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;0&lt;/code&gt; (all)&lt;/td&gt;
&lt;td&gt;Limit to one Google category.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;anchorKeyword&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;none&lt;/td&gt;
&lt;td&gt;Put one keyword (your brand, say) in every comparison.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;includeRelated&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;false&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Top and rising related searches per keyword.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;includeRegions&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;false&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Interest by country, or by region inside the chosen country.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;language&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;en&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Language for related searches and place names, for example &lt;code&gt;ar&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  2. The output
&lt;/h3&gt;

&lt;p&gt;Here is the ranked table from that run (worldwide, past 12 months, run on October 18, 2026). &lt;code&gt;averageScore&lt;/code&gt; is the keyword's average over the period on the shared scale:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Rank&lt;/th&gt;
&lt;th&gt;Keyword&lt;/th&gt;
&lt;th&gt;averageScore&lt;/th&gt;
&lt;th&gt;trend&lt;/th&gt;
&lt;th&gt;change3m&lt;/th&gt;
&lt;th&gt;change12m&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;coffee&lt;/td&gt;
&lt;td&gt;73.9&lt;/td&gt;
&lt;td&gt;rising&lt;/td&gt;
&lt;td&gt;−7%&lt;/td&gt;
&lt;td&gt;+16%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;tea&lt;/td&gt;
&lt;td&gt;39.1&lt;/td&gt;
&lt;td&gt;flat&lt;/td&gt;
&lt;td&gt;−10%&lt;/td&gt;
&lt;td&gt;+1%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;espresso&lt;/td&gt;
&lt;td&gt;9.2&lt;/td&gt;
&lt;td&gt;flat&lt;/td&gt;
&lt;td&gt;−7%&lt;/td&gt;
&lt;td&gt;+14%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;matcha&lt;/td&gt;
&lt;td&gt;8.3&lt;/td&gt;
&lt;td&gt;rising&lt;/td&gt;
&lt;td&gt;−9%&lt;/td&gt;
&lt;td&gt;+14%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;latte&lt;/td&gt;
&lt;td&gt;7.7&lt;/td&gt;
&lt;td&gt;rising&lt;/td&gt;
&lt;td&gt;+13%&lt;/td&gt;
&lt;td&gt;+14%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;chai&lt;/td&gt;
&lt;td&gt;5.5&lt;/td&gt;
&lt;td&gt;rising&lt;/td&gt;
&lt;td&gt;+12%&lt;/td&gt;
&lt;td&gt;+24%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;green tea&lt;/td&gt;
&lt;td&gt;3.6&lt;/td&gt;
&lt;td&gt;flat&lt;/td&gt;
&lt;td&gt;−21%&lt;/td&gt;
&lt;td&gt;−1%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;energy drink&lt;/td&gt;
&lt;td&gt;2.6&lt;/td&gt;
&lt;td&gt;rising&lt;/td&gt;
&lt;td&gt;−4%&lt;/td&gt;
&lt;td&gt;+15%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;mocha&lt;/td&gt;
&lt;td&gt;2.0&lt;/td&gt;
&lt;td&gt;flat&lt;/td&gt;
&lt;td&gt;+10%&lt;/td&gt;
&lt;td&gt;+1%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;cold brew&lt;/td&gt;
&lt;td&gt;1.8&lt;/td&gt;
&lt;td&gt;rising&lt;/td&gt;
&lt;td&gt;−21%&lt;/td&gt;
&lt;td&gt;+75%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;…&lt;/td&gt;
&lt;td&gt;…&lt;/td&gt;
&lt;td&gt;…&lt;/td&gt;
&lt;td&gt;…&lt;/td&gt;
&lt;td&gt;…&lt;/td&gt;
&lt;td&gt;…&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;20&lt;/td&gt;
&lt;td&gt;yerba mate&lt;/td&gt;
&lt;td&gt;0.5&lt;/td&gt;
&lt;td&gt;rising&lt;/td&gt;
&lt;td&gt;−42%&lt;/td&gt;
&lt;td&gt;+5%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Your numbers will differ, because they come from Google at run time.&lt;/p&gt;

&lt;p&gt;Notice that "yerba mate" gets a usable 0.5 even though "coffee" is about 150 times bigger. In a single 5-term chart next to "coffee", it would round to 0 or 1.&lt;/p&gt;

&lt;p&gt;Each dataset row looks like this (a real row from a separate US run on October 20, 2026, with the weekly timeline shortened):&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;"keyword"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"matcha"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"rank"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"averageScore"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;6.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;"peakScore"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"peakDate"&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-04-05"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"latestScore"&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;"change3m"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;-31&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"change12m"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"trend"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"rising"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"peakMonth"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"breakout"&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;"lowVolume"&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;"noData"&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;"timeline"&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;"date"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2025-10-19"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"score"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&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;"date"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2025-10-26"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"score"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&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;"searchType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"web"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"geo"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"US"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"timeRange"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"today 12-m"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"anchorKeyword"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&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;What the fields mean:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;trend&lt;/code&gt; is &lt;code&gt;rising&lt;/code&gt;, &lt;code&gt;falling&lt;/code&gt;, &lt;code&gt;flat&lt;/code&gt; or &lt;code&gt;seasonal&lt;/code&gt;. It describes the whole period, while &lt;code&gt;change3m&lt;/code&gt; only looks at the last 3 months, so they can point in different directions (cold brew above: rising over the year, down 21% over 3 months).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;peakMonth&lt;/code&gt; is filled for ranges of 2 years or more.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;breakout&lt;/code&gt; is true when the last few points are more than double the keyword's average.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;lowVolume&lt;/code&gt; is true when Google rounds a keyword to 0 or 1, so its exact size is uncertain. Compare it again in a list of similar-sized keywords.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;noData: true&lt;/code&gt; means Google has no data for that keyword. These keywords aren't charged.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The run also saves a &lt;strong&gt;SUMMARY&lt;/strong&gt; record in the key-value store with the ranked table and one sentence. For the 20-drink run it was:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Top by interest: coffee (73.9), tea (39.1), espresso (9.2). Fastest rising over 3 months: hot chocolate (+28%), latte (+13%), chai (+12%).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's short enough to paste straight into an LLM prompt or a Slack message.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. What 500 keywords looks like
&lt;/h3&gt;

&lt;p&gt;I also ran a 500-keyword list (worldwide, past 12 months): broad terms like "chatgpt", "iphone" and "coffee" plus long-tail variants like "lipstick deals". It finished in about 7 minutes. Every keyword came back on the same scale, from 71.1 for "chatgpt" at the top down to 0.00033 for "lipstick deals" at rank 500. On the Google Trends website, anything below 1 in a chart just shows as 0, so you couldn't rank those long-tail terms against each other.&lt;/p&gt;

&lt;h2&gt;
  
  
  Run it from code
&lt;/h2&gt;

&lt;p&gt;Python (&lt;code&gt;pip install apify-client&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

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

&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ApifyClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;APIFY_TOKEN&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="n"&gt;keywords&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;keywords.txt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()]&lt;/span&gt;  &lt;span class="c1"&gt;# up to 500
&lt;/span&gt;
&lt;span class="n"&gt;run&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;actor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;s3nafps/google-trends-bulk-compare&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;run_input&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;keywords&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;keywords&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;geo&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;US&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;timeRange&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;today 12-m&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;searchType&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;web&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="n"&gt;rows&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;defaultDatasetId&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nf"&gt;iterate_items&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;rank&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;keyword&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;averageScore&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;trend&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;change3m&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="n"&gt;summary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;key_value_store&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;defaultKeyValueStoreId&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nf"&gt;get_record&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SUMMARY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;summary&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;value&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To get a spreadsheet, export the dataset as CSV or Excel from the run's Output tab, or with pandas:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;pandas&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;

&lt;span class="n"&gt;df&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;DataFrame&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;drop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;columns&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;timeline&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_csv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;trends_ranked.csv&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also run it from n8n, Make or Zapier with the Apify "Run Actor" node, or give it to an AI agent through MCP at &lt;code&gt;https://mcp.apify.com/?actors=s3nafps/google-trends-bulk-compare&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use cases
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;SEO keyword research:&lt;/strong&gt; rank 50–500 keyword ideas by real search interest before you write a page, and use &lt;code&gt;lowVolume&lt;/code&gt; to spot terms that are too small to bother with.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Content planning:&lt;/strong&gt; find the topics with positive &lt;code&gt;change3m&lt;/code&gt; / &lt;code&gt;change12m&lt;/code&gt; or a &lt;code&gt;breakout&lt;/code&gt; flag, and use &lt;code&gt;peakMonth&lt;/code&gt; (with a 2-year+ range) to time seasonal posts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Market research:&lt;/strong&gt; compare the same product list across countries by changing &lt;code&gt;geo&lt;/code&gt;, or set &lt;code&gt;searchType&lt;/code&gt; to &lt;code&gt;shopping&lt;/code&gt; (Google Shopping) or &lt;code&gt;youtube&lt;/code&gt; to see product demand. Add &lt;code&gt;includeRegions&lt;/code&gt; to see where interest is concentrated.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Brand tracking:&lt;/strong&gt; set your brand as &lt;code&gt;anchorKeyword&lt;/code&gt;, add competitors, and schedule a weekly run.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Pricing
&lt;/h2&gt;

&lt;p&gt;Pay per keyword, checked today on the Store:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;$0.002 per keyword compared&lt;/strong&gt; on the Free and Bronze plans (Silver $0.0018, Gold and above $0.0015).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;$0.00005 start fee&lt;/strong&gt; per run.&lt;/li&gt;
&lt;li&gt;Related searches and regions are included at no extra cost.&lt;/li&gt;
&lt;li&gt;Keywords that fail, can't be linked to the shared scale, or have no data aren't charged.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's about $0.20 for 100 keywords and $1.00 for 500 on the free plan.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Why don't my scores match the Google Trends website?&lt;/strong&gt;&lt;br&gt;
On the website, each chart scales its own biggest term to 100. Here, the biggest term across all your keywords is 100. If you compare the same 5 keywords on their own, the ratios between them match the website.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How long does a run take?&lt;/strong&gt;&lt;br&gt;
A 100-keyword run usually takes 1 to 3 minutes. My 500-keyword run took about 7 minutes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why did one keyword get an error instead of a score?&lt;/strong&gt;&lt;br&gt;
A keyword more than about 200 times bigger or smaller than everything it was compared with shows as 0 on Google and can't be linked to the scale. The Actor keeps the biggest set of keywords it can link, reports the outlier with an error and doesn't charge for it. Compare it in a separate list of similar-sized keywords.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Which countries and languages work?&lt;/strong&gt;&lt;br&gt;
Every country Google Trends supports, and keywords in any language, including Arabic.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can I schedule it?&lt;/strong&gt;&lt;br&gt;
Yes. Use Apify Schedules to run the same list daily or weekly, and connect a webhook, Google Sheets or Slack to get the new ranking automatically.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is it allowed?&lt;/strong&gt;&lt;br&gt;
The Actor only reads public, aggregated Google Trends data, with no personal data. Check that your use complies with Google's terms and the laws that apply to you.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;&lt;a href="https://apify.com/s3nafps/google-trends-bulk-compare" rel="noopener noreferrer"&gt;Google Trends Scraper: Bulk Compare 100+ Keywords on the Apify Store&lt;/a&gt;&lt;/strong&gt;. The input form is prefilled with the 20 drinks above, so you can see a full ranked table for about $0.04.&lt;/p&gt;

&lt;p&gt;If a keyword list gives odd results, open an issue on the Actor page with the input so I can look at it.&lt;/p&gt;

</description>
      <category>seo</category>
      <category>googletrends</category>
      <category>python</category>
      <category>datascience</category>
    </item>
    <item>
      <title>Turn any docs site into RAG-ready Markdown (without writing a scraper)</title>
      <dc:creator>Mohamed Senator</dc:creator>
      <pubDate>Tue, 20 Oct 2026 12:59:09 +0000</pubDate>
      <link>https://dev.to/s3nafps/turn-any-docs-site-into-rag-ready-markdown-without-writing-a-scraper-ih</link>
      <guid>https://dev.to/s3nafps/turn-any-docs-site-into-rag-ready-markdown-without-writing-a-scraper-ih</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhudj48gi5mx49lua5ooj.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhudj48gi5mx49lua5ooj.gif" alt="AI-Ready Webpage Extractor demo" width="760" height="428"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://s3nafps.github.io/media/ai-ready-webpage-extractor/promo.mp4" rel="noopener noreferrer"&gt;Watch the full video&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Every RAG project I have seen starts the same way: someone wants a chatbot that answers questions from a documentation site, and the first week disappears into scraping. Menus end up in your embeddings, code blocks lose their language, JavaScript-rendered pages come back empty, and chunks split in the middle of a sentence.&lt;/p&gt;

&lt;p&gt;I got tired of redoing this, so I built a small tool for it: &lt;strong&gt;&lt;a href="https://apify.com/s3nafps/ai-ready-webpage-extractor" rel="noopener noreferrer"&gt;Website to Markdown Crawler for LLMs &amp;amp; RAG&lt;/a&gt;&lt;/strong&gt; on the Apify Store. Disclosure: it is my own Actor. This post shows the approach, the exact input I use and how to load the result into a vector store. The ideas apply to any scraper.&lt;/p&gt;
&lt;h2&gt;
  
  
  What "RAG-ready" actually means
&lt;/h2&gt;

&lt;p&gt;Raw HTML is the wrong input for an embedding model. What you want per page is:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Only the main content.&lt;/strong&gt; No nav bars, cookie banners, footers, share buttons or sidebars.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Structure kept as Markdown.&lt;/strong&gt; Headings, lists, tables and fenced code blocks with their language, so an LLM can read it and you can chunk on headings.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Chunks that respect headings.&lt;/strong&gt; Each chunk should know which section it came from (for example &lt;code&gt;First crawler &amp;gt; Building a crawler&lt;/code&gt;), stay under a size limit, and overlap a little with the previous chunk so context isn't lost at the boundary.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Metadata.&lt;/strong&gt; Title, description, canonical URL, language and dates, for filtering and for citing sources in answers.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  The three problems that cost the most time
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;JavaScript-rendered pages.&lt;/strong&gt; Many docs sites are React or Next.js apps, and a plain HTTP fetch returns an almost empty shell. The cheap fix is to fetch with plain HTTP first, check whether the extracted content looks missing, and only then open the page in a real browser. Rendering every page in a browser works too, but it is much slower.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Finding every page.&lt;/strong&gt; Following links works, but a sitemap is usually faster and more complete. Some sites have no usable sitemap, so you need a fallback to link-following.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Chunking.&lt;/strong&gt; Splitting every N characters cuts code blocks and sentences in half. Splitting on headings first and then packing up to a token limit keeps each chunk coherent.&lt;/p&gt;

&lt;p&gt;The Actor handles all three. &lt;code&gt;renderJavaScript: "auto"&lt;/code&gt; (the default) does the HTTP-first, browser-only-when-needed fetch; &lt;code&gt;crawlMode: "sitemap"&lt;/code&gt; falls back to following links when a site has no sitemap; and &lt;code&gt;chunkContent: true&lt;/code&gt; adds heading-aware chunks.&lt;/p&gt;
&lt;h2&gt;
  
  
  Step by step: one docs section into chunks
&lt;/h2&gt;
&lt;h3&gt;
  
  
  1. Pick the start URL and a filter
&lt;/h3&gt;

&lt;p&gt;Use the docs homepage as the start URL and &lt;code&gt;urlMustContain&lt;/code&gt; to stay inside the section you care about. Start URLs are always extracted; crawled or sitemap URLs must contain the filter text.&lt;/p&gt;
&lt;h3&gt;
  
  
  2. Choose a crawl mode
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;none&lt;/code&gt; (default): only the URLs you list.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;sameDomain&lt;/code&gt;: also follow links on the same site, up to &lt;code&gt;maxCrawlDepth&lt;/code&gt; (default 2).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;sitemap&lt;/code&gt;: use the site's sitemap. Usually the fastest way to get a whole docs site.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  3. Turn on chunking and set a page cap
&lt;/h3&gt;

&lt;p&gt;This is the input I use:&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;"urls"&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;"https://docs.apify.com"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"crawlMode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sitemap"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"urlMustContain"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"docs.apify.com/actors/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"maxPages"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"chunkContent"&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;"chunkSizeTokens"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;800&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"chunkOverlapTokens"&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="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;Other useful options: &lt;code&gt;outputFormat&lt;/code&gt; (&lt;code&gt;markdown&lt;/code&gt;, &lt;code&gt;text&lt;/code&gt; or &lt;code&gt;both&lt;/code&gt;), &lt;code&gt;removeBoilerplate&lt;/code&gt; (on by default), &lt;code&gt;includeLinks&lt;/code&gt;, &lt;code&gt;includeTables&lt;/code&gt; and &lt;code&gt;includeMetadata&lt;/code&gt; (all on by default), &lt;code&gt;renderJavaScript&lt;/code&gt; (&lt;code&gt;auto&lt;/code&gt;, &lt;code&gt;never&lt;/code&gt; or &lt;code&gt;always&lt;/code&gt;) and &lt;code&gt;maxConcurrency&lt;/code&gt; (default 5).&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Run it from code
&lt;/h3&gt;

&lt;p&gt;With the Python client (&lt;code&gt;pip install apify-client&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

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

&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ApifyClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;APIFY_TOKEN&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="n"&gt;run&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;actor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;s3nafps/ai-ready-webpage-extractor&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;run_input&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;urls&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://docs.apify.com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;crawlMode&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sitemap&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;urlMustContain&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;docs.apify.com/actors/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;maxPages&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;chunkContent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;chunkSizeTokens&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;800&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;chunkOverlapTokens&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="n"&gt;pages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;defaultDatasetId&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nf"&gt;iterate_items&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pages&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pages&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;pages&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;title&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;estimatedTokens&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;chunks&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or in JavaScript (&lt;code&gt;npm install apify-client&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

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

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ApifyClient&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;APIFY_TOKEN&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;run&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;actor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;s3nafps/ai-ready-webpage-extractor&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;urls&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;https://docs.apify.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;crawlMode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sitemap&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;urlMustContain&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;docs.apify.com/actors/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;maxPages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;chunkContent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;run&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;defaultDatasetId&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;listItems&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;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="nx"&gt;items&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pages&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  5. What comes back
&lt;/h3&gt;

&lt;p&gt;One dataset item per page. Here is a real item from a crawl of the Crawlee docs, shortened. The chunk starts with text from the end of the previous chunk; that is the overlap.&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;"sourceUrl"&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://crawlee.dev/js/docs/introduction/first-crawler"&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;"First crawler | Crawlee for JavaScript · Build reliable crawlers. Fast."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Your first steps into the world of scraping with Crawlee"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"canonicalUrl"&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://crawlee.dev/js/docs/introduction/first-crawler"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"language"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"en"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mainContentMarkdown"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Version: 3.18&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;# First crawler&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;Now, you will build your first crawler..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"chunks"&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;"index"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"headingPath"&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;"First crawler"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Building a crawler"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Building a CheerioCrawler"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bulk add requests&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;The code above is for illustration of the request queue concept..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"estimatedTokens"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;647&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;"headings"&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;"level"&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;"text"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"First crawler"&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;"wordCount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1219&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"estimatedTokens"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2236&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"renderedWithBrowser"&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;"crawlDepth"&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;"extractionStatus"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"succeeded"&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;Each item also has &lt;code&gt;finalUrl&lt;/code&gt;, &lt;code&gt;httpStatus&lt;/code&gt;, &lt;code&gt;author&lt;/code&gt;, &lt;code&gt;publishedDate&lt;/code&gt;, &lt;code&gt;modifiedDate&lt;/code&gt;, &lt;code&gt;links&lt;/code&gt;, &lt;code&gt;tables&lt;/code&gt;, &lt;code&gt;images&lt;/code&gt;, &lt;code&gt;schemaOrgData&lt;/code&gt; and &lt;code&gt;scrapedAt&lt;/code&gt;. The dataset has two views: &lt;strong&gt;Overview&lt;/strong&gt; (one row per page) and &lt;strong&gt;RAG chunks&lt;/strong&gt; (one row per chunk). Failed pages and their reasons go into the &lt;code&gt;OUTPUT&lt;/code&gt; record in the run's key-value store.&lt;/p&gt;

&lt;p&gt;That Crawlee crawl (&lt;code&gt;sameDomain&lt;/code&gt; mode, &lt;code&gt;urlMustContain: "/js/docs/"&lt;/code&gt;, 25 pages) is also a good warning: 9 of the 25 pages were copies of the Quick Start page under different version folders (&lt;code&gt;/docs/3.11/&lt;/code&gt;, &lt;code&gt;/docs/3.12/&lt;/code&gt; and so on). Check your first run for versioned duplicates and tighten &lt;code&gt;urlMustContain&lt;/code&gt; before crawling more.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Embed the chunks
&lt;/h3&gt;

&lt;p&gt;From here, embedding is a loop over &lt;code&gt;chunks&lt;/code&gt;, using &lt;code&gt;headingPath&lt;/code&gt; and &lt;code&gt;sourceUrl&lt;/code&gt; as metadata:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;pages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;chunk&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;chunks&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
        &lt;span class="n"&gt;vector_store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="n"&gt;metadata&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;url&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;canonicalUrl&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sourceUrl&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;title&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;title&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;section&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; &amp;gt; &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;h&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="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\u200b&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;h&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;headingPath&lt;/span&gt;&lt;span class="sh"&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;replace("\u200b", "")&lt;/code&gt; is there because some docs themes put an invisible zero-width space in heading anchors, and it ends up in the heading text.&lt;/p&gt;

&lt;p&gt;If you use LangChain, &lt;code&gt;ApifyDatasetLoader&lt;/code&gt; from &lt;code&gt;langchain_community.document_loaders&lt;/code&gt; can load the dataset directly; map &lt;code&gt;mainContentMarkdown&lt;/code&gt; (or each chunk) to a &lt;code&gt;Document&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pricing
&lt;/h2&gt;

&lt;p&gt;Pay per page, no subscription. Prices I checked today on the Store:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;$0.003 per extracted page&lt;/strong&gt; on the free plan (Bronze $0.0027, Silver $0.0024, Gold and above $0.002).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;$0.00005 start fee&lt;/strong&gt; per run (per GB of memory, default 1 GB).&lt;/li&gt;
&lt;li&gt;Failed pages are not charged. Browser rendering and chunking are included.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So a 100-page docs section costs about $0.30 on the free plan, and 1,000 pages about $3. Set &lt;code&gt;maxPages&lt;/code&gt;, and &lt;strong&gt;Maximum cost per run&lt;/strong&gt; in the run options, to cap any run.&lt;/p&gt;

&lt;h2&gt;
  
  
  Things to know before you use it
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Public pages only. No logins, cookies, paywalls or CAPTCHA solving.&lt;/li&gt;
&lt;li&gt;Non-HTML files such as PDFs are skipped.&lt;/li&gt;
&lt;li&gt;Main-content detection is heuristic. Unusual layouts can include a little extra text, so spot-check a few pages before you embed thousands.&lt;/li&gt;
&lt;li&gt;Token counts are estimates (about 4 characters per token).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Does it work on JavaScript sites?&lt;/strong&gt;&lt;br&gt;
Yes. With &lt;code&gt;renderJavaScript: "auto"&lt;/code&gt;, pages are fetched as plain HTML first and opened in a real browser only when the content comes back missing. Each item's &lt;code&gt;renderedWithBrowser&lt;/code&gt; field tells you which path was used. For app-like sites that still come back short, set &lt;code&gt;"always"&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What if the site has no sitemap?&lt;/strong&gt;&lt;br&gt;
Sitemap mode falls back to following links from your start URL. You can also use &lt;code&gt;sameDomain&lt;/code&gt; mode with &lt;code&gt;maxCrawlDepth&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How do I keep costs predictable?&lt;/strong&gt;&lt;br&gt;
Set &lt;code&gt;maxPages&lt;/code&gt; and a maximum cost per run. The run stops cleanly at the limit, and failed pages are never charged.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Which vector databases does it work with?&lt;/strong&gt;&lt;br&gt;
Any. The output is plain JSON with text, heading paths and token estimates, so it works with Pinecone, Qdrant, Weaviate, pgvector or a local store.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can an AI agent call it directly?&lt;/strong&gt;&lt;br&gt;
Yes, through the Apify MCP server: add &lt;code&gt;https://mcp.apify.com/?actors=s3nafps/ai-ready-webpage-extractor&lt;/code&gt; to Claude, Cursor or another MCP client.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;&lt;a href="https://apify.com/s3nafps/ai-ready-webpage-extractor" rel="noopener noreferrer"&gt;Website to Markdown Crawler for LLMs &amp;amp; RAG on the Apify Store&lt;/a&gt;&lt;/strong&gt;. The input form is prefilled with one URL, so a first test run costs less than a cent.&lt;/p&gt;

&lt;p&gt;If you have a site that doesn't extract cleanly, open an issue on the Actor page with the URL. That is the fastest way for me to improve it.&lt;/p&gt;

</description>
      <category>rag</category>
      <category>ai</category>
      <category>python</category>
      <category>webscraping</category>
    </item>
    <item>
      <title>How to replace Google's Custom Search JSON API before it shuts down (Jan 1, 2027)</title>
      <dc:creator>Mohamed Senator</dc:creator>
      <pubDate>Mon, 19 Oct 2026 14:46:39 +0000</pubDate>
      <link>https://dev.to/s3nafps/how-to-replace-googles-custom-search-json-api-before-it-shuts-down-jan-1-2027-25gf</link>
      <guid>https://dev.to/s3nafps/how-to-replace-googles-custom-search-json-api-before-it-shuts-down-jan-1-2027-25gf</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7bmct1ibip4lb7p6qp1a.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7bmct1ibip4lb7p6qp1a.gif" alt="Google Custom Search API demo" width="720" height="405"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://s3nafps.github.io/media/google-custom-search-api/promo.mp4" rel="noopener noreferrer"&gt;Watch the full video&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Google's Custom Search JSON API (&lt;code&gt;customsearch/v1&lt;/code&gt;) is closed to new customers and will stop answering on January 1, 2027. If your app, chatbot or AI agent calls it, you have until then to move.&lt;/p&gt;

&lt;p&gt;The official successor, Vertex AI Search, is a different product: new setup, new response format, new pricing. If you just want your existing code to keep working, a drop-in replacement is less work.&lt;/p&gt;

&lt;p&gt;This guide shows how to switch in about a minute, without changing how you read the results.&lt;/p&gt;

&lt;h2&gt;
  
  
  What "drop-in" means here
&lt;/h2&gt;

&lt;p&gt;The replacement I'll use is an open actor on Apify that answers with the same JSON as Google's API: &lt;code&gt;kind: customsearch#search&lt;/code&gt;, &lt;code&gt;items[].title&lt;/code&gt;, &lt;code&gt;items[].link&lt;/code&gt;, &lt;code&gt;items[].snippet&lt;/code&gt;, &lt;code&gt;htmlSnippet&lt;/code&gt;, &lt;code&gt;searchInformation&lt;/code&gt;, and &lt;code&gt;queries.nextPage&lt;/code&gt;. It accepts the same parameters: &lt;code&gt;q&lt;/code&gt;, &lt;code&gt;num&lt;/code&gt;, &lt;code&gt;start&lt;/code&gt;, &lt;code&gt;gl&lt;/code&gt;, &lt;code&gt;hl&lt;/code&gt;, &lt;code&gt;lr&lt;/code&gt;, &lt;code&gt;cr&lt;/code&gt;, &lt;code&gt;safe&lt;/code&gt;, &lt;code&gt;dateRestrict&lt;/code&gt;, &lt;code&gt;siteSearch&lt;/code&gt;, &lt;code&gt;exactTerms&lt;/code&gt;, &lt;code&gt;excludeTerms&lt;/code&gt;, &lt;code&gt;orTerms&lt;/code&gt; and &lt;code&gt;fileType&lt;/code&gt;. Errors come back in Google's shape too, so your error handling keeps working.&lt;/p&gt;

&lt;p&gt;Disclosure: I built it, because I needed it myself.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Get a token
&lt;/h2&gt;

&lt;p&gt;Create a free Apify account and copy your API token from Settings → Integrations. The free plan includes $5 of monthly credit, which covers roughly 1,000 searches.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Change one line
&lt;/h2&gt;

&lt;p&gt;Before:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;GET https://www.googleapis.com/customsearch/v1?key=GOOGLE_API_KEY&amp;amp;cx=ENGINE_ID&amp;amp;q=coffee
~~~

After:

~~~http
GET https://s3nafps--google-custom-search-api.apify.actor/customsearch/v1?token=APIFY_TOKEN&amp;amp;q=coffee
~~~

`key` becomes your Apify token (or send it as an `Authorization: Bearer` header). `cx` is accepted and ignored, so old URLs don't break.

Python:

~~~python
import os, requests

r = requests.get(
    "https://s3nafps--google-custom-search-api.apify.actor/customsearch/v1",
    params={"q": "best espresso machine", "num": 5, "gl": "us"},
    headers={"Authorization": f"Bearer {os.environ['APIFY_TOKEN']}"},
    timeout=60,
)
for item in r.json().get("items", []):
    print(item["title"], item["link"])
~~~

JavaScript:

~~~js
const url = new URL('https://s3nafps--google-custom-search-api.apify.actor/customsearch/v1');
url.search = new URLSearchParams({ q: 'best espresso machine', num: '5' });
const res = await fetch(url, { headers: { Authorization: `Bearer ${process.env.APIFY_TOKEN}` } });
console.log((await res.json()).items?.map((i) =&amp;gt; i.link));
~~~

## Step 3: Check the differences

A few things work differently, so test before you switch production traffic:

- Results come from google.com, not from a Programmable Search Engine you configured. To limit results to one site, use `siteSearch=example.com`.
- Speed: half of searches answer within about 7 seconds, most within 15. Google's API was faster. Set your client timeout to 60 seconds.
- Image search (`searchType=image`) isn't supported yet and returns a clear 400 error.
- `pagemap` only has `metatags`, and only when you ask for Markdown (below).

## Bonus for AI agents: page text in the same call

Add `markdown=true` and each result also carries the clean text of its page as Markdown, ready for an LLM or a RAG pipeline. That saves a second scraping step:

~~~python
params={"q": query, "num": 5, "markdown": "true", "markdownMaxChars": 4000}
~~~

Some sites block automated visitors, so expect text for about half the pages; the others return `markdownError` and aren't charged.

## Cost

Google charged $5 per 1,000 queries. This replacement charges $1.50 per 1,000 searches plus Apify's platform cost (mostly the Google search proxy, about $2.50 per 1,000), so about $4 per 1,000 in total. Searches that fail aren't charged.

## Lists of searches

For batch jobs, run the actor with a list instead of calling the web address:

~~~json
{ "queries": ["best coffee grinder 2026", "how to make cold brew"], "num": 10 }
~~~

Each search becomes one row in Google's format.

Actor page with all parameters: https://apify.com/s3nafps/google-custom-search-api

If you're migrating and something your code relies on is missing, open an issue on the actor page and I'll look at it.
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>api</category>
      <category>ai</category>
    </item>
    <item>
      <title>How to get Aqar.fm property listings into Google Sheets (Riyadh, Jeddah, any Saudi city)</title>
      <dc:creator>Mohamed Senator</dc:creator>
      <pubDate>Sat, 17 Oct 2026 01:52:49 +0000</pubDate>
      <link>https://dev.to/s3nafps/how-to-get-aqarfm-property-listings-into-google-sheets-riyadh-jeddah-any-saudi-city-2bpi</link>
      <guid>https://dev.to/s3nafps/how-to-get-aqarfm-property-listings-into-google-sheets-riyadh-jeddah-any-saudi-city-2bpi</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flg57t8804gifoksd6ysb.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flg57t8804gifoksd6ysb.gif" alt="Aqar Scraper demo" width="720" height="405"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://s3nafps.github.io/media/aqar-scraper/promo.mp4" rel="noopener noreferrer"&gt;Watch the full video&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fupx7zywygo52kqc8xu9r.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fupx7zywygo52kqc8xu9r.gif" alt="Aqar Monitor demo" width="760" height="428"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://s3nafps.github.io/media/aqar-monitor/promo.mp4" rel="noopener noreferrer"&gt;Watch the full video&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Aqar.fm is the biggest property site in Saudi Arabia, but it has no export button. If you want to compare rents across Riyadh districts or spot under-priced listings, you end up copying listings by hand.&lt;/p&gt;

&lt;p&gt;Here's how to get clean listings into a spreadsheet in about two minutes, with no code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Open the scraper&lt;/strong&gt;&lt;br&gt;
Go to &lt;a href="https://apify.com/s3nafps/aqar-scraper" rel="noopener noreferrer"&gt;Aqar.fm Scraper on Apify&lt;/a&gt; and click &lt;strong&gt;Try for free&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2: Pick your search&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;City: Riyadh, Jeddah, Dammam, Al Khobar, Mecca, Medina and more&lt;/li&gt;
&lt;li&gt;Category: apartments, villas, land or buildings, for rent or for sale&lt;/li&gt;
&lt;li&gt;Optional filters: district, price range, area, minimum bedrooms&lt;/li&gt;
&lt;li&gt;Max results: how many listings you want&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Step 3: Run it and export&lt;/strong&gt;&lt;br&gt;
Click &lt;strong&gt;Start&lt;/strong&gt;. When the run finishes, open &lt;strong&gt;Storage → Dataset&lt;/strong&gt; and export to CSV or Excel, then import the file into Google Sheets. To refresh automatically, add a schedule (for example, daily at 8:00).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What you get for each listing&lt;/strong&gt;&lt;br&gt;
English and Arabic title and description, price in SAR, rent period, area (m²), price per m², bedrooms, bathrooms, district, map coordinates, photos, and advertiser type.&lt;/p&gt;

&lt;p&gt;The column most people miss is &lt;strong&gt;&lt;code&gt;priceVsDistrictMedianPct&lt;/code&gt;&lt;/strong&gt;: how far a listing's price per m² is above or below the median for its district.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt; in my test, a 203 m² three-bedroom apartment for rent in Al Malqa, Riyadh, came in at SAR 369/m². The district median was SAR 622/m², so it was &lt;strong&gt;40.6% below median&lt;/strong&gt;. Sort by that column and the deals are at the top.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cost:&lt;/strong&gt; you pay per listing, and there's no subscription.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Want alerts instead of exports?&lt;/strong&gt; &lt;a href="https://apify.com/s3nafps/aqar-monitor" rel="noopener noreferrer"&gt;Aqar.fm Monitor&lt;/a&gt; runs on a schedule and reports only new listings and price drops.&lt;/p&gt;

</description>
      <category>webscraping</category>
      <category>realestate</category>
      <category>saudiarabia</category>
      <category>googlesheets</category>
    </item>
    <item>
      <title>Turn any web page into typed JSON for your AI agent (with a source quote for every value)</title>
      <dc:creator>Mohamed Senator</dc:creator>
      <pubDate>Sat, 17 Oct 2026 01:48:24 +0000</pubDate>
      <link>https://dev.to/s3nafps/turn-any-web-page-into-typed-json-for-your-ai-agent-with-a-source-quote-for-every-value-39lb</link>
      <guid>https://dev.to/s3nafps/turn-any-web-page-into-typed-json-for-your-ai-agent-with-a-source-quote-for-every-value-39lb</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F70idl6qcfgucevrjyjae.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F70idl6qcfgucevrjyjae.gif" alt="Webpage to JSON for AI Agents demo" width="760" height="428"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://s3nafps.github.io/media/webpage-to-json-for-ai-agents/promo.mp4" rel="noopener noreferrer"&gt;Watch the full video&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;LLM agents are great at reasoning and bad at reading messy HTML. The usual fix is a custom scraper per site, which breaks every time the site changes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://apify.com/s3nafps/webpage-to-json-for-ai-agents" rel="noopener noreferrer"&gt;Webpage to JSON for AI Agents&lt;/a&gt; takes a URL and a plain-English description of the fields you want, and returns typed JSON. Every value comes with a quote from the page, so you can check it isn't made up.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Input:&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;"urls"&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;"https://books.toscrape.com/catalogue/a-light-in-the-attic_1000/index.html"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"instruction"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Extract the book title, price as a number, currency code, and whether it is in stock."&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Output (real run):&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;"data"&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;"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;"A Light in the Attic"&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="mf"&gt;51.77&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"currencyCode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"GBP"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"inStock"&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="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;"evidence"&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;"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;"# A Light in the Attic"&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="s2"&gt;"£51.77"&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;"unverifiedFields"&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;&lt;strong&gt;Useful options&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;JSON schema:&lt;/strong&gt; pass &lt;code&gt;extractionSchema&lt;/code&gt; when you need an exact shape.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;List mode:&lt;/strong&gt; set &lt;code&gt;listMode: true&lt;/code&gt; to get many records from one page, such as product grids or job boards.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Strict evidence:&lt;/strong&gt; &lt;code&gt;requireEvidence&lt;/code&gt; is on by default. A value without a source quote is flagged in &lt;code&gt;unverifiedFields&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Call it from code&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;apify_client&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ApifyClient&lt;/span&gt;
&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ApifyClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;YOUR_APIFY_TOKEN&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;run&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;actor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;s3nafps/webpage-to-json-for-ai-agents&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;run_input&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;urls&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://example.com/pricing&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;instruction&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;List each plan with name, monthly price and included seats.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;defaultDatasetId&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nf"&gt;iterate_items&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Use it as an MCP tool:&lt;/strong&gt; add &lt;code&gt;https://mcp.apify.com/?actors=s3nafps/webpage-to-json-for-ai-agents&lt;/code&gt; to Claude, Cursor or any MCP client, and your agent can call it directly.&lt;/p&gt;

&lt;p&gt;The LLM cost is included. You pay $0.03 per page with data and nothing for pages where no data was found.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>agents</category>
      <category>webscraping</category>
    </item>
  </channel>
</rss>
