<?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: İlyas Yıldırım</title>
    <description>The latest articles on DEV Community by İlyas Yıldırım (@ilyas_yldrm_72e3216f68).</description>
    <link>https://dev.to/ilyas_yldrm_72e3216f68</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3600592%2F4f82a6aa-b4c7-485b-9076-426f698e358e.jpg</url>
      <title>DEV Community: İlyas Yıldırım</title>
      <link>https://dev.to/ilyas_yldrm_72e3216f68</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ilyas_yldrm_72e3216f68"/>
    <language>en</language>
    <item>
      <title>Google Places API vs. basedonb for B2B lead generation: an honest comparison</title>
      <dc:creator>İlyas Yıldırım</dc:creator>
      <pubDate>Thu, 07 May 2026 15:07:33 +0000</pubDate>
      <link>https://dev.to/ilyas_yldrm_72e3216f68/google-places-api-vs-basedonb-for-b2b-lead-generation-an-honest-comparison-e96</link>
      <guid>https://dev.to/ilyas_yldrm_72e3216f68/google-places-api-vs-basedonb-for-b2b-lead-generation-an-honest-comparison-e96</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Disclosure up front:&lt;/strong&gt; I built basedonb. That makes this comparison biased by definition. I'm publishing the methodology and raw numbers below so you can audit it. If you find a mistake, comment and I'll edit.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A reader asked: "Why would I use a third-party Maps scraper when Google has an official Places API?" It's a fair question. So I ran the same workload through both and wrote down what happened.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; They're not the same product. Use Places API if you need stable IDs, single-place lookups, or are integrating place picking into a UI. Use basedonb (or a similar Maps scraper) if you need &lt;em&gt;bulk&lt;/em&gt; B2B leads by category and geography. Mixing them is also a valid strategy.&lt;/p&gt;

&lt;h2&gt;
  
  
  Test setup
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Workload:&lt;/strong&gt; 100 queries of "dentists in {city}, {state}" across the 50 most populous US metros, target 200 leads each → ~20,000 leads ceiling.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Date:&lt;/strong&gt; ran 2026-04-22 to 2026-04-24.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Places API path:&lt;/strong&gt; Text Search → Place Details (for each result, to get phone/website).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;basedonb path:&lt;/strong&gt; single &lt;code&gt;POST /scrapes&lt;/code&gt; per metro.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Field comparison:&lt;/strong&gt; I diffed &lt;code&gt;name&lt;/code&gt;, &lt;code&gt;address&lt;/code&gt;, &lt;code&gt;phone&lt;/code&gt;, &lt;code&gt;website&lt;/code&gt;, &lt;code&gt;rating&lt;/code&gt;, &lt;code&gt;reviews_count&lt;/code&gt;, &lt;code&gt;lat/lng&lt;/code&gt;, &lt;code&gt;business_status&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Caveat: basedonb's data ultimately comes from Google Maps too — you should not expect a different &lt;em&gt;truth&lt;/em&gt;, you should expect a different &lt;em&gt;shape, cost, and friction&lt;/em&gt;. That's what I measured.&lt;/p&gt;

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

&lt;p&gt;Both are usage-priced. The Places API charges per call; basedonb charges per returned lead.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Workload&lt;/th&gt;
&lt;th&gt;Places API&lt;/th&gt;
&lt;th&gt;basedonb (Starter)&lt;/th&gt;
&lt;th&gt;basedonb (Growth, $40+)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1× place lookup (you have an ID)&lt;/td&gt;
&lt;td&gt;~$0.017&lt;/td&gt;
&lt;td&gt;n/a — bulk only&lt;/td&gt;
&lt;td&gt;n/a&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1k leads, single metro&lt;/td&gt;
&lt;td&gt;~$32 (Text Search + Details + small enrichment)&lt;/td&gt;
&lt;td&gt;$10&lt;/td&gt;
&lt;td&gt;$9&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;20k leads, multi-metro&lt;/td&gt;
&lt;td&gt;~$640&lt;/td&gt;
&lt;td&gt;$200&lt;/td&gt;
&lt;td&gt;$180 (Growth) / $140 (Business)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Free tier&lt;/td&gt;
&lt;td&gt;$200/mo Maps credit&lt;/td&gt;
&lt;td&gt;50 free leads&lt;/td&gt;
&lt;td&gt;50 free leads&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Places API math: a Text Search is &lt;strong&gt;$32 per 1k&lt;/strong&gt;, and Place Details (for phone/website) is &lt;strong&gt;$17 per 1k&lt;/strong&gt; at the Basic Data SKU, with extra fields charged separately. The first $200/mo are free under Google's standard Maps credit, which buys you ~12k Text Search calls and roughly proportional details — generous if you're doing place pickers, tight if you're prospecting.&lt;/p&gt;

&lt;p&gt;basedonb math: 1 credit = 1 returned lead. Starter is $10/1k. Volume tiers down to $6/1k at $500+ top-ups. Credits don't expire.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Verdict:&lt;/strong&gt; for &lt;em&gt;bulk&lt;/em&gt; prospecting, basedonb is meaningfully cheaper. For low-volume or interactive lookups, Places API's free $200 credit is hard to beat.&lt;/p&gt;

&lt;h2&gt;
  
  
  Data completeness
&lt;/h2&gt;

&lt;p&gt;Sample: 100 queries, picking the first 50 leads of each (so 5,000 leads per source).&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;Places API (Text+Details)&lt;/th&gt;
&lt;th&gt;basedonb&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;name&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;address&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;td&gt;99.4%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;phone&lt;/td&gt;
&lt;td&gt;89.1%&lt;/td&gt;
&lt;td&gt;87.3%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;website&lt;/td&gt;
&lt;td&gt;76.4%&lt;/td&gt;
&lt;td&gt;74.8%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;rating&lt;/td&gt;
&lt;td&gt;92.7%&lt;/td&gt;
&lt;td&gt;91.9%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;reviews_count&lt;/td&gt;
&lt;td&gt;92.7%&lt;/td&gt;
&lt;td&gt;91.9%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;business_status&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;td&gt;99.1%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;lat/lng&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;td&gt;99.8%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;email&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;0%&lt;/strong&gt; (Google doesn't expose)&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;0%&lt;/strong&gt; (basedonb doesn't expose)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Both sources draw from Google's underlying business graph, so completeness is within ~2 points. Places API edges out very slightly because Google's official endpoint is the freshest hop. The gap is not material for cold-outreach work.&lt;/p&gt;

&lt;p&gt;If you saw "Maps scraper with 95% email coverage" in some pitch, ask where the emails come from. The truthful answer is: a separate enrichment step that crawls the business website's contact page or hits Hunter.io. Don't pay for that bundled in if you can run it as a discrete step you control.&lt;/p&gt;

&lt;h2&gt;
  
  
  Latency
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operation&lt;/th&gt;
&lt;th&gt;Places API&lt;/th&gt;
&lt;th&gt;basedonb&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1 lead&lt;/td&gt;
&lt;td&gt;200–600ms (Text Search) + 200–500ms (Details)&lt;/td&gt;
&lt;td&gt;n/a (bulk)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;50 leads, single metro (cache hit)&lt;/td&gt;
&lt;td&gt;~25–60s sequential, ~3–5s parallel&lt;/td&gt;
&lt;td&gt;&amp;lt;1s (cached)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;200 leads, fresh scrape&lt;/td&gt;
&lt;td&gt;~100–240s parallel + your retry/backoff code&lt;/td&gt;
&lt;td&gt;30–120s (server-side grid)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;basedonb's "fresh" path is async by design (&lt;code&gt;202&lt;/code&gt; + poll), which is annoying for an interactive UI but right for a cron job. Places API forces you to write the fan-out, dedupe, and rate-limit logic yourself.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stable IDs vs. transient IDs
&lt;/h2&gt;

&lt;p&gt;This is the one where Places API wins clearly. &lt;strong&gt;Place IDs from Google are stable&lt;/strong&gt; (with caveats; they can change but Google publishes redirects). basedonb returns &lt;code&gt;place_id&lt;/code&gt; strings sourced from Google but does not guarantee stability over time the way Google's contract does. If your product stores a &lt;code&gt;place_id&lt;/code&gt; and re-queries it months later, use Places API.&lt;/p&gt;

&lt;p&gt;If your product is "pull a fresh list of dentists in Phoenix once a quarter," you don't care.&lt;/p&gt;

&lt;h2&gt;
  
  
  Code shape
&lt;/h2&gt;

&lt;p&gt;Places API for a "give me 200 dentists in Manhattan" task:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Pseudo — real code is longer because of pagination tokens.&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://places.googleapis.com/v1/places:searchText&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;X-Goog-Api-Key&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;X-Goog-FieldMask&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;places.id,places.displayName,places.formattedAddress&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;textQuery&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dentists in Manhattan, NY&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;pageSize&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="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

&lt;span class="c1"&gt;// Then for each place, hit Place Details for phone/website:&lt;/span&gt;
&lt;span class="k"&gt;for &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;p&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;places&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;details&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`https://places.googleapis.com/v1/places/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;X-Goog-Api-Key&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;X-Goog-FieldMask&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;internationalPhoneNumber,websiteUri,rating,userRatingCount,businessStatus&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
  &lt;span class="c1"&gt;// ...merge, retry on 429, paginate via nextPageToken (with the 2s wait Google requires)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;basedonb for the same task:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;job&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://www.basedonb.com/api/v1/scrapes&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;Authorization&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Bearer &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dentists&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;country&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;US&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;city&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Manhattan&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;target_leads&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
&lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;job&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;done&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;job&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// else poll job.id until done, then GET /scrapes/{id}/results&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The difference is "I'm assembling a workflow" vs. "I'm calling a function." Both have their place.&lt;/p&gt;

&lt;h2&gt;
  
  
  When to use which
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Use Places API when:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You need a place picker / autocomplete in a user-facing UI.&lt;/li&gt;
&lt;li&gt;You have a Place ID and want to refresh details.&lt;/li&gt;
&lt;li&gt;Your volume is low enough that the $200 free credit covers you.&lt;/li&gt;
&lt;li&gt;You need stable IDs that won't drift over time.&lt;/li&gt;
&lt;li&gt;Compliance / legal wants the data path to be 100% Google's official surface.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Use basedonb (or another Maps scraper) when:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You're pulling bulk lead lists by category × geography.&lt;/li&gt;
&lt;li&gt;You want a flat per-lead price, not a per-call price.&lt;/li&gt;
&lt;li&gt;You don't want to manage pagination, fan-out, retry, and dedupe yourself.&lt;/li&gt;
&lt;li&gt;You're a one-person operation and the engineering time to wire Places + Details + caching is not worth it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Use both when:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You prospect with a Maps scraper for breadth, then refresh hot accounts with Place Details for stable IDs and freshness in your CRM. This is what most agencies I know end up doing.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Methodology details + raw numbers
&lt;/h2&gt;

&lt;p&gt;Full methodology, the 100 queries, and the diff CSV are in &lt;a href="https://gist.github.com/&amp;lt;your-username&amp;gt;/&amp;lt;gist-id" rel="noopener noreferrer"&gt;this gist&lt;/a&gt; — feel free to re-run with your own keys. (Will publish before this post goes live.)&lt;/p&gt;

&lt;p&gt;If your numbers come out materially different — especially on completeness — I want to know. Comment with the query and I'll dig in.&lt;/p&gt;

&lt;h2&gt;
  
  
  Closing
&lt;/h2&gt;

&lt;p&gt;I built basedonb because the workflow above ("script Places + Details + retry + dedupe") was eating my Saturdays. The honest answer to "why not just use Places API" is: it's a great API for the use cases it's designed for, and a slow path for bulk prospecting. Pick the one that matches your shape of work.&lt;/p&gt;

&lt;p&gt;— &lt;a href="https://www.basedonb.com" rel="noopener noreferrer"&gt;basedonb&lt;/a&gt;. Bias acknowledged; data above is reproducible.&lt;/p&gt;

</description>
      <category>api</category>
      <category>webdev</category>
      <category>data</category>
      <category>comparison</category>
    </item>
    <item>
      <title>Building a no-code lead-gen pipeline with basedonb + Make.com + HubSpot</title>
      <dc:creator>İlyas Yıldırım</dc:creator>
      <pubDate>Thu, 07 May 2026 15:06:55 +0000</pubDate>
      <link>https://dev.to/ilyas_yldrm_72e3216f68/building-a-no-code-lead-gen-pipeline-with-basedonb-makecom-hubspot-36n4</link>
      <guid>https://dev.to/ilyas_yldrm_72e3216f68/building-a-no-code-lead-gen-pipeline-with-basedonb-makecom-hubspot-36n4</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Disclosure:&lt;/strong&gt; I run &lt;a href="https://www.basedonb.com" rel="noopener noreferrer"&gt;basedonb&lt;/a&gt;, the Google Maps lead API in this scenario. The pipeline pattern works just as well with Outscraper, Apify, or anything else that returns business leads as JSON.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I run a one-person agency on the side. Every Monday at 9 AM I want a fresh list of N businesses in a target city, deduped against what's already in HubSpot, with new contacts slacked to me before standup. I used to do this manually on Sunday nights. This post is the Make.com scenario that replaced 30 minutes of CSV gymnastics with zero touches.&lt;/p&gt;

&lt;p&gt;The whole thing is one scenario, six modules, ~$0.05 per run.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we're building
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Cron: Monday 9am]
       │
       ▼
[HTTP — POST basedonb /scrapes]
       │
       ▼
[Iterator — split results array]
       │
       ▼
[HubSpot — search contact by phone]
       │
       ▼
[Router — exists? skip : create]
       │
       ▼
[Aggregator — count new]
       │
       ▼
[Slack — post summary]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output: a Slack message that says "27 new dentists in Manhattan added to HubSpot this morning" with a link to the segment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stack
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;basedonb&lt;/strong&gt; — lead source (REST API). &lt;a href="https://www.basedonb.com/docs" rel="noopener noreferrer"&gt;docs&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Make.com&lt;/strong&gt; — orchestration. Free plan covers ~1,000 ops/mo, enough for weekly runs at modest volume. There's also a &lt;a href="https://www.basedonb.com/integrations/make" rel="noopener noreferrer"&gt;native basedonb integration page&lt;/a&gt; that walks through the connection.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HubSpot&lt;/strong&gt; — CRM destination. Free tier works.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Slack&lt;/strong&gt; — heartbeat + summary.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you prefer Zapier, the same shape works there — basedonb has a &lt;a href="https://www.basedonb.com/integrations/zapier" rel="noopener noreferrer"&gt;Zapier integration&lt;/a&gt; too. I picked Make because the Iterator/Router primitives map cleanly to "split the array, branch per item."&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1 — the HTTP module (basedonb scrape)
&lt;/h2&gt;

&lt;p&gt;Make.com → "HTTP" → "Make a request":&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;URL:&lt;/strong&gt; &lt;code&gt;https://www.basedonb.com/api/v1/scrapes&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Method:&lt;/strong&gt; POST&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Headers:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Authorization: Bearer bdb_live_YOUR_KEY&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Content-Type: application/json&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Body type:&lt;/strong&gt; Raw → JSON&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Body:&lt;/strong&gt;
&lt;/li&gt;

&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"query"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dentists"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"country"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"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;"city"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"New York"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"target_leads"&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;ul&gt;
&lt;li&gt;
&lt;strong&gt;Parse response:&lt;/strong&gt; Yes (so downstream modules can address fields by path).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The API returns one of two shapes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;200&lt;/code&gt; with &lt;code&gt;status: "done"&lt;/code&gt; and &lt;code&gt;results: [...]&lt;/code&gt; inline (cache hit — common for popular queries).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;202&lt;/code&gt; with &lt;code&gt;status: "submitted"&lt;/code&gt; and an &lt;code&gt;id&lt;/code&gt; you have to poll.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For a weekly cron, the simplest pattern is: scope &lt;code&gt;target_leads&lt;/code&gt; to a value you know is cacheable (≤ a few hundred), and route on &lt;code&gt;status&lt;/code&gt;. If you need fresh scrapes for niche queries, add a follow-up "polling" module — Make has a built-in &lt;strong&gt;Sleep + Repeater&lt;/strong&gt; combo for this. The pattern:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Repeater 1..30]
   ├─ Sleep 10s
   ├─ HTTP GET /scrapes/{{id}}
   └─ Break if status == done
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I keep a separate scenario for the polling case so the main one stays linear.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2 — Iterator
&lt;/h2&gt;

&lt;p&gt;The basedonb response has a &lt;code&gt;results&lt;/code&gt; array. Drop a Make Iterator pointed at &lt;code&gt;1.results&lt;/code&gt; so each subsequent module runs once per lead.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3 — HubSpot search (dedupe)
&lt;/h2&gt;

&lt;p&gt;Most "leads pipelines" pollute the CRM because they re-add the same business every run. The cheapest dedupe is on phone number — basedonb returns E.164-ish strings (&lt;code&gt;+12125550101&lt;/code&gt;) which HubSpot accepts as-is.&lt;/p&gt;

&lt;p&gt;HubSpot module → "Search Objects":&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Object type:&lt;/strong&gt; Contacts&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Filter:&lt;/strong&gt; &lt;code&gt;phone&lt;/code&gt; equals &lt;code&gt;{{phone}}&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Limit:&lt;/strong&gt; 1&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Important: don't dedupe on &lt;code&gt;website&lt;/code&gt; alone. Two businesses can share a website (chains). Phone is the cleanest unique key for SMB Google Maps data.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4 — Router
&lt;/h2&gt;

&lt;p&gt;Two routes off the search:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Bundle exists&lt;/strong&gt; → connect nothing (skip). Add a Set Variable &lt;code&gt;skipped += 1&lt;/code&gt; so the summary can mention it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bundle missing&lt;/strong&gt; → "Create Contact":

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;firstname&lt;/code&gt;: split &lt;code&gt;{{title}}&lt;/code&gt; on space, take &lt;code&gt;[0]&lt;/code&gt; (good enough for SMBs that aren't person-named — adjust if you're targeting solo practitioners)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;phone&lt;/code&gt;: &lt;code&gt;{{phone}}&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;website&lt;/code&gt;: &lt;code&gt;{{website}}&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;company&lt;/code&gt;: &lt;code&gt;{{title}}&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;address&lt;/code&gt;: &lt;code&gt;{{address}}&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;hs_lead_status&lt;/code&gt;: &lt;code&gt;NEW&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Custom property&lt;/strong&gt; &lt;code&gt;lead_source&lt;/code&gt;: &lt;code&gt;basedonb-google-maps&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Custom property&lt;/strong&gt; &lt;code&gt;lead_rating&lt;/code&gt;: &lt;code&gt;{{rating}}&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Custom property&lt;/strong&gt; &lt;code&gt;lead_reviews&lt;/code&gt;: &lt;code&gt;{{reviews_count}}&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;The &lt;code&gt;rating&lt;/code&gt; and &lt;code&gt;reviews_count&lt;/code&gt; fields are gold for prioritization — sort your sales queue by &lt;code&gt;reviews_count DESC&lt;/code&gt; and you're calling real businesses, not zombie listings.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5 — Aggregator + Slack
&lt;/h2&gt;

&lt;p&gt;After the Router, drop a "Numeric Aggregator" with source = the create-contact module, function = count. The output is &lt;code&gt;new_count&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Slack "Create a Message":&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;:dart: *{{new_count}}* new dentists in New York added to HubSpot.
{{skipped}} already existed (deduped on phone).
Top by reviews: {{top.title}} — {{top.reviews_count}} reviews, {{top.rating}}★
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For the "top by reviews" bit, add a &lt;strong&gt;Sort + Get First&lt;/strong&gt; between the iterator and aggregator on &lt;code&gt;reviews_count desc&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 6 — schedule
&lt;/h2&gt;

&lt;p&gt;Scenario settings → "Scheduling" → "Every week, Monday, 09:00".&lt;/p&gt;

&lt;p&gt;Done. The full scenario is six core modules (HTTP, Iterator, HubSpot Search, Router, HubSpot Create, Aggregator + Slack).&lt;/p&gt;

&lt;h2&gt;
  
  
  Cost model
&lt;/h2&gt;

&lt;p&gt;For 100 leads/week into HubSpot, deduped:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Item&lt;/th&gt;
&lt;th&gt;Cost&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;basedonb credits (100 leads × $0.01 at Starter)&lt;/td&gt;
&lt;td&gt;$1.00/run&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Make.com ops (~6 × 100 = 600 ops)&lt;/td&gt;
&lt;td&gt;covered by free 1k/mo&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HubSpot Free tier&lt;/td&gt;
&lt;td&gt;$0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Slack&lt;/td&gt;
&lt;td&gt;$0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Per run&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;~$1.00&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Per month (4 runs)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;~$4.00&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;At 1k leads/week the basedonb cost drops to $0.009/lead (Growth tier — top up $40+) and you'll bump into Make's ops limit, so go to the $9/mo Core plan or self-host n8n. n8n doesn't have an official basedonb node yet, but the same HTTP Request node works against the same REST API — drop the JSON body in and the rest of the workflow ports 1:1.&lt;/p&gt;

&lt;h2&gt;
  
  
  Failure modes I hit (and how to fix them)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Phantom dedupe misses.&lt;/strong&gt; HubSpot phone search is exact-match. If basedonb returns &lt;code&gt;+1 212-555-0101&lt;/code&gt; and your old record has &lt;code&gt;(212) 555-0101&lt;/code&gt;, they don't match. Fix: in the Router's "exists" branch, also search by &lt;code&gt;website&lt;/code&gt; if phone normalization is messy. Or normalize both sides with a Make Text → Replace before search.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cache-hit confusion.&lt;/strong&gt; First time you run a query you'll get a &lt;code&gt;202&lt;/code&gt; and the rest of the scenario silently fails because &lt;code&gt;1.results&lt;/code&gt; is empty. Add an Error Handler on the HTTP module that routes &lt;code&gt;202&lt;/code&gt; to the polling scenario instead.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rate limit / 503.&lt;/strong&gt; basedonb returns &lt;code&gt;503 scraper_unavailable&lt;/code&gt; when its backend is briefly offline. Set the HTTP module's "Number of retries" to 3, "Initial interval" to 30s, exponential.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Slack message length.&lt;/strong&gt; If you list every new lead, you'll exceed Slack's 4k char limit. Cap at 5 examples and link to a HubSpot list view.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What I'd build next
&lt;/h2&gt;

&lt;p&gt;If I were doing this seriously I'd:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Replace the cron with a &lt;strong&gt;dynamic territory queue&lt;/strong&gt; — Airtable table of (query, city, last_run) and the scenario rotates through.&lt;/li&gt;
&lt;li&gt;Add a &lt;strong&gt;website-alive check&lt;/strong&gt; between dedupe and create — a 200 from &lt;code&gt;HEAD {{website}}&lt;/code&gt; weeds out dead businesses faster than reviews count alone.&lt;/li&gt;
&lt;li&gt;Add an &lt;strong&gt;enrichment pass&lt;/strong&gt; — basedonb gives you the website, and a separate Hunter.io / Clearbit module turns that into emails. The CRM record is much more useful with both phone and email.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Final notes
&lt;/h2&gt;

&lt;p&gt;The leverage here isn't that any one of these tools is special — it's that the connection is glue you don't have to maintain. I haven't touched this scenario in 4 months and it's still depositing 80–120 contacts/week into HubSpot.&lt;/p&gt;

&lt;p&gt;If you build a variant — say, basedonb → Pipedrive, or basedonb → Airtable → Lemlist — drop a screenshot in the comments. I'm collecting these into a &lt;a href="https://www.basedonb.com/integrations/make" rel="noopener noreferrer"&gt;public scenario library&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;— &lt;a href="https://www.basedonb.com" rel="noopener noreferrer"&gt;basedonb&lt;/a&gt; · open to feedback.&lt;/p&gt;

</description>
      <category>automation</category>
      <category>nocode</category>
      <category>api</category>
      <category>productivity</category>
    </item>
    <item>
      <title>How to scrape Google Maps without code: a REST API tutorial</title>
      <dc:creator>İlyas Yıldırım</dc:creator>
      <pubDate>Thu, 07 May 2026 15:05:14 +0000</pubDate>
      <link>https://dev.to/ilyas_yldrm_72e3216f68/how-to-scrape-google-maps-without-code-a-rest-api-tutorial-p67</link>
      <guid>https://dev.to/ilyas_yldrm_72e3216f68/how-to-scrape-google-maps-without-code-a-rest-api-tutorial-p67</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Disclosure:&lt;/strong&gt; I run &lt;a href="https://www.basedonb.com" rel="noopener noreferrer"&gt;basedonb&lt;/a&gt;, the API I'm using in this tutorial. The technique works with any Google Maps scraping API — the trade-offs section is honest. If you'd rather build the scraper yourself, the "Why DIY scraping fails" section explains what you're signing up for.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Last week a friend asked me for 500 dentist leads in Manhattan for a cold email campaign. I sent him a CSV in 90 seconds. This post is how to do that yourself in ~12 lines of code, and the failure modes you avoid by not writing the scraper yourself.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why building your own Google Maps scraper is harder than it looks
&lt;/h2&gt;

&lt;p&gt;Every "I'll just write a quick Puppeteer script" plan dies on the same five rocks:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;IP blocking.&lt;/strong&gt; Google flags datacenter IPs after ~50–100 requests. You need a residential proxy pool (~$50–$200/mo) just to start.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CAPTCHAs.&lt;/strong&gt; Once flagged, every request becomes a reCAPTCHA challenge. Solving services exist but cost ~$2 per 1k.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Layout drift.&lt;/strong&gt; Google ships UI changes constantly. Your selectors break and you don't notice until your CSV is empty.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rate limits + soft bans.&lt;/strong&gt; Even the official Places API caps you, and the unofficial Maps frontend will silently degrade results when it thinks you're a bot.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Legal exposure.&lt;/strong&gt; Scraping Maps directly is a grey area. Going through an API that has commercial terms shifts that risk off your laptop.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If your goal is "ship a list this week," skip the scraper and just call an API. Below is a full example using basedonb because that's what I built, but the same shape applies to Outscraper, Apify's Google Maps actor, etc.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mental model: submit → poll → fetch
&lt;/h2&gt;

&lt;p&gt;Most Google Maps scrapers are async — a 500-result query takes 30–120 seconds because Maps doesn't actually return 500 results to a single search; the scraper has to grid the area and dedupe. So the API has three states:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;POST /scrapes&lt;/code&gt;&lt;/strong&gt; — you submit a job. If results are already cached, you get &lt;code&gt;200&lt;/code&gt; with the data inline. Otherwise you get &lt;code&gt;202&lt;/code&gt; with a job &lt;code&gt;id&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;GET /scrapes/{id}&lt;/code&gt;&lt;/strong&gt; — you poll status until it's &lt;code&gt;done&lt;/code&gt; or &lt;code&gt;failed&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;GET /scrapes/{id}/results&lt;/code&gt;&lt;/strong&gt; — you fetch the leads.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's the whole API surface. Auth is a bearer token.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tutorial: cURL
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://www.basedonb.com/api/v1/scrapes &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer bdb_live_YOUR_KEY"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "query": "dentists",
    "country": "US",
    "city": "New York",
    "target_leads": 500
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Response when results are cached (&lt;code&gt;200&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"id"&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;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"done"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"leads_found"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"results"&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;"place_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;"ChIJ..."&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;"Smile NYC Dental"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"category"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Dental clinic"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"address"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"123 W 23rd St, New York, NY 10011"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"phone"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"+12125550101"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"website"&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://example.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;"rating"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;4.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;"reviews_count"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;412&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"latitude"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;40.7445&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"longitude"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;-74.0010&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"business_status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"OPERATIONAL"&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_level"&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;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;Response when a fresh scrape is needed (&lt;code&gt;202&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"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;"3fa85f64-5717-4562-b3fc-2c963f66afa6"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"submitted"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"leads_found"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then you poll:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl https://www.basedonb.com/api/v1/scrapes/3fa85f64-5717-4562-b3fc-2c963f66afa6 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer bdb_live_YOUR_KEY"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When &lt;code&gt;status&lt;/code&gt; becomes &lt;code&gt;done&lt;/code&gt;, fetch results:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl https://www.basedonb.com/api/v1/scrapes/3fa85f64-5717-4562-b3fc-2c963f66afa6/results &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer bdb_live_YOUR_KEY"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. Three endpoints.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tutorial: Node.js (12 lines, no dependencies)
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;KEY&lt;/span&gt; &lt;span class="o"&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;BASEDONB_KEY&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;BASE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://www.basedonb.com/api/v1&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;headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Authorization&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Bearer &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;submit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;BASE&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/scrapes`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dentists&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;country&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;US&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;city&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;New York&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;target_leads&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
&lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;job&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;submit&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;job&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;done&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;job&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;failed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="nx"&gt;job&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;BASE&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/scrapes/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;submit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;headers&lt;/span&gt; &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
  &lt;span class="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="s2"&gt;`progress &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;job&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;progress&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;% — &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;job&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;leads_found&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; leads`&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;results&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="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;BASE&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/scrapes/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;submit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/results`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;headers&lt;/span&gt; &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="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="s2"&gt;`done — &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;results&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="s2"&gt; leads`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Real production code should add a poll budget (don't loop forever) and handle the &lt;code&gt;200&lt;/code&gt;-with-inline-results case for cache hits. Two extra lines.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tutorial: Python
&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;import&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;time&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;

&lt;span class="n"&gt;KEY&lt;/span&gt; &lt;span class="o"&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;BASEDONB_KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;BASE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://www.basedonb.com/api/v1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;H&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;Authorization&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Bearer &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;KEY&lt;/span&gt;&lt;span class="si"&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;submit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;BASE&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/scrapes&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;H&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;json&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;query&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;dentists&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;country&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;city&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;New York&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;target_leads&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;submit&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;done&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;submit&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;results&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;job_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;submit&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;job&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;BASE&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/scrapes/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;job_id&lt;/span&gt;&lt;span class="si"&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;headers&lt;/span&gt;&lt;span class="o"&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;json&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;job&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;done&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;failed&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="k"&gt;break&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;job&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;progress&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;% — &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;job&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;leads_found&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; leads&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;BASE&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/scrapes/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;job_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/results&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&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;json&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;results&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="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;got &lt;/span&gt;&lt;span class="si"&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;results&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; leads&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;Dump to CSV in two more lines:&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;csv&lt;/span&gt;
&lt;span class="k"&gt;with&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;dentists_nyc.csv&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;w&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;newline&lt;/span&gt;&lt;span class="o"&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;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;csv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;DictWriter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fieldnames&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;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;phone&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;website&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;address&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;rating&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;reviews_count&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writeheader&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;r&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writerow&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k&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;k&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fieldnames&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Error handling that actually matters
&lt;/h2&gt;

&lt;p&gt;You'll see four error shapes in practice:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;401 unauthorized&lt;/code&gt;&lt;/strong&gt; — bad key or wrong header. Make sure you're sending &lt;code&gt;Authorization: Bearer bdb_live_...&lt;/code&gt; (or the equivalent &lt;code&gt;X-API-Key&lt;/code&gt; header).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;400 bad_request&lt;/code&gt;&lt;/strong&gt; — usually missing &lt;code&gt;target_leads&lt;/code&gt; or an invalid state code. US state codes are GeoNames-dotted (&lt;code&gt;US.TX&lt;/code&gt;, not &lt;code&gt;TX&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;402 insufficient_credits&lt;/code&gt;&lt;/strong&gt; — your balance can't cover &lt;code&gt;target_leads&lt;/code&gt;. The error body tells you the gap.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;503 scraper_unavailable&lt;/code&gt;&lt;/strong&gt; — backend is briefly offline. Retry with backoff. This is rare but you should plan for it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Don't paper over &lt;code&gt;503&lt;/code&gt; with infinite retries. Cap at 3 attempts with exponential backoff (3s, 9s, 27s).&lt;/p&gt;

&lt;h2&gt;
  
  
  Pricing reality check
&lt;/h2&gt;

&lt;p&gt;This is the part most "API tutorial" posts skip and you find out later.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;1 credit = 1 lead. No subscription, credits don't expire.&lt;/li&gt;
&lt;li&gt;Starter is &lt;strong&gt;$10 per 1,000 leads&lt;/strong&gt; ($0.01/lead). It scales down: $9, $7, $6 per 1k at $40 / $150 / $500 top-ups.&lt;/li&gt;
&lt;li&gt;New accounts get &lt;strong&gt;50 free credits&lt;/strong&gt; — enough to run the full tutorial above, just lower &lt;code&gt;target_leads&lt;/code&gt; to 50.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For a 500-lead Manhattan dentists job: ~$5 at the lowest tier, less at volume. Compare that to the Google Places API at $17 per 1,000 &lt;em&gt;Place Details&lt;/em&gt; calls (without enrichment), or Outscraper around $3 per 1k for Maps Search but their UI/API trade-off is different.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the API does &lt;em&gt;not&lt;/em&gt; return (be honest)
&lt;/h2&gt;

&lt;p&gt;The fields you get are the ones in the response above: &lt;code&gt;title&lt;/code&gt;, &lt;code&gt;category&lt;/code&gt;, &lt;code&gt;address&lt;/code&gt;, &lt;code&gt;phone&lt;/code&gt;, &lt;code&gt;website&lt;/code&gt;, &lt;code&gt;rating&lt;/code&gt;, &lt;code&gt;reviews_count&lt;/code&gt;, &lt;code&gt;latitude&lt;/code&gt;, &lt;code&gt;longitude&lt;/code&gt;, &lt;code&gt;business_status&lt;/code&gt;, &lt;code&gt;price_level&lt;/code&gt;. &lt;strong&gt;There is no &lt;code&gt;email&lt;/code&gt; field&lt;/strong&gt; — Maps doesn't expose business emails, and any "Maps scraper" that hands you emails is doing a separate enrichment step (usually crawling the website's contact page) which you can do yourself with one more API call to a tool like Hunter.io or by parsing &lt;code&gt;mailto:&lt;/code&gt; links from the website.&lt;/p&gt;

&lt;p&gt;Don't pay for a "leads API with email" that's secretly doing email scraping you didn't authorize. The right shape is: pull leads here, enrich emails separately, keep the steps observable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;Three endpoints, two minutes of code, ~$5 for 500 verified business leads. The cost/effort ratio of writing your own scraper does not pencil out unless you're doing this at &amp;gt;100k/month volume.&lt;/p&gt;

&lt;p&gt;Full code in the snippets above is runnable as-is — drop your key in and go. If you find a better Maps API, I'd genuinely like to know in the comments.&lt;/p&gt;

&lt;p&gt;— I'm building &lt;a href="https://www.basedonb.com" rel="noopener noreferrer"&gt;basedonb&lt;/a&gt;. Honest feedback welcome.&lt;/p&gt;

</description>
      <category>api</category>
      <category>google</category>
      <category>tutorial</category>
      <category>webscraping</category>
    </item>
    <item>
      <title>Build a YouTube Search &amp; Download API Integration in Minutes (with RapidAPI)</title>
      <dc:creator>İlyas Yıldırım</dc:creator>
      <pubDate>Fri, 07 Nov 2025 07:48:43 +0000</pubDate>
      <link>https://dev.to/ilyas_yldrm_72e3216f68/build-a-youtube-search-download-api-integration-in-minutes-with-rapidapi-4e26</link>
      <guid>https://dev.to/ilyas_yldrm_72e3216f68/build-a-youtube-search-download-api-integration-in-minutes-with-rapidapi-4e26</guid>
      <description>&lt;h1&gt;
  
  
  Build a YouTube Search &amp;amp; Download API Integration in Minutes (with Apigle API)
&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;🚀 In this quick tutorial, we’ll explore how to integrate the &lt;strong&gt;Apigle YouTube Search &amp;amp; Download API&lt;/strong&gt; to fetch metadata, analyze videos, and even download MP3/MP4 content — all through a single endpoint.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  🎯 Why I Built This API
&lt;/h2&gt;

&lt;p&gt;As developers, we often need to &lt;strong&gt;fetch YouTube metadata&lt;/strong&gt;, analyze titles, or download video/audio files for automation tasks — such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Building media downloaders or content archivers&lt;/li&gt;
&lt;li&gt;Powering SEO or analytics dashboards&lt;/li&gt;
&lt;li&gt;Creating educational or music apps&lt;/li&gt;
&lt;li&gt;Running internal video-processing pipelines&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;However, most public APIs either limit downloads or are too slow.&lt;br&gt;
That’s why I built &lt;strong&gt;Apigle YouTube Search &amp;amp; Download API&lt;/strong&gt; — a single unified endpoint that delivers both &lt;strong&gt;search&lt;/strong&gt; and &lt;strong&gt;download&lt;/strong&gt; features with &lt;strong&gt;99.5% uptime&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;👉 RapidAPI link:&lt;br&gt;
&lt;a href="https://rapidapi.com/boztek-technology-boztek-technology-default/api/youtube-search-download3" rel="noopener noreferrer"&gt;https://rapidapi.com/boztek-technology-boztek-technology-default/api/youtube-search-download3&lt;/a&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  ⚙️ Getting Started
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Step 1. Get your API key&lt;/strong&gt;&lt;br&gt;
Visit the RapidAPI page above and subscribe to the free or pro plan.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2. Test it with Python&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;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;

&lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://youtube-search-download3.p.rapidapi.com/search&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;querystring&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;query&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;lofi hip hop&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;headers&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;x-rapidapi-key&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;YOUR_API_KEY&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;x-rapidapi-host&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;youtube-search-download3.p.rapidapi.com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;querystring&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;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  📦 Example Output
&lt;/h2&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;"nextToken"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"CBQQAA"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"contents"&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;"video"&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;"Lofi hip hop radio 📻 – beats to relax/study to"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"videoId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"jfKfPfyJRdk"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"author"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Lofi Girl"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"viewCount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"12,300,000"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"lengthText"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1:00:00"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"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;"Perfect study beats 🎧"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"thumbnail"&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://i.ytimg.com/vi/jfKfPfyJRdk/hqdefault.jpg"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🎵 Downloading a Video or Audio
&lt;/h2&gt;

&lt;p&gt;Once you have a &lt;code&gt;videoId&lt;/code&gt;, you can fetch MP3 or MP4 links instantly:&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;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://youtube-search-download3.p.rapidapi.com/download&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;querystring&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;video&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;jfKfPfyJRdk&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;headers&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;x-rapidapi-key&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;YOUR_API_KEY&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;x-rapidapi-host&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;youtube-search-download3.p.rapidapi.com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;querystring&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;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You’ll receive &lt;strong&gt;direct stream URLs&lt;/strong&gt; for both formats (short-lived signed links).&lt;/p&gt;




&lt;h2&gt;
  
  
  🧠 API Features
&lt;/h2&gt;

&lt;p&gt;✅ Unified endpoint for &lt;strong&gt;search + download&lt;/strong&gt;&lt;br&gt;
✅ MP3/MP4 support (with duration limits up to 3 hours)&lt;br&gt;
✅ Fast global CDN delivery&lt;br&gt;
✅ 99.5% uptime guarantee&lt;br&gt;
✅ Free tier available on RapidAPI&lt;/p&gt;




&lt;h2&gt;
  
  
  🔗 Official Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;RapidAPI Page: &lt;a href="https://rapidapi.com/boztek-technology-boztek-technology-default/api/youtube-search-download3" rel="noopener noreferrer"&gt;https://rapidapi.com/boztek-technology-boztek-technology-default/api/youtube-search-download3&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Official Site: &lt;a href="https://www.apigle.com" rel="noopener noreferrer"&gt;https://www.apigle.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Postman Docs: &lt;a href="https://documenter.getpostman.com/view/33176096/2sB2jAa7YZ" rel="noopener noreferrer"&gt;https://documenter.getpostman.com/view/33176096/2sB2jAa7YZ&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  💬 Final Thoughts
&lt;/h2&gt;

&lt;p&gt;I’d love to hear your feedback or see how you use the API.&lt;br&gt;
If you’re building a YouTube-based app, automation tool, or content downloader — this API can save you weeks of effort.&lt;/p&gt;

&lt;p&gt;You can reach me anytime on Telegram: &lt;a href="https://t.me/api_chat_sup" rel="noopener noreferrer"&gt;https://t.me/api_chat_sup&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Apigle — Developer APIs that actually work.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>api</category>
      <category>tutorial</category>
      <category>programming</category>
      <category>automation</category>
    </item>
  </channel>
</rss>
