<?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: Dev Stack Hub</title>
    <description>The latest articles on DEV Community by Dev Stack Hub (@devstackhub).</description>
    <link>https://dev.to/devstackhub</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%2F4081963%2F9d10c5fa-c76e-4c9d-88af-e3f2fced5a62.jpg</url>
      <title>DEV Community: Dev Stack Hub</title>
      <link>https://dev.to/devstackhub</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/devstackhub"/>
    <language>en</language>
    <item>
      <title>Best AI Web Scraper Tools in 2026: I Tested 7, These Are Worth It</title>
      <dc:creator>Dev Stack Hub</dc:creator>
      <pubDate>Sun, 01 Nov 2026 15:14:04 +0000</pubDate>
      <link>https://dev.to/devstackcommunity/best-ai-web-scraper-tools-in-2026-i-tested-7-these-are-worth-it-n2e</link>
      <guid>https://dev.to/devstackcommunity/best-ai-web-scraper-tools-in-2026-i-tested-7-these-are-worth-it-n2e</guid>
      <description>&lt;p&gt;I tested seven AI web scrapers, from no-code point-and-click tools to scraping APIs and open-source libraries. Here is what each one does well, where it falls short, and which one I would choose for different projects.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Last reviewed: October 2026. Pricing, plans, and features change fast in this category, so check each vendor's current docs before you commit.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No-code monitoring and scheduled extraction:&lt;/strong&gt; Browse AI&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No-code bulk scraping with ready-made templates:&lt;/strong&gt; Octoparse&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Quick extraction straight from your browser:&lt;/strong&gt; Thunderbit&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Developer feeding data to an LLM, RAG pipeline, or agent:&lt;/strong&gt; Firecrawl&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Protected sites at scale:&lt;/strong&gt; Bright Data&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Self-hosted Python with LLM-based extraction:&lt;/strong&gt; ScrapeGraphAI&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Free, self-hosted crawling to clean Markdown:&lt;/strong&gt; Crawl4AI&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;AI web scraping sounds simple: point a tool at a page, say what data you want, and get clean rows or JSON back. But once you move past the demo page, the differences between tools become obvious. Layout changes break selectors, JavaScript-heavy pages return empty HTML, anti-bot systems block requests, and the cost model decides whether the project survives.&lt;/p&gt;

&lt;p&gt;Some of these tools are built for people who never want to see a selector. Others give developers an API that returns LLM-ready Markdown. A few are open-source libraries you run on your own machine.&lt;/p&gt;

&lt;p&gt;I compared 7 tools across three groups: no-code scrapers, scraping APIs, and open-source libraries. This is an editorial comparison based on each tool's documented capabilities and public product information, not a controlled benchmark in which every tool ran against identical pages.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;My biggest takeaway:&lt;/strong&gt; The best AI scraper is the one that still works in week two, when the site changes its layout, the volume grows, and the data has to keep flowing.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is an AI web scraper?
&lt;/h2&gt;

&lt;p&gt;A traditional scraper relies on selectors (CSS or XPath) that you write and maintain. An AI web scraper uses a language model or learned patterns to decide what to extract from a plain-language prompt, a schema, or a few clicks. Many also return output meant for LLMs, such as clean Markdown or structured JSON.&lt;/p&gt;

&lt;p&gt;For example, you might ask a tool to pull the name, price, and availability from every product page on a store, without writing a single selector.&lt;/p&gt;

&lt;p&gt;But AI doesn't remove the hard parts of scraping: rendering JavaScript, avoiding blocks, running at scale, and staying within a site's rules. The tools also put AI in different places. Some use it only to suggest fields, some to interpret every page, and some mainly to render and clean pages for an LLM to read. That distinction should drive your choice.&lt;/p&gt;

&lt;h2&gt;
  
  
  How AI scrapers work under the hood
&lt;/h2&gt;

&lt;p&gt;Most AI scrapers run the same four stages, and the tools differ mainly in which stages they handle for you.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Stage&lt;/th&gt;
&lt;th&gt;What happens&lt;/th&gt;
&lt;th&gt;Typical failure&lt;/th&gt;
&lt;th&gt;Who handles it&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1. Fetch and render&lt;/td&gt;
&lt;td&gt;Load the page, run JavaScript, route through proxies&lt;/td&gt;
&lt;td&gt;Empty HTML, 403 errors, CAPTCHAs&lt;/td&gt;
&lt;td&gt;Scraping APIs and Bright Data; you, with open-source libraries&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2. Clean&lt;/td&gt;
&lt;td&gt;Strip navigation, ads, and scripts; convert to Markdown or simplified HTML&lt;/td&gt;
&lt;td&gt;Boilerplate leaks into the output&lt;/td&gt;
&lt;td&gt;Firecrawl and Crawl4AI&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3. Extract&lt;/td&gt;
&lt;td&gt;Pull fields with selectors, a schema, or an LLM&lt;/td&gt;
&lt;td&gt;Wrong or missing fields&lt;/td&gt;
&lt;td&gt;All of them, in different ways&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4. Validate and store&lt;/td&gt;
&lt;td&gt;Check types, retry failures, save&lt;/td&gt;
&lt;td&gt;Silent bad data&lt;/td&gt;
&lt;td&gt;You&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The practical difference shows up in how you describe what you want. A traditional scraper encodes the structure of the page, so it breaks when the markup changes:&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="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;bs4&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BeautifulSoup&lt;/span&gt;

&lt;span class="n"&gt;html&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://example.com/products&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;
&lt;span class="n"&gt;soup&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;BeautifulSoup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;html.parser&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;products&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;card&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select_one&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;h2.product-title&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_text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;strip&lt;/span&gt;&lt;span class="o"&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;price&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;card&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select_one&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;span.price&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_text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;strip&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&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;card&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;soup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;div.product-card&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;An AI scraper encodes the result you want instead. You describe the output as a schema, and the tool works out where the fields are:&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;pydantic&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BaseModel&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;
    &lt;span class="n"&gt;in_stock&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;

&lt;span class="c1"&gt;# An AI scraper takes a schema like this plus a URL or a prompt and
# returns objects shaped like Product. The sections below show
# example calls for each tool.
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Choosing an approach by page type
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Page type&lt;/th&gt;
&lt;th&gt;Symptom&lt;/th&gt;
&lt;th&gt;Start with&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Static HTML&lt;/td&gt;
&lt;td&gt;The data is visible in the raw response&lt;/td&gt;
&lt;td&gt;Plain requests with selectors, or Crawl4AI&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;JavaScript-rendered&lt;/td&gt;
&lt;td&gt;The raw response is nearly empty&lt;/td&gt;
&lt;td&gt;Firecrawl, Crawl4AI (Playwright), or Bright Data's Browser API&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Protected&lt;/td&gt;
&lt;td&gt;403 errors, CAPTCHAs, rate limiting&lt;/td&gt;
&lt;td&gt;Bright Data's unblocking tools, or Firecrawl&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Behind a login&lt;/td&gt;
&lt;td&gt;Content appears only after sign-in&lt;/td&gt;
&lt;td&gt;Browse AI robots, Firecrawl's interact features, or a browser-based API&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Irregular layouts&lt;/td&gt;
&lt;td&gt;Selectors keep breaking&lt;/td&gt;
&lt;td&gt;LLM extraction with ScrapeGraphAI or Firecrawl's extract&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  How the tools are compared
&lt;/h2&gt;

&lt;p&gt;A scraper that works on one clean page tells you little about week two. So the tools are judged on five criteria that matter after the first successful run:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Ease of setup:&lt;/strong&gt; how quickly its intended user gets a first working extraction, and what skills that takes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Extraction quality:&lt;/strong&gt; whether you get accurate, structured output from messy real-world pages&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dynamic sites and anti-bot handling:&lt;/strong&gt; JavaScript rendering, logins, and blocking&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scale and cost control:&lt;/strong&gt; how volume, concurrency, and pricing behave as the project grows, and whether you can self-host&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Output and integrations:&lt;/strong&gt; APIs, exports, scheduling, LLM-ready formats, and MCP support&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The better tool keeps delivering clean data when the site changes, volume grows, and the target starts fighting back.&lt;/p&gt;

&lt;h2&gt;
  
  
  The 7 tools at a glance
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;Best for&lt;/th&gt;
&lt;th&gt;Coding knowledge needed&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Browse AI&lt;/td&gt;
&lt;td&gt;No-code scraper&lt;/td&gt;
&lt;td&gt;Scheduled extraction and change monitoring&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Octoparse&lt;/td&gt;
&lt;td&gt;No-code scraper&lt;/td&gt;
&lt;td&gt;Template-based bulk scraping with cloud runs&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Thunderbit&lt;/td&gt;
&lt;td&gt;No-code scraper&lt;/td&gt;
&lt;td&gt;Fast extraction from your browser&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Firecrawl&lt;/td&gt;
&lt;td&gt;Scraping API&lt;/td&gt;
&lt;td&gt;LLM-ready Markdown and JSON for apps and agents&lt;/td&gt;
&lt;td&gt;Beginner to intermediate&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bright Data&lt;/td&gt;
&lt;td&gt;Scraping API and infrastructure&lt;/td&gt;
&lt;td&gt;Protected sites and large-scale collection&lt;/td&gt;
&lt;td&gt;Intermediate to advanced&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ScrapeGraphAI&lt;/td&gt;
&lt;td&gt;Open-source library&lt;/td&gt;
&lt;td&gt;Prompt-driven extraction in Python&lt;/td&gt;
&lt;td&gt;Intermediate&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Crawl4AI&lt;/td&gt;
&lt;td&gt;Open-source library&lt;/td&gt;
&lt;td&gt;Self-hosted crawling to clean Markdown&lt;/td&gt;
&lt;td&gt;Intermediate&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Scorecard against the five criteria
&lt;/h3&gt;

&lt;p&gt;These ratings reflect my editorial judgment based on documented capabilities, not measured test results. Ease of setup is rated for each tool's intended user, so a high score for an API means it is easy for a developer.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Ease of setup&lt;/th&gt;
&lt;th&gt;Extraction quality&lt;/th&gt;
&lt;th&gt;Dynamic sites and anti-bot&lt;/th&gt;
&lt;th&gt;Scale and cost control&lt;/th&gt;
&lt;th&gt;Output and integrations&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Browse AI&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Octoparse&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Thunderbit&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Firecrawl&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bright Data&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ScrapeGraphAI&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Crawl4AI&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The trade-off is clear. No-code tools are the quickest to start but the least flexible at scale. Scraping APIs handle blocking and volume for you at a usage-based cost. Open-source libraries give you the most control and the lowest cost, but leave browsers, proxies, and scaling to you.&lt;/p&gt;

&lt;h3&gt;
  
  
  Who should pick what
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;You are...&lt;/th&gt;
&lt;th&gt;Start with&lt;/th&gt;
&lt;th&gt;Why&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;A marketer or analyst who needs recurring data&lt;/td&gt;
&lt;td&gt;Browse AI or Octoparse&lt;/td&gt;
&lt;td&gt;You set it up visually and it runs on a schedule&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Someone who needs a table from a page right now&lt;/td&gt;
&lt;td&gt;Thunderbit&lt;/td&gt;
&lt;td&gt;Extraction starts from the page in your browser&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A developer building an LLM app, RAG pipeline, or agent&lt;/td&gt;
&lt;td&gt;Firecrawl&lt;/td&gt;
&lt;td&gt;One API for scrape, crawl, search, and extract, with Markdown and JSON output&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A team scraping protected sites at scale&lt;/td&gt;
&lt;td&gt;Bright Data&lt;/td&gt;
&lt;td&gt;Proxy and unblocking infrastructure is the core product&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A Python developer who wants prompt-based extraction&lt;/td&gt;
&lt;td&gt;ScrapeGraphAI&lt;/td&gt;
&lt;td&gt;Describe the fields in a prompt and run it with your own LLM&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A developer who wants free, self-hosted crawling&lt;/td&gt;
&lt;td&gt;Crawl4AI&lt;/td&gt;
&lt;td&gt;Open source, runs on your own infrastructure&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;My default picks: Browse AI for non-developers, Firecrawl for developers building with LLMs, Bright Data when blocking is the main problem, and Crawl4AI when you want to self-host. Replace any of these if the specifics below fit your project better.&lt;/p&gt;

&lt;h2&gt;
  
  
  Category 1: No-Code Scrapers (For Non-Developers)
&lt;/h2&gt;

&lt;p&gt;These are the best places to start if you want data in a spreadsheet without writing code or running infrastructure.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Browse AI
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpqcr94z2l6nxh7t6do1y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpqcr94z2l6nxh7t6do1y.png" alt=" " width="800" height="364"&gt;&lt;/a&gt;&lt;br&gt;
I would send someone who needs data from a website on a schedule and doesn't want to touch code to Browse AI. You train a robot by pointing and clicking on the data you want, then run it on demand or on a schedule. Its help center describes it as a way to scrape, monitor, and automate data extraction without coding.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What makes it useful&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Point-and-click robot training, with no selectors or XPath&lt;/li&gt;
&lt;li&gt;Scheduled runs with change monitoring and notifications&lt;/li&gt;
&lt;li&gt;Prebuilt robots for popular sites and use cases&lt;/li&gt;
&lt;li&gt;Handling for pagination, scrolling, and, in many cases, logged-in pages&lt;/li&gt;
&lt;li&gt;Results delivered as a spreadsheet, a file, or an API&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example, a retailer could train a robot on three competitor product pages, run it every morning, and get an alert when a price changes.&lt;/p&gt;

&lt;p&gt;The important part is to train on representative pages. A robot that has only seen one layout may miss fields on a page that looks slightly different.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where it falls short:&lt;/strong&gt; Robots are tied to the structure of the pages they were trained on, so a redesign can break them. Sites with aggressive anti-bot protection or complex login flows may not work reliably, and monitoring many pages can quickly use up credits.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;My verdict:&lt;/strong&gt; Browse AI is my starting recommendation for non-developers who need recurring data or change alerts from a handful of sites.&lt;/p&gt;
&lt;h3&gt;
  
  
  2. Octoparse
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0w8za0qzoszptxuzqul1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0w8za0qzoszptxuzqul1.png" alt=" " width="800" height="364"&gt;&lt;/a&gt;&lt;br&gt;
Octoparse is a no-code scraper with a desktop app and optional cloud runs. You select data visually, or start from a template, and it handles the extraction. Its website lists a library of preset templates for common targets such as maps, marketplaces, and directories.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What makes it useful&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A visual builder with automatic on-page data detection&lt;/li&gt;
&lt;li&gt;A large library of ready-made templates for popular sites&lt;/li&gt;
&lt;li&gt;Cloud runs and scheduling on paid plans, so your computer doesn't have to stay on&lt;/li&gt;
&lt;li&gt;Anti-blocking options such as proxies and CAPTCHA solving, on paid tiers&lt;/li&gt;
&lt;li&gt;An API and an MCP server for triggering tasks from other tools and agents&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example, an e-commerce analyst could pick a marketplace template, change the search term, and schedule a daily export of listings.&lt;/p&gt;

&lt;p&gt;The free plan is best treated as a trial. It runs locally, so recurring cloud jobs need a paid plan.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where it falls short:&lt;/strong&gt; Reviewers report slower results on JavaScript-heavy pages, and cloud concurrency is capped by plan. It isn't an API-first crawler, so developers who want code-level control will outgrow it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;My verdict:&lt;/strong&gt; Octoparse is a good fit for analysts and small teams that want template-driven bulk scraping and scheduling without a developer.&lt;/p&gt;
&lt;h3&gt;
  
  
  3. Thunderbit
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyykngx0t5qdtu0y3iv1k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyykngx0t5qdtu0y3iv1k.png" alt=" " width="800" height="366"&gt;&lt;/a&gt;&lt;br&gt;
Thunderbit is an AI scraper that runs as a Chrome extension. You open a page, click once, and it suggests the fields to extract, which you can adjust or extend by describing extra columns in plain language. It can also visit linked pages and add their data as extra columns.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What makes it useful&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AI-suggested fields, with custom columns defined by natural-language instructions&lt;/li&gt;
&lt;li&gt;Subpage scraping that enriches a table with data from linked pages&lt;/li&gt;
&lt;li&gt;Templates for popular sites&lt;/li&gt;
&lt;li&gt;Export to Google Sheets, Airtable, Notion, and common file formats&lt;/li&gt;
&lt;li&gt;Scheduled scraping on paid plans and an API for programmatic use&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example, a recruiter could scrape a list of candidate profiles and let it open each profile to collect a contact detail into a new column.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where it falls short:&lt;/strong&gt; Browser-based work suits small and medium jobs better than high-volume collection. Check how it handles logins, blocking, and credit usage for your target sites, and spot-check the AI's field suggestions before you trust a large export.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;My verdict:&lt;/strong&gt; Thunderbit is the quickest way to turn a page you are already looking at into a spreadsheet, best for one-off and small recurring jobs.&lt;/p&gt;
&lt;h3&gt;
  
  
  Working with exports from no-code tools
&lt;/h3&gt;

&lt;p&gt;No-code tools end in a CSV or JSON file, and that is where code takes over. Treat every export as untrusted input: coerce types and reject rows that don't fit your schema before they reach a database.&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="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pydantic&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ValidationError&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Row&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&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="nf"&gt;read_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;export.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;df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;price&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="n"&gt;pd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_numeric&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;price&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;astype&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nb"&gt;str&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="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;[^0-9.]&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="n"&gt;regex&lt;/span&gt;&lt;span class="o"&gt;=&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;errors&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;coerce&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;valid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rejected&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt; &lt;span class="ow"&gt;in&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_dict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;orient&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;records&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;valid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Row&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;ValidationError&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;rejected&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&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="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;valid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; valid, &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;rejected&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; rejected&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;h2&gt;
  
  
  Category 2: Scraping APIs and Infrastructure (For Developers)
&lt;/h2&gt;

&lt;p&gt;These give developers an API instead of a visual builder. You send a URL, and the service handles rendering, retries, and often blocking, then returns data your code can use.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Firecrawl
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fygtlfzifmfdjgtssj7c7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fygtlfzifmfdjgtssj7c7.png" alt=" " width="800" height="366"&gt;&lt;/a&gt;&lt;br&gt;
Firecrawl is built for developers who want web data in a form that LLMs can use. A single API covers scraping, crawling, mapping, search, and structured extraction, and it returns clean Markdown or JSON. Its documentation also describes endpoints for interacting with pages and for monitoring them for changes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What makes it useful&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Scrape one page, crawl a whole site, map its URLs, or search and scrape results in one call&lt;/li&gt;
&lt;li&gt;LLM-ready Markdown, or structured JSON from a schema or a prompt&lt;/li&gt;
&lt;li&gt;SDKs for Python, Node.js, Go, Rust, and Java, plus a CLI&lt;/li&gt;
&lt;li&gt;A hosted MCP server, so Claude, Cursor, and other MCP clients can call it directly&lt;/li&gt;
&lt;li&gt;An open-source version you can self-host&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example, to give an agent fresh context, you could search for a topic, scrape the top results as Markdown, and pass the text to the model.&lt;/p&gt;

&lt;p&gt;The important part is to start with the simplest endpoint that fits. A plain scrape often does the job before you need structured extraction.&lt;/p&gt;

&lt;p&gt;A minimal Python quick start with the official SDK. Response field names have varied between SDK versions, so check the current SDK reference before copying this into production.&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="c1"&gt;# pip install firecrawl-py
&lt;/span&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;firecrawl&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Firecrawl&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Firecrawl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;api_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;FIRECRAWL_API_KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="c1"&gt;# 1. Scrape one page to LLM-ready Markdown
&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scrape&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="n"&gt;formats&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;markdown&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;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;markdown&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="c1"&gt;# 2. Crawl a documentation site
&lt;/span&gt;&lt;span class="n"&gt;crawl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;crawl&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.example.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;limit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;scrape_options&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;formats&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;markdown&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;page&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;crawl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&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="n"&gt;markdown&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="c1"&gt;# 3. Search, then scrape the top results
&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;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;vector database benchmarks 2026&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;scrape_options&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;formats&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;markdown&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;For structured output, pass a schema so the response has a predictable shape:&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;pydantic&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BaseModel&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;
    &lt;span class="n"&gt;in_stock&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;

&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;extract&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;urls&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;https://example.com/product/42&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;schema&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;system_prompt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Extract the product name, price, and availability&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="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&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;Where it falls short:&lt;/strong&gt; It is credit-based, so crawling large sites adds up, and heavier features like extraction may cost more than a basic scrape. If you plan to self-host, check which features the open-source version includes compared with the hosted one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;My verdict:&lt;/strong&gt; Firecrawl is my default for developers building LLM apps, RAG pipelines, and agents that need clean web content with minimal setup.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Bright Data
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpvym2twm6isnn0eqx9lr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpvym2twm6isnn0eqx9lr.png" alt=" " width="800" height="366"&gt;&lt;/a&gt;&lt;br&gt;
Bright Data is less a single tool than a web data platform. Its documentation lists a Scraper API for structured data from specific sites, Scraper Studio, datasets, a Web Unlocker API, a SERP API, a Browser API, and large proxy networks, along with an MCP server for AI agents.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What makes it useful&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Unblocking infrastructure, with Web Unlocker handling blocks and CAPTCHAs&lt;/li&gt;
&lt;li&gt;Prebuilt scrapers that return structured data from popular sites&lt;/li&gt;
&lt;li&gt;A Browser API for pages that need a real browser session&lt;/li&gt;
&lt;li&gt;Proxy networks for geographic targeting&lt;/li&gt;
&lt;li&gt;An MCP server that exposes these tools to AI agents and coding assistants&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example, a team that tracks product data from heavily protected retail sites could use a prebuilt scraper instead of maintaining its own proxies and unblocking logic.&lt;/p&gt;

&lt;p&gt;To give an MCP client such as Claude Desktop or Cursor access to Bright Data, add the server to your MCP configuration. It runs through &lt;code&gt;npx&lt;/code&gt;, and by default it uses a Web Unlocker zone named &lt;code&gt;mcp_unlocker&lt;/code&gt;, which you can override with the &lt;code&gt;WEB_UNLOCKER_ZONE&lt;/code&gt; environment variable.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Bright 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;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"npx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&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;"@brightdata/mcp"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"env"&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;"API_TOKEN"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;your-brightdata-api-token&amp;gt;"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Where it falls short:&lt;/strong&gt; The breadth also creates complexity: you have to understand many products and pricing models before you pick the right one. Costs are usage-based, which is efficient for hard-to-reach sites and expensive for easy ones, so check which product your target actually needs. You remain responsible for collecting data legally and under each site's terms.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;My verdict:&lt;/strong&gt; Bright Data is the strongest option when getting blocked, not parsing, is your main problem, and you have the engineering time to use a larger platform.&lt;/p&gt;

&lt;h2&gt;
  
  
  Category 3: Open-Source Libraries (For Self-Hosters)
&lt;/h2&gt;

&lt;p&gt;These are libraries you install and run yourself. You get the most control and the lowest license cost, but you handle the browsers, proxies, and scaling.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. ScrapeGraphAI
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flr79ki51fj24tq8kj2q0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flr79ki51fj24tq8kj2q0.png" alt=" " width="800" height="362"&gt;&lt;/a&gt;&lt;br&gt;
ScrapeGraphAI is a Python library that combines an LLM with graph-based pipelines to scrape websites and local documents. You say which information you want, and the library builds the extraction for you. Its GitHub repository is MIT-licensed and also points to a managed cloud API with SDKs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What makes it useful&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Prompt-driven extraction: describe the fields instead of writing selectors&lt;/li&gt;
&lt;li&gt;Ready-made pipelines, such as a single-page scraper, a search-and-scrape pipeline, and one that generates a Python script for multiple pages&lt;/li&gt;
&lt;li&gt;Works with hosted models or local ones through Ollama&lt;/li&gt;
&lt;li&gt;Supports schemas for structured output&lt;/li&gt;
&lt;li&gt;A managed API is available if you don't want to run it yourself&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example, you could point it at a company page and ask for the description, founders, and contact email, and get a dictionary back.&lt;/p&gt;

&lt;p&gt;The important part is to define a schema. Without one, the output shape can change between runs.&lt;/p&gt;

&lt;p&gt;Here is a schema-based pipeline that runs against a local model through Ollama. To use a hosted model instead, change the &lt;code&gt;llm&lt;/code&gt; block to include an &lt;code&gt;api_key&lt;/code&gt; and a model such as &lt;code&gt;openai/gpt-4o-mini&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="c1"&gt;# pip install scrapegraphai &amp;amp;&amp;amp; playwright install
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;typing&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pydantic&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Field&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;scrapegraphai.graphs&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;SmartScraperGraph&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Product name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Price as shown on the page&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ProductList&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;products&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;graph_config&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;llm&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;model&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;ollama/llama3.2&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;model_tokens&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;8192&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;headless&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="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;graph&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SmartScraperGraph&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Extract all products with their names and prices&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;source&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://example.com/products&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;schema&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ProductList&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;graph_config&lt;/span&gt;&lt;span class="p"&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;graph&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&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;Where it falls short:&lt;/strong&gt; In the open-source version, you manage the browsers, proxies, and scaling yourself, and every page costs LLM tokens or local compute. Because an LLM interprets each page, extraction can vary, so validate the output before it feeds anything important. The project's README also frames it as meant for data exploration and research.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;My verdict:&lt;/strong&gt; ScrapeGraphAI is a good fit for Python developers who want to prototype prompt-based extraction quickly and are happy to run their own LLM.&lt;/p&gt;

&lt;h3&gt;
  
  
  7. Crawl4AI
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnyd36visex1d1nzpvb3v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnyd36visex1d1nzpvb3v.png" alt=" " width="800" height="366"&gt;&lt;/a&gt;&lt;br&gt;
Crawl4AI is an open-source, LLM-friendly crawler and scraper that turns web pages into clean Markdown for RAG, agents, and data pipelines. It runs on Playwright, installs with pip, and can also be deployed with Docker. See the GitHub repository and the documentation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What makes it useful&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Clean Markdown output, with a filtered mode that strips noise from pages&lt;/li&gt;
&lt;li&gt;An async crawler with a browser pool, caching, and deep crawling strategies&lt;/li&gt;
&lt;li&gt;Structured extraction with CSS-based strategies, or with any LLM, including local models&lt;/li&gt;
&lt;li&gt;Sessions, proxies, cookies, and hooks for custom behavior&lt;/li&gt;
&lt;li&gt;A command-line interface and Docker images, with no API keys required&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example, you could crawl a documentation site to depth three, save the Markdown, and feed it into a vector store.&lt;/p&gt;

&lt;p&gt;The important part is to use CSS-based extraction for repetitive pages and save the LLM for irregular ones. It is cheaper and more predictable.&lt;/p&gt;

&lt;p&gt;This example uses CSS-based extraction, so the crawl needs no LLM calls. It also prints the page's Markdown, and extraction strategies and config options have changed between releases, so check the documentation for your installed version.&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="c1"&gt;# pip install crawl4ai &amp;amp;&amp;amp; crawl4ai-setup
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;crawl4ai&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;AsyncWebCrawler&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CrawlerRunConfig&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CacheMode&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;crawl4ai.extraction_strategy&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;JsonCssExtractionStrategy&lt;/span&gt;

&lt;span class="n"&gt;schema&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;name&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;Products&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;baseSelector&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;div.product-card&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;fields&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;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;selector&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;h2.product-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;type&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;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&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;price&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;selector&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;span.price&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;type&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;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;CrawlerRunConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;extraction_strategy&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;JsonCssExtractionStrategy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;cache_mode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;CacheMode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BYPASS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nc"&gt;AsyncWebCrawler&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;crawler&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;crawler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;arun&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/products&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;config&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;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;extracted_content&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;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;markdown&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The command-line interface covers quick jobs without writing any Python:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Crawl one page to Markdown&lt;/span&gt;
crwl https://www.example.com/blog &lt;span class="nt"&gt;-o&lt;/span&gt; markdown

&lt;span class="c"&gt;# Deep crawl with a breadth-first strategy, capped at 10 pages&lt;/span&gt;
crwl https://docs.crawl4ai.com &lt;span class="nt"&gt;--deep-crawl&lt;/span&gt; bfs &lt;span class="nt"&gt;--max-pages&lt;/span&gt; 10
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Where it falls short:&lt;/strong&gt; You run and maintain the infrastructure, including browser memory, scaling, and proxies. Anti-bot handling is largely your responsibility, so heavily protected sites will need extra work or a service like the ones above.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;My verdict:&lt;/strong&gt; Crawl4AI is a strong choice for developers who want free, self-hosted crawling to Markdown and don't mind operating it.&lt;/p&gt;

&lt;h3&gt;
  
  
  ScrapeGraphAI or Crawl4AI?
&lt;/h3&gt;

&lt;p&gt;Both are open source and run on your own machine. Crawl4AI focuses on fast, controllable crawling and clean Markdown, with optional LLM extraction. ScrapeGraphAI starts from the LLM and builds the extraction around a prompt. If you mostly need crawled content for retrieval, start with Crawl4AI. If you want to describe fields and get structured data back, try ScrapeGraphAI. Test each on the same small set of your own pages and compare the output.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pricing, scale, and lock-in
&lt;/h2&gt;

&lt;p&gt;Prices change too often to list, but the models differ in ways that matter:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No-code tools&lt;/strong&gt; are typically plan- or credit-based, and cloud runs, scheduling, and anti-blocking often sit on paid tiers. Free plans are better for trials than production.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scraping APIs&lt;/strong&gt; are usage-based. Firecrawl bills in credits per page, while Bright Data prices by product and volume, so estimate your page count before you commit.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Open-source libraries&lt;/strong&gt; cost nothing to license, but you pay for servers, proxies, and, for LLM extraction, tokens.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Lock-in lives in the setup. Robots, templates, and workflows built inside a no-code tool don't move easily, while open-source code and standard outputs like Markdown and JSON do.&lt;/p&gt;

&lt;p&gt;In general, the more the tool does for you, the more you should check export options and exit costs before you commit.&lt;/p&gt;

&lt;h2&gt;
  
  
  What AI scrapers can't replace
&lt;/h2&gt;

&lt;p&gt;AI has made scraping more accessible, but it hasn't removed the need for judgment and validation.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Rules and permissions still matter:&lt;/strong&gt; Check a site's terms and robots.txt, avoid personal data you have no right to collect, and get legal advice if you're unsure. A tool being able to fetch a page doesn't mean you are allowed to use the data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Extraction needs verification:&lt;/strong&gt; Language models can misread a field or invent a value. Spot-check outputs, validate against a schema, and watch for silent changes in format.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Maintenance never disappears:&lt;/strong&gt; Sites change layouts, add blocking, and move content behind logins. Plan for monitoring and fixes, not a one-time setup.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cost has to be modeled:&lt;/strong&gt; Credits, proxies, and LLM tokens scale with page count. Test on a small sample and multiply before you commit to a large crawl.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;AI scraping works best when the tool handles the repetitive extraction, and you stay responsible for what you collect, how you use it, and whether it is correct.&lt;/p&gt;

&lt;h2&gt;
  
  
  Production patterns that apply to any scraper
&lt;/h2&gt;

&lt;p&gt;These three patterns work with every tool in this guide, because they wrap whatever scraper you choose.&lt;/p&gt;

&lt;h3&gt;
  
  
  Check robots.txt before you fetch
&lt;/h3&gt;

&lt;p&gt;Python's standard library can read robots.txt. Remember that it is a convention, not legal permission, and a site's terms of service still apply.&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;urllib&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;robotparser&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;urllib.parse&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;urlparse&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;allowed&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="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user_agent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;my-scraper&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;parts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;urlparse&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;parser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;robotparser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;RobotFileParser&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_url&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;parts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;scheme&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;parts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;netloc&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/robots.txt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;can_fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_agent&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Retry with exponential backoff
&lt;/h3&gt;

&lt;p&gt;Networks fail and sites rate-limit. Retry transient errors with growing delays, and give up after a few attempts instead of looping forever.&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;random&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;with_retries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;attempts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;1.0&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;attempt&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;attempts&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&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;attempt&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;attempts&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;raise&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="n"&gt;base&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt; &lt;span class="n"&gt;attempt&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Validate every record
&lt;/h3&gt;

&lt;p&gt;LLM extraction can return a plausible but wrong value. Validate against a schema and treat a failure as a signal to inspect, not a row to store.&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;pydantic&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ValidationError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;field_validator&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;

    &lt;span class="nd"&gt;@field_validator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;price&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nd"&gt;@classmethod&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;price_is_sane&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;100_000&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;price out of range&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;ValidationError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;  &lt;span class="c1"&gt;# log it, don't store it
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Run your own comparison
&lt;/h3&gt;

&lt;p&gt;The scores in this guide come from documentation, not from your pages, so test on your own. Write one small function per tool that takes a URL and returns a dict, then run them all through the same harness. It reports median latency and how many of your expected fields each tool actually filled in.&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;time&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;statistics&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;median&lt;/span&gt;

&lt;span class="n"&gt;URLS&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;https://example.com/product/1&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;https://example.com/product/2&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;https://example.com/product/3&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;EXPECTED_FIELDS&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;name&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;price&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;in_stock&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;benchmark&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;scrape_fn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;urls&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;URLS&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;latencies&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;completeness&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;urls&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&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;perf_counter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;record&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;scrape_fn&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="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;record&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
        &lt;span class="n"&gt;latencies&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&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;perf_counter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;found&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;items&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;value&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;None&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="n"&gt;completeness&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&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;found&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;EXPECTED_FIELDS&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&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;EXPECTED_FIELDS&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;average&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;completeness&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&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;completeness&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="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;: median &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;median&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;latencies&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;s, field completeness &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;average&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;%&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="c1"&gt;# benchmark("firecrawl", firecrawl_scrape)
# benchmark("crawl4ai", crawl4ai_scrape)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use at least 20 pages that include your hardest cases, such as long product lists, missing fields, and slow-loading pages. Then compare cost per page alongside the results.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final verdict
&lt;/h2&gt;

&lt;p&gt;No single tool suits every project. If you aren't a developer, start with Browse AI for scheduled data and change alerts, Octoparse for template-based bulk jobs, or Thunderbit for quick extraction from your browser. If you're building with LLMs, use Firecrawl. If the target fights back, use Bright Data. If you want to self-host, choose Crawl4AI for crawling to Markdown or ScrapeGraphAI for prompt-based extraction.&lt;/p&gt;

&lt;p&gt;AI scraping shortens the time between wanting data and having it, but the first successful run is only the beginning. Choose the tool that fits your technical comfort, handles your target sites, and keeps working when those sites change.&lt;/p&gt;

&lt;h2&gt;
  
  
  Frequently asked questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What is the best AI web scraper?
&lt;/h3&gt;

&lt;p&gt;It depends on who is using it. Browse AI, Octoparse, and Thunderbit suit non-developers. Firecrawl suits developers building with LLMs, Bright Data suits protected sites at scale, and Crawl4AI and ScrapeGraphAI suit self-hosters.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can I scrape a website without coding?
&lt;/h3&gt;

&lt;p&gt;Yes. No-code tools let you select data visually or describe it, then export it to a spreadsheet or app. Complex sites with logins or heavy blocking may still need a developer or a more capable service.&lt;/p&gt;

&lt;h3&gt;
  
  
  What are the best web scraping tools for beginners?
&lt;/h3&gt;

&lt;p&gt;For beginners, no-code &lt;a href="https://dev.to/devstackhub/top-13-web-scraping-tools-every-developer-should-know-31p4"&gt;web scraping tools&lt;/a&gt; are the easiest starting point since they need no coding knowledge. Browse AI and Octoparse are good for scheduled or template-based jobs, and Thunderbit works well for quick one-off extraction from your browser. Once you're comfortable with Python, Firecrawl and Crawl4AI are approachable next steps for LLM-ready output.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is the difference between a scraping API and an open-source library?
&lt;/h3&gt;

&lt;p&gt;A scraping API is a hosted service that handles rendering, retries, and often blocking, and bills by usage. An open-source library runs on your own infrastructure, costs nothing to license, and leaves browsers, proxies, and scaling to you.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is AI web scraping legal?
&lt;/h3&gt;

&lt;p&gt;It depends on the site, the data, and where you are. Review the site's terms and robots.txt, avoid collecting personal data without a lawful basis, and get legal advice for commercial or sensitive projects.&lt;/p&gt;

&lt;h3&gt;
  
  
  Which tool is best for feeding data to an LLM or RAG pipeline?
&lt;/h3&gt;

&lt;p&gt;Firecrawl and Crawl4AI both return clean Markdown designed for LLMs. Firecrawl is a hosted API with SDKs and an MCP server, while Crawl4AI is free to self-host. Bright Data can supply the pages when sites block simpler tools.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webscraping</category>
      <category>python</category>
      <category>discuss</category>
    </item>
    <item>
      <title>Great guide! Playwright is a powerful choice for scraping JavaScript-heavy websites. The practical examples make it easier to understand how to handle dynamic content and extract data reliably.</title>
      <dc:creator>Dev Stack Hub</dc:creator>
      <pubDate>Sat, 31 Oct 2026 15:38:50 +0000</pubDate>
      <link>https://dev.to/devstackhub/api-gateways-can-make-or-break-an-applications-architecture-comparing-the-right-tools-helps-5feo</link>
      <guid>https://dev.to/devstackhub/api-gateways-can-make-or-break-an-applications-architecture-comparing-the-right-tools-helps-5feo</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/alinashah/scrape-javascript-heavy-sites-with-playwright-in-python-developer-guide-49mh" class="crayons-story__hidden-navigation-link"&gt;Scrape JavaScript-Heavy Sites with Playwright in Python (Developer Guide)&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/alinashah" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4139861%2Fad406f4f-1a76-4259-9ba8-42437ccdbfc3.jpg" alt="alinashah profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/alinashah" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Alina Shah
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Alina Shah
                
                
              
              &lt;div id="story-author-preview-content-4953037" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/alinashah" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4139861%2Fad406f4f-1a76-4259-9ba8-42437ccdbfc3.jpg" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Alina Shah&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/alinashah/scrape-javascript-heavy-sites-with-playwright-in-python-developer-guide-49mh" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Oct 31&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/alinashah/scrape-javascript-heavy-sites-with-playwright-in-python-developer-guide-49mh" id="article-link-4953037"&gt;
          Scrape JavaScript-Heavy Sites with Playwright in Python (Developer Guide)
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag crayons-tag--filled  " href="/t/discuss"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;discuss&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/javascript"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;javascript&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/python"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;python&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/playwright"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;playwright&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/alinashah/scrape-javascript-heavy-sites-with-playwright-in-python-developer-guide-49mh" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/fire-f60e7a582391810302117f987b22a8ef04a2fe0df7e3258a5f49332df1cec71e.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/raised-hands-74b2099fd66a39f2d7eed9305ee0f4553df0eb7b4f11b01b6b1b499973048fe5.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;13&lt;span class="hidden s:inline"&gt;&amp;nbsp;reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/alinashah/scrape-javascript-heavy-sites-with-playwright-in-python-developer-guide-49mh#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              10&lt;span class="hidden s:inline"&gt;&amp;nbsp;comments&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            9 min read
          &lt;/small&gt;
          
          &lt;div class="only-sidebar-menu-item trusted-visible-block print-hidden align-center"&gt;
                
                    
                
          &lt;/div&gt;

        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
      <category>python</category>
      <category>tutorial</category>
      <category>webscraping</category>
    </item>
    <item>
      <title>6 Best API Gateways for Developers Breaking Their Routing Logic</title>
      <dc:creator>Dev Stack Hub</dc:creator>
      <pubDate>Sat, 31 Oct 2026 14:43:59 +0000</pubDate>
      <link>https://dev.to/devstackhub/6-best-api-gateways-for-developers-breaking-their-routing-logic-4a99</link>
      <guid>https://dev.to/devstackhub/6-best-api-gateways-for-developers-breaking-their-routing-logic-4a99</guid>
      <description>&lt;p&gt;Six API gateways compared through the lens of routing: path matching, rewrites, header-based rules, and traffic splitting. Here is what each one does well, where it falls short, and which one I would choose for different projects.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Last reviewed: October 2026.&lt;/strong&gt; Pricing, licensing, and features change often in this category, so check each vendor's current docs before you commit.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;AWS-native and fully managed:&lt;/strong&gt; Amazon API Gateway.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enterprise API programs with governance and policies:&lt;/strong&gt; Apigee.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Microsoft and Azure shops:&lt;/strong&gt; Azure API Management.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fine-grained route matching and a big plugin ecosystem:&lt;/strong&gt; Kong Gateway (read the licensing note first).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stateless, declarative, and good at aggregation:&lt;/strong&gt; KrakenD.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Open-source core with an optional dashboard:&lt;/strong&gt; Tyk.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Most gateway bugs are routing bugs. A trailing slash that returns a 404. A rewrite that doubles a path segment. A canary that sends everything to the new version. A header rule that never fires because the casing is different.&lt;/p&gt;

&lt;p&gt;Every gateway here can route a request. They differ in how you express rules, split traffic, ship config changes, and what happens when a rule is wrong.&lt;/p&gt;

&lt;p&gt;I compared 6 gateways across two groups: cloud-managed and self-hosted. This is an editorial comparison based on each product's documented capabilities, not a benchmark in which every gateway handled identical traffic.&lt;/p&gt;

&lt;p&gt;My biggest takeaway: the best gateway is the one whose routing rules you can read, test, and change safely when something breaks at 2 a.m.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is an API gateway?
&lt;/h2&gt;

&lt;p&gt;An API gateway is the single entry point between clients and your services. It receives every request, decides which upstream should handle it, and applies cross-cutting rules on the way: authentication, rate limits, transformations, and logging.&lt;/p&gt;

&lt;p&gt;For example, a client calls &lt;code&gt;/v1/orders/42&lt;/code&gt;, and the gateway authenticates it, rewrites the path to &lt;code&gt;/orders/42&lt;/code&gt;, and forwards it to the orders service without the client knowing where that service lives.&lt;/p&gt;

&lt;p&gt;That routing step is where gateways differ most. Some express rules as declarative config, some as XML policies, some as code-like expressions. That distinction should drive your choice.&lt;/p&gt;

&lt;h2&gt;
  
  
  How gateway routing works
&lt;/h2&gt;

&lt;p&gt;Every gateway in this guide runs roughly the same pipeline for each request:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Stage&lt;/th&gt;
&lt;th&gt;What happens&lt;/th&gt;
&lt;th&gt;Typical failure&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1. Match&lt;/td&gt;
&lt;td&gt;Find the route by host, path, method, and headers&lt;/td&gt;
&lt;td&gt;Wrong route wins, or no route matches&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2. Authenticate and limit&lt;/td&gt;
&lt;td&gt;Check keys, tokens, and quotas&lt;/td&gt;
&lt;td&gt;Rule applied to the wrong route&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3. Transform&lt;/td&gt;
&lt;td&gt;Rewrite the path, add or strip headers&lt;/td&gt;
&lt;td&gt;Doubled or missing path segments&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4. Select upstream&lt;/td&gt;
&lt;td&gt;Pick a backend: static, weighted, or conditional&lt;/td&gt;
&lt;td&gt;Split ratios that don't match the config&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5. Proxy&lt;/td&gt;
&lt;td&gt;Forward with timeouts, retries, and health checks&lt;/td&gt;
&lt;td&gt;Retries on non-idempotent calls&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6. Respond&lt;/td&gt;
&lt;td&gt;Return, transform, or aggregate the response&lt;/td&gt;
&lt;td&gt;Leaked internal headers or errors&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Here is how each gateway expresses the routing-specific parts:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Gateway&lt;/th&gt;
&lt;th&gt;Match on&lt;/th&gt;
&lt;th&gt;Rewrite with&lt;/th&gt;
&lt;th&gt;Split traffic with&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Amazon API Gateway&lt;/td&gt;
&lt;td&gt;Resource path and method&lt;/td&gt;
&lt;td&gt;Integration request mapping&lt;/td&gt;
&lt;td&gt;Canary release settings on a REST API stage&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Apigee&lt;/td&gt;
&lt;td&gt;Base path, plus conditions on request variables&lt;/td&gt;
&lt;td&gt;Policies and the target Path&lt;/td&gt;
&lt;td&gt;Weighted load balancer across target servers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Azure API Management&lt;/td&gt;
&lt;td&gt;Operation URL template and method, plus policy expressions&lt;/td&gt;
&lt;td&gt;The &lt;code&gt;rewrite-uri&lt;/code&gt; policy&lt;/td&gt;
&lt;td&gt;A &lt;code&gt;choose&lt;/code&gt; policy with a random expression, or backend pools&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kong Gateway&lt;/td&gt;
&lt;td&gt;Paths (including regex), methods, hosts, and headers&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;strip_path&lt;/code&gt; and the service path&lt;/td&gt;
&lt;td&gt;Weighted targets in an upstream&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;KrakenD&lt;/td&gt;
&lt;td&gt;Endpoint and method; header conditions in Enterprise&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;url_pattern&lt;/code&gt; on each backend&lt;/td&gt;
&lt;td&gt;Repeated hosts for weights, or DNS SRV weights&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tyk&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;listen_path&lt;/code&gt;, API versions, and rewrite rules&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;strip_listen_path&lt;/code&gt; and &lt;code&gt;target_url&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Load balancing with weights set in the Dashboard&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  One routing problem, six configs
&lt;/h2&gt;

&lt;p&gt;To compare gateways fairly, I use the same problem throughout:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Rewrite:&lt;/strong&gt; &lt;code&gt;GET /v1/orders/{id}&lt;/code&gt; must reach the upstream as &lt;code&gt;/orders/{id}&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Split:&lt;/strong&gt; 90 percent of traffic goes to orders v1, and 10 percent goes to orders v2.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Override:&lt;/strong&gt; a request with the header &lt;code&gt;X-Canary: true&lt;/code&gt; always goes to v2.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each tool section shows the config for this problem. Where a gateway can't do a part natively, I say so. The testing section at the end includes a small harness you can run against any of them.&lt;/p&gt;

&lt;h2&gt;
  
  
  How the tools are compared
&lt;/h2&gt;

&lt;p&gt;A gateway that routes a demo request tells you little about production. So the tools are judged on five criteria that matter once real traffic and real teams arrive:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Routing expressiveness:&lt;/strong&gt; how precisely you can match requests and rewrite them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Traffic splitting and releases:&lt;/strong&gt; how well it supports canary, blue/green, and header-based overrides.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Config model and GitOps:&lt;/strong&gt; whether rules live in reviewable files you can test and deploy from CI.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Operations:&lt;/strong&gt; how much infrastructure you run, and how it scales and fails.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cost and licensing clarity:&lt;/strong&gt; how predictable the pricing and license terms are.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The better gateway is the one that lets you change a routing rule safely, review it in a pull request, and roll it back quickly.&lt;/p&gt;

&lt;h2&gt;
  
  
  The 6 tools at a glance
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;Best for&lt;/th&gt;
&lt;th&gt;Config style&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Amazon API Gateway&lt;/td&gt;
&lt;td&gt;Cloud-managed&lt;/td&gt;
&lt;td&gt;AWS-native APIs, Lambda, and private VPC integrations&lt;/td&gt;
&lt;td&gt;OpenAPI and infrastructure as code&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Apigee&lt;/td&gt;
&lt;td&gt;Cloud-managed&lt;/td&gt;
&lt;td&gt;Large API programs with governance&lt;/td&gt;
&lt;td&gt;XML proxy bundles and policies&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Azure API Management&lt;/td&gt;
&lt;td&gt;Cloud-managed&lt;/td&gt;
&lt;td&gt;Azure-based teams and hybrid deployments&lt;/td&gt;
&lt;td&gt;XML policies and Bicep or ARM&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kong Gateway&lt;/td&gt;
&lt;td&gt;Self-hosted&lt;/td&gt;
&lt;td&gt;Rich route matching and plugins&lt;/td&gt;
&lt;td&gt;Declarative YAML, decK, or Admin API&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;KrakenD&lt;/td&gt;
&lt;td&gt;Self-hosted&lt;/td&gt;
&lt;td&gt;Stateless gateways and API aggregation&lt;/td&gt;
&lt;td&gt;A single JSON file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tyk&lt;/td&gt;
&lt;td&gt;Self-hosted&lt;/td&gt;
&lt;td&gt;Open-source core with optional dashboard&lt;/td&gt;
&lt;td&gt;API definitions in JSON or YAML&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Scorecard against the five criteria
&lt;/h2&gt;

&lt;p&gt;These ratings reflect my editorial judgment based on documented capabilities, not measured test results.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Routing expressiveness&lt;/th&gt;
&lt;th&gt;Traffic splitting and releases&lt;/th&gt;
&lt;th&gt;Config model and GitOps&lt;/th&gt;
&lt;th&gt;Operations&lt;/th&gt;
&lt;th&gt;Cost and licensing clarity&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Amazon API Gateway&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Apigee&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Azure API Management&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kong Gateway&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;KrakenD&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tyk&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The trade-off is clear. Cloud-managed gateways remove the operations work but tie your routing logic to one vendor's model. Self-hosted gateways give you declarative config and portability, and you run them yourself.&lt;/p&gt;

&lt;h2&gt;
  
  
  Who should pick what
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;You are...&lt;/th&gt;
&lt;th&gt;Start with&lt;/th&gt;
&lt;th&gt;Why&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;An AWS-native team using Lambda or private services&lt;/td&gt;
&lt;td&gt;Amazon API Gateway&lt;/td&gt;
&lt;td&gt;Fully managed, with native canary releases on REST APIs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A large organization with an API governance program&lt;/td&gt;
&lt;td&gt;Apigee&lt;/td&gt;
&lt;td&gt;Policy-driven proxies and API product management&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A team standardized on Azure&lt;/td&gt;
&lt;td&gt;Azure API Management&lt;/td&gt;
&lt;td&gt;Policy expressions and deep Azure integration&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A team that needs flexible route matching and plugins&lt;/td&gt;
&lt;td&gt;Kong Gateway&lt;/td&gt;
&lt;td&gt;Routes by path, host, header, and regex, plus weighted upstreams&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A team that wants a stateless gateway in one JSON file&lt;/td&gt;
&lt;td&gt;KrakenD&lt;/td&gt;
&lt;td&gt;Simple deployment and built-in aggregation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A team that wants an open-source core and a dashboard&lt;/td&gt;
&lt;td&gt;Tyk&lt;/td&gt;
&lt;td&gt;Open-source gateway with a commercial dashboard option&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;My default picks: Amazon API Gateway if you're on AWS, Azure API Management if you're on Azure, KrakenD for a simple self-hosted gateway, and Kong when you need the richest route matching and can accept its licensing terms. Replace any of these if the specifics below fit your project better.&lt;/p&gt;

&lt;h2&gt;
  
  
  Category 1: Cloud-Managed Gateways
&lt;/h2&gt;

&lt;p&gt;These run as a service, so you don't patch or scale gateway nodes. They are the best place to start if your backends already live in that cloud.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Amazon API Gateway
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fztibzar0z0ddpoekcajv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fztibzar0z0ddpoekcajv.png" alt=" " width="799" height="358"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://aws.amazon.com/api-gateway/" rel="noopener noreferrer"&gt;Amazon API Gateway&lt;/a&gt; is AWS's managed gateway. It offers REST, HTTP, and WebSocket APIs, and it integrates directly with Lambda, HTTP backends, and private services inside a VPC. Its canary release documentation describes how to send a percentage of traffic to a new deployment.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What makes it useful&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fully managed, with no gateway servers to run or scale.&lt;/li&gt;
&lt;li&gt;Stage variables let one API definition point to different backends per stage.&lt;/li&gt;
&lt;li&gt;Canary release deployments on REST API stages, with a &lt;code&gt;percentTraffic&lt;/code&gt; setting and stage variable overrides.&lt;/li&gt;
&lt;li&gt;Native integrations with Lambda, authorizers, usage plans, and AWS services.&lt;/li&gt;
&lt;li&gt;Configuration through OpenAPI, the AWS CLI, and infrastructure-as-code tools.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here is the rewrite part of the running problem for a REST API. The integration URI uses a stage variable for the host, and the path parameter is mapped from the method request to the integration request.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;openapi&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3.0.1"&lt;/span&gt;
&lt;span class="na"&gt;info&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;orders-gateway&lt;/span&gt;
  &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1.0"&lt;/span&gt;
&lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="s"&gt;/v1/orders/{id}&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;parameters&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;id&lt;/span&gt;
          &lt;span class="na"&gt;in&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;path&lt;/span&gt;
          &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
          &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;string&lt;/span&gt;
      &lt;span class="na"&gt;x-amazon-apigateway-integration&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;http_proxy&lt;/span&gt;
        &lt;span class="na"&gt;httpMethod&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;GET&lt;/span&gt;
        &lt;span class="na"&gt;uri&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;http://${stageVariables.ordersHost}/orders/{id}&lt;/span&gt;
        &lt;span class="na"&gt;requestParameters&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;integration.request.path.id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;method.request.path.id&lt;/span&gt;
        &lt;span class="na"&gt;passthroughBehavior&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;when_no_match&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The 90/10 split comes from a canary release. First deploy to the stage with the production host in a stage variable, then deploy again with canary settings that override that variable for 10 percent of traffic:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 1. Deploy v1 to the stage, with the host in a stage variable&lt;/span&gt;
aws apigateway create-deployment &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--rest-api-id&lt;/span&gt; abcd1234 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--stage-name&lt;/span&gt; prod &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--variables&lt;/span&gt; &lt;span class="nv"&gt;ordersHost&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;orders-v1.internal.example.com

&lt;span class="c"&gt;# 2. Deploy again as a canary that overrides the variable for 10% of requests&lt;/span&gt;
aws apigateway create-deployment &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--rest-api-id&lt;/span&gt; abcd1234 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--stage-name&lt;/span&gt; prod &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--canary-settings&lt;/span&gt; &lt;span class="nv"&gt;percentTraffic&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;10,stageVariableOverrides&lt;span class="o"&gt;={&lt;/span&gt;&lt;span class="nv"&gt;ordersHost&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;orders-v2.internal.example.com&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The important part&lt;/strong&gt; is that the split is percentage-based and applies to the whole stage. I didn't find a native way to force a header such as &lt;code&gt;X-Canary: true&lt;/code&gt; to v2 in the documentation I reviewed, so a header override needs a separate stage or a layer in front.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where it falls short:&lt;/strong&gt; A canary deployment covers the whole API stage, so you can't canary a single route. The documented canary workflow is for REST APIs, so check the current feature matrix if you plan to use the lighter HTTP API type. Routing rules also live partly in OpenAPI extensions and partly in stage settings, which makes them harder to review in one place.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;My verdict:&lt;/strong&gt; Amazon API Gateway is my default for AWS-native teams that want a managed &lt;a href="https://dev.to/devstackcommunity/i-compared-5-llm-gateway-tools-for-real-world-production-use-4n5p"&gt;gateway&lt;/a&gt; and simple percentage-based canaries.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Apigee
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkq5ex8f0mmo0webb1ipu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkq5ex8f0mmo0webb1ipu.png" alt=" " width="800" height="384"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://cloud.google.com/apigee" rel="noopener noreferrer"&gt;Apigee&lt;/a&gt; is Google Cloud's API management platform. You build an API proxy from a bundle of XML: a &lt;code&gt;ProxyEndpoint&lt;/code&gt; that receives requests, a &lt;code&gt;TargetEndpoint&lt;/code&gt; that calls your backend, and policies in between. Its load balancing documentation covers target servers and the available algorithms.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What makes it useful&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;RouteRules&lt;/code&gt; with conditions let one proxy send requests to different &lt;code&gt;TargetEndpoints&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Target servers separate backend hosts from proxy config so that you can change them per environment.&lt;/li&gt;
&lt;li&gt;A built-in load balancer with RoundRobin, Weighted, and LeastConnections algorithms.&lt;/li&gt;
&lt;li&gt;A large policy library for transformation, security, and traffic management.&lt;/li&gt;
&lt;li&gt;Managed and hybrid deployment options.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This proxy covers the whole routing problem: the &lt;code&gt;BasePath&lt;/code&gt; handles &lt;code&gt;/v1/orders&lt;/code&gt;, a header-based &lt;code&gt;RouteRule&lt;/code&gt; overrides to v2, and a weighted load balancer splits everything else.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;ProxyEndpoint&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"default"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;HTTPProxyConnection&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;BasePath&amp;gt;&lt;/span&gt;/v1/orders&lt;span class="nt"&gt;&amp;lt;/BasePath&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/HTTPProxyConnection&amp;gt;&lt;/span&gt;

  &lt;span class="c"&gt;&amp;lt;!-- Header override: always v2 --&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;RouteRule&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"canary-header"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;Condition&amp;gt;&lt;/span&gt;request.header.x-canary = "true"&lt;span class="nt"&gt;&amp;lt;/Condition&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;TargetEndpoint&amp;gt;&lt;/span&gt;orders-v2&lt;span class="nt"&gt;&amp;lt;/TargetEndpoint&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/RouteRule&amp;gt;&lt;/span&gt;

  &lt;span class="c"&gt;&amp;lt;!-- Default rule goes last --&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;RouteRule&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"default"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;TargetEndpoint&amp;gt;&lt;/span&gt;orders-split&lt;span class="nt"&gt;&amp;lt;/TargetEndpoint&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/RouteRule&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/ProxyEndpoint&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;TargetEndpoint&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"orders-split"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;HTTPTargetConnection&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;LoadBalancer&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;Algorithm&amp;gt;&lt;/span&gt;Weighted&lt;span class="nt"&gt;&amp;lt;/Algorithm&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;Server&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"orders-v1"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;Weight&amp;gt;&lt;/span&gt;9&lt;span class="nt"&gt;&amp;lt;/Weight&amp;gt;&amp;lt;/Server&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;Server&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"orders-v2"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;Weight&amp;gt;&lt;/span&gt;1&lt;span class="nt"&gt;&amp;lt;/Weight&amp;gt;&amp;lt;/Server&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/LoadBalancer&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;Path&amp;gt;&lt;/span&gt;/orders&lt;span class="nt"&gt;&amp;lt;/Path&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/HTTPTargetConnection&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/TargetEndpoint&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;TargetEndpoint&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"orders-v2"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;HTTPTargetConnection&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;LoadBalancer&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;Server&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"orders-v2"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/LoadBalancer&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;Path&amp;gt;&lt;/span&gt;/orders&lt;span class="nt"&gt;&amp;lt;/Path&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/HTTPTargetConnection&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/TargetEndpoint&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The request path after the &lt;code&gt;BasePath&lt;/code&gt; is appended to the target &lt;code&gt;Path&lt;/code&gt;, so &lt;code&gt;/v1/orders/42&lt;/code&gt; reaches the backend as &lt;code&gt;/orders/42&lt;/code&gt;. The &lt;code&gt;orders-v1&lt;/code&gt; and &lt;code&gt;orders-v2&lt;/code&gt; names refer to target servers that you define in the environment.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The important part&lt;/strong&gt; is that every server in one load balancer shares the same &lt;code&gt;Path&lt;/code&gt;. If v2 lived at a different path, a weighted balancer can't express that, and you would need conditional routes or a separate proxy in front.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where it falls short:&lt;/strong&gt; The XML bundle model is verbose, and routing logic spreads across proxy endpoints, target endpoints, and target servers. Enterprise-grade pricing and platform breadth make it heavy for a small team that only needs routing. Check the current pricing model before you commit.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;My verdict:&lt;/strong&gt; Apigee is the strongest option when routing is one part of a larger API governance program, and a poor fit when it is the whole job.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Azure API Management
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fq8fg6e9ewapt5hrd5g0y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fq8fg6e9ewapt5hrd5g0y.png" alt=" " width="800" height="385"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://azure.microsoft.com/products/api-management" rel="noopener noreferrer"&gt;Azure API Management&lt;/a&gt;, or APIM, is Microsoft's managed gateway. Its routing logic lives in policies, XML snippets that run in the inbound, backend, outbound, and on-error sections of a request. Policies can be set at global, product, API, or operation scope. See the &lt;code&gt;set-backend-service&lt;/code&gt; policy reference.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What makes it useful&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Policy expressions use C# syntax so that conditions can inspect headers, query strings, and variables.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;set-backend-service&lt;/code&gt; switches the backend by base URL or by a named backend entity.&lt;/li&gt;
&lt;li&gt;Backend entities can form a load-balanced pool with circuit breaker rules.&lt;/li&gt;
&lt;li&gt;Gateway options include classic, v2, consumption, and self-hosted gateways for hybrid setups.&lt;/li&gt;
&lt;li&gt;You can configure this through the portal, Bicep, ARM, and the management API.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here is an operation-level policy for the running problem. It rewrites the path, then picks a backend with a header override or a 10 percent random split.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;policies&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;inbound&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;base&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;rewrite-uri&lt;/span&gt; &lt;span class="na"&gt;template=&lt;/span&gt;&lt;span class="s"&gt;"/orders/{id}"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;choose&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;when&lt;/span&gt; &lt;span class="na"&gt;condition=&lt;/span&gt;&lt;span class="s"&gt;"@(context.Request.Headers.GetValueOrDefault(&amp;amp;quot;x-canary&amp;amp;quot;, &amp;amp;quot;&amp;amp;quot;) == &amp;amp;quot;true&amp;amp;quot; || new System.Random().Next(1, 101) &amp;gt; 90)"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;set-backend-service&lt;/span&gt; &lt;span class="na"&gt;base-url=&lt;/span&gt;&lt;span class="s"&gt;"https://orders-v2.internal.example.com"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/when&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;otherwise&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;set-backend-service&lt;/span&gt; &lt;span class="na"&gt;base-url=&lt;/span&gt;&lt;span class="s"&gt;"https://orders-v1.internal.example.com"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/otherwise&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/choose&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/inbound&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;backend&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;base&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/backend&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;outbound&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;base&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/outbound&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;on-error&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;base&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/on-error&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/policies&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The operation's URL template would be &lt;code&gt;/v1/orders/{id}&lt;/code&gt;. Published examples of this pattern note that APIM has no dedicated traffic-splitting policy, so you build it from a random expression, and each request is decided independently.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The important part&lt;/strong&gt; is to keep the split values in named values, not hard-coded in the expression, so you can change the percentage without editing policy code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where it falls short:&lt;/strong&gt; XML plus C# expressions is hard to unit test, and a quoting mistake is a runtime error. The random split is not sticky, so the same user can bounce between versions. Tier choice affects features and cost, so check the tier comparison before you design around a capability.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;My verdict:&lt;/strong&gt; Azure API Management is the natural choice for Azure-centric teams, and its policy expressions make header-based routing easy. Plan to build and test the traffic split yourself.&lt;/p&gt;

&lt;h2&gt;
  
  
  Category 2: Self-Hosted Gateways
&lt;/h2&gt;

&lt;p&gt;These run on your infrastructure, in containers or on Kubernetes. You get portable, declarative config and full control, and you take on the operations and the licensing questions.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Kong Gateway
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fe4cqhuml3v5nj75v9gy1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fe4cqhuml3v5nj75v9gy1.png" alt=" " width="800" height="382"&gt;&lt;/a&gt;&lt;br&gt;
Kong Gateway models routing with services, routes, upstreams, and plugins. Routes match requests by path (including regex), method, host, and headers, and upstreams hold weighted targets. Its documentation covers the entities and the declarative config format.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A licensing note before you adopt it.&lt;/strong&gt; Starting with Gateway 3.10, released in March 2025, Kong stopped publishing prebuilt open-source Docker images and removed free mode, so running 3.10 or later without a license behaves like an expired Enterprise license. The last open-source line is 3.9, which Kong's community discussions describe as frozen. Confirm Kong's current licensing and downloads before you choose it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What makes it useful&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Precise route matching on paths, regex, methods, hosts, and headers.&lt;/li&gt;
&lt;li&gt;Upstreams with weighted targets and health checks, so a 90/10 split is a config value.&lt;/li&gt;
&lt;li&gt;A large plugin ecosystem for authentication, rate limiting, and transformation.&lt;/li&gt;
&lt;li&gt;Declarative config you can validate and sync from CI with decK, or run without a database.&lt;/li&gt;
&lt;li&gt;Kubernetes support through its ingress controller.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This declarative file covers the running problem. The main route sends traffic to a weighted upstream, and a second, more specific route sends the canary header straight to v2.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;_format_version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3.0"&lt;/span&gt;

&lt;span class="na"&gt;upstreams&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;orders-upstream&lt;/span&gt;
    &lt;span class="na"&gt;targets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;orders-v1.internal:8080&lt;/span&gt;
        &lt;span class="na"&gt;weight&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;90&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;orders-v2.internal:8080&lt;/span&gt;
        &lt;span class="na"&gt;weight&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;

&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;orders&lt;/span&gt;
    &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;orders-upstream&lt;/span&gt;
    &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/orders&lt;/span&gt;
    &lt;span class="na"&gt;routes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;orders-route&lt;/span&gt;
        &lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/v1/orders&lt;/span&gt;
        &lt;span class="na"&gt;methods&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;GET&lt;/span&gt;
        &lt;span class="na"&gt;strip_path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;orders-v2&lt;/span&gt;
    &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;orders-v2.internal&lt;/span&gt;
    &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8080&lt;/span&gt;
    &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/orders&lt;/span&gt;
    &lt;span class="na"&gt;routes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;orders-canary-header&lt;/span&gt;
        &lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/v1/orders&lt;/span&gt;
        &lt;span class="na"&gt;methods&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;GET&lt;/span&gt;
        &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;x-canary&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;true"&lt;/span&gt;
        &lt;span class="na"&gt;strip_path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Preview, then apply (older decK versions use `deck diff` and `deck sync`)&lt;/span&gt;
deck gateway diff kong.yaml
deck gateway &lt;span class="nb"&gt;sync &lt;/span&gt;kong.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The important part&lt;/strong&gt; is how the rewrite works. With &lt;code&gt;strip_path&lt;/code&gt; set to &lt;code&gt;true&lt;/code&gt;, Kong removes the matched route path, &lt;code&gt;/v1/orders&lt;/code&gt;, and prepends the service path, &lt;code&gt;/orders&lt;/code&gt;, so &lt;code&gt;/v1/orders/42&lt;/code&gt; reaches the backend as &lt;code&gt;/orders/42&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where it falls short:&lt;/strong&gt; The licensing change is the biggest risk, because the free path now ends at an older release and current builds need a license. Route precedence between a plain route and a header route is easy to get wrong, so confirm the winner with a test request. Plugin availability also varies by tier, so check that the plugins you need are in the plan you choose.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;My verdict:&lt;/strong&gt; Kong has the most expressive routing in this list. Weigh the licensing terms first, and pick it when you will use that expressiveness.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. KrakenD
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fv7bst0utcmcstj2einxb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fv7bst0utcmcstj2einxb.png" alt=" " width="800" height="365"&gt;&lt;/a&gt;&lt;br&gt;
KrakenD is a stateless gateway driven by one JSON file, with no database. You declare endpoints, and each endpoint lists the backends it calls. Its backend documentation explains how &lt;code&gt;host&lt;/code&gt;, &lt;code&gt;url_pattern&lt;/code&gt;, and aggregation work. The Community Edition is open source under Apache 2.0, and an Enterprise Edition adds features.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What makes it useful&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Stateless and database-free, so deployment is a container and a config file.&lt;/li&gt;
&lt;li&gt;An endpoint can call several backends in parallel and merge the results, which suits backend-for-frontend designs.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;url_pattern&lt;/code&gt; on each backend rewrites the path, using variables from the endpoint.&lt;/li&gt;
&lt;li&gt;Egress load balancing across a host list, with DNS SRV records for weighted balancing.&lt;/li&gt;
&lt;li&gt;Enterprise adds API key authentication and conditional routing, and the configuration is compatible with the Community Edition.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here is the Community Edition config for the running problem. KrakenD's documentation describes a simple way to weight a host list: repeat the host. Nine entries for v1 and one for v2 give a 90/10 split.&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;"$schema"&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.krakend.io/schema/krakend.json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"endpoints"&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;"endpoint"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/v1/orders/{id}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"method"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"GET"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"output_encoding"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"no-op"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"backend"&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;"encoding"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"no-op"&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_pattern"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/orders/{id}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"host"&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;"http://orders-v1:8080"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"http://orders-v1:8080"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"http://orders-v1:8080"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="s2"&gt;"http://orders-v1:8080"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"http://orders-v1:8080"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"http://orders-v1:8080"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="s2"&gt;"http://orders-v1:8080"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"http://orders-v1:8080"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"http://orders-v1:8080"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="s2"&gt;"http://orders-v2:8080"&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;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;The header override needs Enterprise conditional routing. In that edition, you split v1 and v2 into separate backends and let a header rule choose between them, with a fallback for everything else:&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="nl"&gt;"backend"&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;"url_pattern"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/orders/{id}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"host"&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;"http://orders-v2:8080"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"extra_config"&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;"backend/conditional"&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;"strategy"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"header"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"X-Canary"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&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="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;"url_pattern"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/orders/{id}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"host"&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;"http://orders-v1:8080"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"extra_config"&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;"backend/conditional"&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;"strategy"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"fallback"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The important part&lt;/strong&gt; is KrakenD's design rule that one endpoint can't have several write backends, which keeps the gateway from acting as a transaction coordinator.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where it falls short:&lt;/strong&gt; Weighted splitting in the Community Edition is a workaround, not a feature, and header-based routing and API keys are Enterprise-only. By default, &lt;code&gt;url_pattern&lt;/code&gt; must be RESTful, which you can relax with &lt;code&gt;disable_rest&lt;/code&gt;. KrakenD has also announced that it is dropping plugin support in the open-source edition, so check the current status if you depend on custom plugins.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;My verdict:&lt;/strong&gt; KrakenD is my default for a simple self-hosted gateway, especially when you want aggregation and a config file you can review in one place. It is the weakest option here for fine-grained traffic splitting.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Tyk
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbjlp8clb4x0yi9liv4td.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbjlp8clb4x0yi9liv4td.png" alt=" " width="799" height="376"&gt;&lt;/a&gt;&lt;br&gt;
Tyk is a gateway whose routing lives in API definitions. Each definition sets a &lt;code&gt;listen_path&lt;/code&gt;, a &lt;code&gt;target_url&lt;/code&gt;, and options such as &lt;code&gt;strip_listen_path&lt;/code&gt; and load balancing. The gateway is open source, and the Dashboard and other management features are offered commercially. Its load balancing documentation covers target lists and weighting.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What makes it useful&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Simple API definitions, expressed as JSON or as Kubernetes resources through Tyk Operator.&lt;/li&gt;
&lt;li&gt;Native round-robin load balancing with a &lt;code&gt;target_list&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Weighting in the Dashboard so that you can send, for example, 10 percent of traffic to a beta backend.&lt;/li&gt;
&lt;li&gt;Service discovery integrations, such as Consul and etcd, for dynamic target lists.&lt;/li&gt;
&lt;li&gt;Rewrite rules that can act on request details, plus gRPC load balancing.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here is a Tyk Operator definition for the running problem. Without weights, the two targets share traffic evenly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;tyk.tyk.io/v1alpha1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ApiDefinition&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;orders&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;orders&lt;/span&gt;
  &lt;span class="na"&gt;use_keyless&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="na"&gt;protocol&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;http&lt;/span&gt;
  &lt;span class="na"&gt;active&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="na"&gt;proxy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;listen_path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/v1/orders&lt;/span&gt;
    &lt;span class="na"&gt;strip_listen_path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="na"&gt;target_url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;http://orders-v1.internal:8080/orders&lt;/span&gt;
    &lt;span class="na"&gt;enable_load_balancing&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="na"&gt;target_list&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;http://orders-v1.internal:8080/orders&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;http://orders-v2.internal:8080/orders&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The rewrite follows the documented rule: Tyk strips the listen path and appends what remains to the target URL, so &lt;code&gt;/v1/orders/42&lt;/code&gt; reaches the backend as &lt;code&gt;/orders/42&lt;/code&gt;. For the 90/10 ratio, set weights of 9 and 1 in the Dashboard's load balancing settings. A header override can use a URL rewrite rule that matches the header, and the exact syntax depends on your Tyk version, so check its rewrite documentation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The important part&lt;/strong&gt; is how Tyk treats versions. Its documentation recommends a separate, non-versioned API definition for each URL-based version (e.g., &lt;code&gt;/v1&lt;/code&gt; and &lt;code&gt;/v2&lt;/code&gt;), because versions in an API definition assume the same URL structure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where it falls short:&lt;/strong&gt; Weighted routing is documented through the Dashboard, so confirm how you will express weights in your deployment model, whether files, the Operator, or the Dashboard. Without &lt;code&gt;strip_listen_path&lt;/code&gt;, proxied requests are literal, so the listen path leaks into the upstream URL. Check which management features your use case needs a commercial license for.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;My verdict:&lt;/strong&gt; Tyk is a solid choice if you want an &lt;a href="https://dev.to/devstackhub/i-compared-the-5-best-open-source-llm-gateways-for-enterprise-ai-2mln"&gt;open-source gateway&lt;/a&gt; with a Kubernetes-friendly definition format and the option of a dashboard.&lt;/p&gt;

&lt;h2&gt;
  
  
  Kong, KrakenD, or Tyk?
&lt;/h2&gt;

&lt;p&gt;Kong has the richest route matching and plugin ecosystem, but it carries the licensing risk described above. KrakenD is the simplest to run and the best at aggregation, but it has the weakest traffic splitting in its Community Edition. Tyk sits between them, with an open-source gateway, simple definitions, and weighting that lives in its Dashboard. Test each on the same routing problem, using the harness below, before you decide.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pricing, licensing, and lock-in
&lt;/h2&gt;

&lt;p&gt;Prices change too often to list, but the models differ in ways that matter:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Cloud-managed gateways&lt;/strong&gt; charge by usage, tier, or subscription. Amazon API Gateway bills per request, Azure API Management sells tiers with different features, and Apigee uses enterprise-style pricing, so model your expected traffic before you compare.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Kong&lt;/strong&gt; changed its terms in 2025: the free path ends at the 3.9 open-source line, and later releases need a license.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;KrakenD&lt;/strong&gt; has a free Community Edition under Apache 2.0 and a commercial Enterprise Edition that is quoted on request.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tyk&lt;/strong&gt; offers an open-source gateway, with commercial management features such as the Dashboard.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Lock-in lives in your rules. XML policies and C# expressions don't port between vendors, while declarative route definitions are easier to translate. Keep business logic out of the gateway, so switching is a routing migration, not a rewrite.&lt;/p&gt;

&lt;p&gt;In general, the more routing logic you put into vendor-specific policy, the more you should check export options and exit costs before you commit.&lt;/p&gt;

&lt;h2&gt;
  
  
  Test your routing logic before production
&lt;/h2&gt;

&lt;p&gt;Routing rules fail quietly, so test them like code. The setup below works against any gateway in this guide. First, a tiny upstream that reports its path and version, so you can see exactly what the gateway sent.&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="c1"&gt;# echo.py  (pip install flask)
&lt;/span&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;flask&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Flask&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;jsonify&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Flask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;VERSION&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="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;VERSION&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;v1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nd"&gt;@app.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;/orders/&amp;lt;order_id&amp;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;def&lt;/span&gt; &lt;span class="nf"&gt;get_order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order_id&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="nf"&gt;jsonify&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="n"&gt;order_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;path&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;version&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;VERSION&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="n"&gt;headers&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-Served-By&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="n"&gt;VERSION&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;0.0.0.0&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;8080&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run one copy as v1 and another as v2, point the gateway at them, then assert the three requirements from the running problem:&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;collections&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;

&lt;span class="n"&gt;GATEWAY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://localhost:8000&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_path_is_rewritten&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;r&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;GATEWAY&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/v1/orders/42&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;
    &lt;span class="k"&gt;assert&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;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;path&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;/orders/42&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_header_forces_v2&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;_&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&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="n"&gt;r&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;GATEWAY&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/v1/orders/42&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-Canary&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;true&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;headers&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-Served-By&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;v2&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_split_is_close_to_90_10&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;counts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;collections&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Counter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2000&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;r&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;GATEWAY&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/v1/orders/42&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;counts&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="n"&gt;headers&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-Served-By&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="mi"&gt;1&lt;/span&gt;
    &lt;span class="n"&gt;v2_share&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;counts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;v2&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="n"&gt;n&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="mf"&gt;0.06&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;v2_share&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mf"&gt;0.14&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;counts&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The tolerance on the split matters. With 2,000 requests, a true 10 percent split will vary by well under a percentage point, so a band of 6 to 14 percent catches a wrong ratio without flaking. Run these in CI against a staging gateway whenever routing config changes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common routing bugs
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Symptom&lt;/th&gt;
&lt;th&gt;Likely cause&lt;/th&gt;
&lt;th&gt;What to check&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;404 only when the URL ends in a slash&lt;/td&gt;
&lt;td&gt;Trailing-slash handling differs between gateways&lt;/td&gt;
&lt;td&gt;Test the path with and without a trailing slash&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;The backend receives &lt;code&gt;/v1/orders/42&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;The route prefix wasn't stripped&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;strip_path&lt;/code&gt;, &lt;code&gt;strip_listen_path&lt;/code&gt;, or a rewrite rule&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;The backend receives &lt;code&gt;/orders/orders/42&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;A stripped prefix plus a service path that repeats the segment&lt;/td&gt;
&lt;td&gt;Compare the route path with the service or target path&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;The canary header is ignored&lt;/td&gt;
&lt;td&gt;Route precedence, or the rule never matches&lt;/td&gt;
&lt;td&gt;Send the header through a test and confirm which route wins&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;The split is off at low volume&lt;/td&gt;
&lt;td&gt;Random selection needs a large sample&lt;/td&gt;
&lt;td&gt;Measure with at least 1,000 requests&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Users bounce between versions&lt;/td&gt;
&lt;td&gt;The split is random per request, not sticky&lt;/td&gt;
&lt;td&gt;Route by header, cookie, or a hash of the user ID&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Duplicate writes appear&lt;/td&gt;
&lt;td&gt;The gateway retried a non-idempotent call&lt;/td&gt;
&lt;td&gt;Limit retries to idempotent methods&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Roll out a canary with a rollback
&lt;/h2&gt;

&lt;p&gt;A percentage split is only safe if something watches it. This sketch shows the loop: raise the weight in steps, compare the error rate with the baseline, and roll back when it worsens. Wire &lt;code&gt;set_weight&lt;/code&gt; and &lt;code&gt;error_rate&lt;/code&gt; to your gateway's API and your metrics.&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;STEPS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;50&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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;promote&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;set_weight&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;error_rate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;baseline&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tolerance&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.005&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;percent&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;STEPS&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;set_weight&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;percent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="c1"&gt;# Let traffic soak here, then compare against the baseline
&lt;/span&gt;        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;error_rate&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;baseline&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;tolerance&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nf"&gt;set_weight&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="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;RuntimeError&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;Rolled back at &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;percent&lt;/span&gt;&lt;span class="si"&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also set explicit upstream timeouts, add health checks or circuit breakers where the gateway supports them, and tag every response with the version that served it so that you can compare error rates and latency per version.&lt;/p&gt;

&lt;h2&gt;
  
  
  What gateways can't replace
&lt;/h2&gt;

&lt;p&gt;A gateway centralizes routing, but it doesn't remove the need for good design.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Business logic belongs in services:&lt;/strong&gt; Complex orchestration in gateway policy is hard to test, version, and debug. Keep the gateway to routing, security, and cross-cutting rules.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Authentication isn't authorization:&lt;/strong&gt; A gateway can verify a token, but your services still decide what each caller may do.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Config drift is a real risk:&lt;/strong&gt; Rules edited in a console and not in version control will diverge from what you reviewed. Treat gateway config as code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Observability is your job:&lt;/strong&gt; Without per-route metrics, logs, and traces, a bad rule looks like a flaky backend.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Gateways work best when they handle routing and policy, and you stay responsible for testing the rules, watching the traffic, and keeping the logic simple.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final verdict
&lt;/h2&gt;

&lt;p&gt;No single gateway suits every team. If you're on AWS, start with Amazon API Gateway for managed canaries. If you're on Azure, use Azure API Management and plan to build your own traffic split. If you run a large API program with governance needs, consider Apigee. If you want a simple self-hosted gateway, use KrakenD. If you need the richest route matching and can accept its licensing terms, use Kong. If you want an open-source gateway with an optional dashboard, consider Tyk.&lt;/p&gt;

&lt;p&gt;A gateway shortens the path between a client request and the right service, but the first working route is only the beginning. Choose the gateway whose rules you can read, test in CI, and change safely when traffic and teams grow.&lt;/p&gt;

&lt;h2&gt;
  
  
  Frequently asked questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What is the best API gateway for microservices?
&lt;/h3&gt;

&lt;p&gt;It depends on where you run. Amazon API Gateway suits AWS, Azure API Management suits Azure, Apigee suits large governed API programs, and Kong, KrakenD, and Tyk suit self-hosted setups on Kubernetes or containers.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is the difference between an API gateway, a reverse proxy, and an ingress controller?
&lt;/h3&gt;

&lt;p&gt;A reverse proxy forwards requests to backends. An ingress controller routes external traffic into a Kubernetes cluster. An API gateway adds API-level features, such as authentication, rate limiting, transformation, and developer management. The lines blur, and several products play multiple roles.&lt;/p&gt;

&lt;h3&gt;
  
  
  How do I run a canary release at the gateway?
&lt;/h3&gt;

&lt;p&gt;Send a small percentage of traffic to the new version and watch error rates and latency before raising it. Amazon API Gateway has canary settings on REST API stages, Kong uses weighted upstream targets, Apigee uses a weighted load balancer, and Azure API Management needs a policy expression. Use a header or cookie rule if users must stay on one version.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is Kong still open source?
&lt;/h3&gt;

&lt;p&gt;Kong's source repository is open, but the open-source distribution stopped at the 3.9 line. Starting with 3.10, Kong stopped publishing prebuilt open-source images and removed free mode, so current builds need a license. Check Kong's current terms before you adopt it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Should I put business logic in my API gateway?
&lt;/h3&gt;

&lt;p&gt;Generally no. Keep the gateway to routing, authentication, rate limiting, and transformation. Business rules in gateway policy are hard to test and tie your system to one vendor.&lt;/p&gt;

</description>
      <category>api</category>
      <category>devops</category>
      <category>backend</category>
      <category>discuss</category>
    </item>
    <item>
      <title>Struggling with 403, 429, or CAPTCHA errors while web scraping? Learn how to troubleshoot blocks, use proxies effectively, and make your scraping workflows more reliable. 🚀

Read the guide 👇</title>
      <dc:creator>Dev Stack Hub</dc:creator>
      <pubDate>Wed, 28 Oct 2026 17:08:56 +0000</pubDate>
      <link>https://dev.to/devstackhub/struggling-with-403-429-or-captcha-errors-while-web-scraping-learn-how-to-troubleshoot-blocks-2a7</link>
      <guid>https://dev.to/devstackhub/struggling-with-403-429-or-captcha-errors-while-web-scraping-learn-how-to-troubleshoot-blocks-2a7</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/devstackhub/debugging-403-429-and-captcha-errors-with-proxies-31i8" class="crayons-story__hidden-navigation-link"&gt;Debugging 403, 429 and CAPTCHA Errors with Proxies&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/devstackhub" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4081963%2F9d10c5fa-c76e-4c9d-88af-e3f2fced5a62.jpg" alt="devstackhub profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/devstackhub" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Dev Stack Hub
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Dev Stack Hub
                
                
              
              &lt;div id="story-author-preview-content-4936019" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/devstackhub" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4081963%2F9d10c5fa-c76e-4c9d-88af-e3f2fced5a62.jpg" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Dev Stack Hub&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/devstackhub/debugging-403-429-and-captcha-errors-with-proxies-31i8" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Oct 28&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/devstackhub/debugging-403-429-and-captcha-errors-with-proxies-31i8" id="article-link-4936019"&gt;
          Debugging 403, 429 and CAPTCHA Errors with Proxies
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag crayons-tag--filled  " href="/t/discuss"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;discuss&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/python"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;python&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/webscraping"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;webscraping&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/debugging"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;debugging&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/devstackhub/debugging-403-429-and-captcha-errors-with-proxies-31i8" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/fire-f60e7a582391810302117f987b22a8ef04a2fe0df7e3258a5f49332df1cec71e.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/raised-hands-74b2099fd66a39f2d7eed9305ee0f4553df0eb7b4f11b01b6b1b499973048fe5.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;34&lt;span class="hidden s:inline"&gt;&amp;nbsp;reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/devstackhub/debugging-403-429-and-captcha-errors-with-proxies-31i8#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              19&lt;span class="hidden s:inline"&gt;&amp;nbsp;comments&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            8 min read
          &lt;/small&gt;
          
          &lt;div class="only-sidebar-menu-item trusted-visible-block print-hidden align-center"&gt;
                
                    
                
          &lt;/div&gt;

        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
      <category>automation</category>
      <category>debugging</category>
      <category>webscraping</category>
    </item>
    <item>
      <title>Debugging 403, 429 and CAPTCHA Errors with Proxies</title>
      <dc:creator>Dev Stack Hub</dc:creator>
      <pubDate>Wed, 28 Oct 2026 17:05:07 +0000</pubDate>
      <link>https://dev.to/devstackhub/debugging-403-429-and-captcha-errors-with-proxies-31i8</link>
      <guid>https://dev.to/devstackhub/debugging-403-429-and-captcha-errors-with-proxies-31i8</guid>
      <description>&lt;p&gt;Your scraper worked fine on 50 requests. At 500, it started returning &lt;code&gt;403 Forbidden&lt;/code&gt;. At 5,000, you're staring at &lt;code&gt;429 Too Many Requests&lt;/code&gt; and CAPTCHA pages with a &lt;code&gt;200&lt;/code&gt; status code. Adding more proxies didn't help, or helped for ten minutes.&lt;/p&gt;

&lt;p&gt;The mistake most developers make here is changing several things at once: new proxies, new headers, more retries. This guide gives you a repeatable way to find out &lt;strong&gt;why&lt;/strong&gt; you're being blocked so that you can fix the right thing.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we'll cover
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;What 403, 429, and CAPTCHA responses actually mean&lt;/li&gt;
&lt;li&gt;A step-by-step isolation process (and a small Python probe script)&lt;/li&gt;
&lt;li&gt;Detecting "soft blocks" that return HTTP 200&lt;/li&gt;
&lt;li&gt;Backoff, &lt;code&gt;Retry-After&lt;/code&gt;, and pacing done properly&lt;/li&gt;
&lt;li&gt;Tracking proxy health so bad IPs get benched automatically&lt;/li&gt;
&lt;li&gt;When the problem isn't the IP at all&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Examples use Python and &lt;code&gt;requests&lt;/code&gt;, with placeholder proxies from the documentation range (&lt;code&gt;203.0.113.x&lt;/code&gt;).&lt;/p&gt;

&lt;h2&gt;
  
  
  What each signal usually means
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Signal&lt;/th&gt;
&lt;th&gt;What the server is telling you&lt;/th&gt;
&lt;th&gt;Typical root cause&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;429 Too Many Requests&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;You exceeded a rate limit&lt;/td&gt;
&lt;td&gt;Too many requests per IP, per session or per account in a time window&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;403 Forbidden&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The server understood you and refuses&lt;/td&gt;
&lt;td&gt;IP reputation, bot detection, missing or inconsistent headers, geo restriction&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;200 with a CAPTCHA page&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;A challenge is served instead of content&lt;/td&gt;
&lt;td&gt;Risk score too high: IP, behavior or fingerprint looks automated&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;407 Proxy Authentication Required&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The proxy rejected you, not the target&lt;/td&gt;
&lt;td&gt;Wrong credentials or non-whitelisted IP&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Note that 407 is not a block at all. It's your proxy configuration, and it's the first thing to rule out.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Isolate where the block happens
&lt;/h2&gt;

&lt;p&gt;Before touching your scraper, test the target with the simplest possible client. If &lt;code&gt;curl&lt;/code&gt; through the proxy works but your script fails, the problem is in your code. If both fail, it's the IP or the target.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Direct (no proxy)&lt;/span&gt;
curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; /dev/null &lt;span class="nt"&gt;-w&lt;/span&gt; &lt;span class="s2"&gt;"%{http_code}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; https://example.com/products

&lt;span class="c"&gt;# Through the proxy&lt;/span&gt;
curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; /dev/null &lt;span class="nt"&gt;-w&lt;/span&gt; &lt;span class="s2"&gt;"%{http_code}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-x&lt;/span&gt; http://user:pass@203.0.113.10:8080 https://example.com/products
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then reproduce the comparison in Python with a small probe that varies one thing at a time:&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;TARGET&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://example.com/products&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
&lt;span class="n"&gt;PROXY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;http://user:pass@203.0.113.10:8080&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
&lt;span class="n"&gt;PROXIES&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;http&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;PROXY&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&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;PROXY&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;BROWSER_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;User-Agent&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;Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
       &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;(KHTML, like Gecko) Chrome/124.0 Safari/537.36&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;Accept&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/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8&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;Accept-Language&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;en-US,en;q=0.9&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;def&lt;/span&gt; &lt;span class="nf"&gt;probe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
   &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
       &lt;span class="n"&gt;r&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;TARGET&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&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="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&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="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="mi"&gt;28&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;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&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="nf"&gt;len&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="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; bytes&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RequestException&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;exc&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="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="mi"&gt;28&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; ERROR &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;exc&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;__name__&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="nf"&gt;probe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;direct, default headers&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;probe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;direct, browser headers&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;BROWSER_HEADERS&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;probe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;proxy, default headers&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;proxies&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;PROXIES&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;probe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;proxy, browser headers&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;proxies&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;PROXIES&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;BROWSER_HEADERS&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Read the results like a truth table:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Direct works, proxy fails:&lt;/strong&gt; the proxy IP is the problem (reputation, type, or geo).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Default headers fail, browser headers pass:&lt;/strong&gt; the site checks the User-Agent and friends. The default &lt;code&gt;python-requests/x.y&lt;/code&gt; agent is an easy signal to filter.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Everything fails:&lt;/strong&gt; the page may need JavaScript, a cookie, or a login. The proxy is not the issue.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Everything passes:&lt;/strong&gt; your problem is volume or pacing, so move on to Step 3.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Also confirm the proxy really is being used by calling an IP-echo endpoint such as &lt;code&gt;https://api.ipify.org?format=json&lt;/code&gt; through it. A surprising number of "proxy problems" are requests silently going out directly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Detect soft blocks
&lt;/h2&gt;

&lt;p&gt;A response code of 200 doesn't mean you got the page. Many sites serve a CAPTCHA or an "access denied" page with a success status, and your parser then silently stores garbage. Classify every response before you trust it:&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;BLOCK_MARKERS&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;captcha&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;unusual traffic&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;access denied&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;verify you are human&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;are you a robot&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;def&lt;/span&gt; &lt;span class="nf"&gt;classify&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="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;407&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
       &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;proxy_auth&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
   &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;429&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
       &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;rate_limited&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
   &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;401&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;403&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
       &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;blocked&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;

   &lt;span class="n"&gt;snippet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="mi"&gt;5000&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
   &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;any&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;marker&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;snippet&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;marker&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;BLOCK_MARKERS&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
       &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;challenge&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
   &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ok&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The marker list is site-specific, so tune it to the challenge pages you actually see. A cheaper complementary check is to assert that the content you expect (a product title, a JSON key) is present, and treat its absence as a failure.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Fix 429s with backoff and pacing
&lt;/h2&gt;

&lt;p&gt;A 429 is the friendliest error you'll get, because the server is telling you exactly what to do: slow down. Many servers include a &lt;code&gt;Retry-After&lt;/code&gt; header, either as several seconds or as an HTTP date. Honor it:&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;random&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timezone&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;email.utils&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;parsedate_to_datetime&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;retry_after_seconds&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="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
   &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;headers&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;Retry-After&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="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
       &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;default&lt;/span&gt;
   &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isdigit&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
       &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
       &lt;span class="n"&gt;when&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;parsedate_to_datetime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
       &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;max&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="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;when&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;timezone&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;utc&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;total_seconds&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
   &lt;span class="nf"&gt;except &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;TypeError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
       &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;default&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;polite_sleep&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="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;jitter&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;1.5&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
   &lt;span class="c1"&gt;# randomized delay between requests, so traffic isn't perfectly periodic
&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="n"&gt;base&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;uniform&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="n"&gt;jitter&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three principles prevent most 429s:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Limit concurrency per IP, not just overall.&lt;/strong&gt; Ten threads on one IP is a burst. Ten threads across ten IPs is not.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Add jitter.&lt;/strong&gt; A request every exactly 1.000 seconds looks robotic.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use exponential backoff on repeated failures.&lt;/strong&gt; If a 429 comes back after waiting, wait longer next time instead of retrying at the same speed.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you're still rate-limited at a sensible pace, the limit probably applies per IP, and the fix is a larger pool rather than a faster loop.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: Track proxy health and bench bad IPs
&lt;/h2&gt;

&lt;p&gt;Random rotation keeps sending traffic to IPs that are already burned. Instead, record outcomes and give failing proxies a cooldown:&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;random&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;collections&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;defaultdict&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ProxyPool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
   &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;proxies&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cooldown&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_failures&lt;/span&gt;&lt;span class="o"&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;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;proxies&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;proxies&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
       &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cooldown&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cooldown&lt;/span&gt;
       &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;max_failures&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;max_failures&lt;/span&gt;
       &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;failures&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;defaultdict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
       &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;benched_until&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

   &lt;span class="k"&gt;def&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;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
       &lt;span class="n"&gt;now&lt;/span&gt; &lt;span class="o"&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;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
       &lt;span class="n"&gt;available&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;proxies&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;benched_until&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;p&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;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
       &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;available&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
           &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;RuntimeError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;No healthy proxies: slow down or add more IPs&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
       &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;choice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;available&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

   &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;report&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;proxy&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;outcome&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;outcome&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ok&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
           &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;failures&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;proxy&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
           &lt;span class="k"&gt;return&lt;/span&gt;
       &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;failures&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;proxy&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
       &lt;span class="c1"&gt;# a 429 benches the IP immediately; other failures need a streak
&lt;/span&gt;       &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;outcome&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;rate_limited&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;failures&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;proxy&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;max_failures&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
           &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;benched_until&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;proxy&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&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;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cooldown&lt;/span&gt;
           &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;failures&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;proxy&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And a fetch function that ties the pieces together:&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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fetch&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;pool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;attempts&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;4&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;_&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;attempts&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
       &lt;span class="n"&gt;proxy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pool&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="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
           &lt;span class="n"&gt;r&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;proxies&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;http&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;proxy&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&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;proxy&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;BROWSER_HEADERS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
               &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&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="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
           &lt;span class="p"&gt;)&lt;/span&gt;
       &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RequestException&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
           &lt;span class="n"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;report&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;proxy&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;error&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
           &lt;span class="k"&gt;continue&lt;/span&gt;

       &lt;span class="n"&gt;outcome&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;classify&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="n"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;report&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;proxy&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;outcome&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;outcome&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ok&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
           &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;
       &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;outcome&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;rate_limited&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="nf"&gt;retry_after_seconds&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="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;outcome&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;proxy_auth&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
           &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;RuntimeError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Proxy credentials rejected (407). Check auth or IP whitelist.&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

       &lt;span class="nf"&gt;polite_sleep&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

   &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;RuntimeError&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;Failed to fetch &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; after &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;attempts&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; attempts&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;Log the outcome, proxy, and URL for every request. After a few hundred requests, you'll be able to see whether failures cluster on specific IPs (bad IPs), specific URLs (protected pages), or specific times (rate-limit windows). That pattern tells you which fix to apply.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5: When it's the IP type
&lt;/h2&gt;

&lt;p&gt;If your probe showed that browser-like headers pass directly but fail through the proxy, you're dealing with IP reputation. Different proxy types carry different levels of trust:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Proxy type&lt;/th&gt;
&lt;th&gt;Trust with strict sites&lt;/th&gt;
&lt;th&gt;Strengths&lt;/th&gt;
&lt;th&gt;Watch out for&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Datacenter / IPv6&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Lower&lt;/td&gt;
&lt;td&gt;Fast, cheap, easy to scale&lt;/td&gt;
&lt;td&gt;Whole ranges are often flagged; shared IPs inherit other users' behavior&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ISP (static residential)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Higher&lt;/td&gt;
&lt;td&gt;Looks like a home user, stable for long sessions&lt;/td&gt;
&lt;td&gt;Priced per IP, so pool size is limited by budget&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Residential (rotating)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;Huge pools, precise geo-targeting&lt;/td&gt;
&lt;td&gt;Usually billed per GB; can be slower&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Mobile (4G/5G)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Highest&lt;/td&gt;
&lt;td&gt;Carrier IPs are shared by many real users, so sites are cautious about blocking them&lt;/td&gt;
&lt;td&gt;Most expensive; speed varies&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A few related checks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Dedicated vs shared:&lt;/strong&gt; a shared IP can be burned by someone else before you send a single request.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Geo mismatch:&lt;/strong&gt; if your &lt;code&gt;Accept-Language&lt;/code&gt; says &lt;code&gt;en-US&lt;/code&gt; but your exit IP is in another country, or your IP is in Germany while you request a US-only price page, you will get blocked or served different content.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Burned static IPs:&lt;/strong&gt; a static IP that has been hammered for days may need a rest or a replacement. Rotation isn't just for volume; it's hygiene.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 6: When it isn't the IP at all
&lt;/h2&gt;

&lt;p&gt;If you've tried clean IPs and sensible pacing and still get 403s, look beyond the proxy:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Session and cookies:&lt;/strong&gt; Many sites set a cookie on the first page and expect it on the next. Use a &lt;code&gt;requests.Session&lt;/code&gt; and keep the same IP for the whole flow (a sticky session if your provider supports one).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Inconsistent headers:&lt;/strong&gt; A Chrome User-Agent with a missing &lt;code&gt;Accept-Language&lt;/code&gt;, or headers in an unusual order, is a signal. Keep the set realistic and consistent per session.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TLS fingerprinting:&lt;/strong&gt; Some bot-protection systems inspect the TLS handshake itself, and Python's default stack looks different from a real browser's. If headers and IPs are fine but a site still refuses you, a real browser (Playwright or Selenium) or an HTTP client that mimics browser TLS such as &lt;code&gt;curl_cffi&lt;/code&gt; may be the next thing to test.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;JavaScript-rendered content:&lt;/strong&gt; If the data is loaded by JavaScript, plain &lt;code&gt;requests&lt;/code&gt; get an empty shell. Look for the JSON API call the page makes in your browser's network tab, or use a headless browser.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Behavioral signals:&lt;/strong&gt; Hitting deep URLs with no prior navigation, or requesting pages at inhuman speed, can raise a risk score even from a perfect IP.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  A word on CAPTCHAs
&lt;/h2&gt;

&lt;p&gt;A CAPTCHA is not an error to defeat. It's a signal that your traffic looks risky. The most reliable strategy is to trigger fewer of them: better IP quality, lower rate, consistent sessions, realistic headers. If you're seeing them constantly, treat that as a symptom and work back through the steps above.&lt;/p&gt;

&lt;p&gt;It's also worth asking whether you should be scraping that page at all. Check for an official API or data export first, read the site's terms of service and &lt;code&gt;robots.txt&lt;/code&gt;, and only collect data you're permitted to use. Circumventing access controls on sites that forbid automated access can create legal problems that no proxy will solve.&lt;/p&gt;

&lt;h2&gt;
  
  
  Choosing proxies for testing
&lt;/h2&gt;

&lt;p&gt;To follow along, you need proxies that let you compare types on the same target. I used a provider that offers datacenter, ISP, residential, and mobile IPs with both HTTP/HTTPS and SOCKS5 so that I could swap types without changing code. &lt;em&gt;Disclosure: [add your affiliate or sponsorship note here if applicable].&lt;/em&gt; Whichever service you use, buy a small amount of each type you're considering, run the probe script from Step 1, and compare success rate and latency on your target before committing to a larger plan.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick reference
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;407?&lt;/strong&gt; Fix proxy auth first. It isn't a block.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Isolate:&lt;/strong&gt; compare direct vs proxy, default vs browser headers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Classify&lt;/strong&gt; every response, including 200 pages that are really CAPTCHAs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;429:&lt;/strong&gt; honor &lt;code&gt;Retry-After&lt;/code&gt;, add jitter, limit per-IP concurrency, then grow the pool.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bench&lt;/strong&gt; bad IPs with a cooldown instead of rotating unquestioningly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;403 on clean IPs:&lt;/strong&gt; check sessions, headers, TLS fingerprint, and JavaScript rendering.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Constant CAPTCHAs:&lt;/strong&gt; reduce the triggers, and check whether an official API exists.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Got a blocking pattern that isn't covered here? Share it in the comments, and I'll add it to the guide.&lt;/p&gt;

</description>
      <category>python</category>
      <category>webscraping</category>
      <category>debugging</category>
      <category>discuss</category>
    </item>
    <item>
      <title>Choosing an AI design-to-code tool isn't just about generating code. It's about finding the right fit for your workflow. I compared 11 tools for different use cases, from Figma-to-code to full-stack AI builders. Which tool are you using for your project?</title>
      <dc:creator>Dev Stack Hub</dc:creator>
      <pubDate>Tue, 27 Oct 2026 16:09:37 +0000</pubDate>
      <link>https://dev.to/devstackhub/choosing-an-ai-design-to-code-tool-isnt-just-about-generating-code-its-about-finding-the-right-1a29</link>
      <guid>https://dev.to/devstackhub/choosing-an-ai-design-to-code-tool-isnt-just-about-generating-code-its-about-finding-the-right-1a29</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/devstackhub/i-compared-11-best-ai-design-to-code-tools-for-my-client-projects-4alo" class="crayons-story__hidden-navigation-link"&gt;I Compared 11 Best AI Design-to-Code Tools for My Client Projects&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/devstackhub" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4081963%2F9d10c5fa-c76e-4c9d-88af-e3f2fced5a62.jpg" alt="devstackhub profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/devstackhub" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Dev Stack Hub
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Dev Stack Hub
                
                
              
              &lt;div id="story-author-preview-content-4923481" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/devstackhub" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4081963%2F9d10c5fa-c76e-4c9d-88af-e3f2fced5a62.jpg" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Dev Stack Hub&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/devstackhub/i-compared-11-best-ai-design-to-code-tools-for-my-client-projects-4alo" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Oct 26&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/devstackhub/i-compared-11-best-ai-design-to-code-tools-for-my-client-projects-4alo" id="article-link-4923481"&gt;
          I Compared 11 Best AI Design-to-Code Tools for My Client Projects
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag crayons-tag--filled  " href="/t/discuss"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;discuss&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/ai"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;ai&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/design"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;design&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/nocode"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;nocode&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/devstackhub/i-compared-11-best-ai-design-to-code-tools-for-my-client-projects-4alo" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/fire-f60e7a582391810302117f987b22a8ef04a2fe0df7e3258a5f49332df1cec71e.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/raised-hands-74b2099fd66a39f2d7eed9305ee0f4553df0eb7b4f11b01b6b1b499973048fe5.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;59&lt;span class="hidden s:inline"&gt;&amp;nbsp;reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/devstackhub/i-compared-11-best-ai-design-to-code-tools-for-my-client-projects-4alo#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              45&lt;span class="hidden s:inline"&gt;&amp;nbsp;comments&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            13 min read
          &lt;/small&gt;
          
          &lt;div class="only-sidebar-menu-item trusted-visible-block print-hidden align-center"&gt;
                
                    
                
          &lt;/div&gt;

        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
      <category>ai</category>
      <category>frontend</category>
      <category>productivity</category>
      <category>tools</category>
    </item>
    <item>
      <title>I Compared 11 Best AI Design-to-Code Tools for My Client Projects</title>
      <dc:creator>Dev Stack Hub</dc:creator>
      <pubDate>Mon, 26 Oct 2026 17:45:04 +0000</pubDate>
      <link>https://dev.to/devstackhub/i-compared-11-best-ai-design-to-code-tools-for-my-client-projects-4alo</link>
      <guid>https://dev.to/devstackhub/i-compared-11-best-ai-design-to-code-tools-for-my-client-projects-4alo</guid>
      <description>&lt;p&gt;From Figma integrations to AI coding agents, here is how to choose the right tool for turning an approved design into a working interface.&lt;/p&gt;

&lt;p&gt;Last reviewed: October 2026. This space changes monthly, so confirm features, supported frameworks, and pricing in each vendor's current documentation.&lt;/p&gt;

&lt;p&gt;The hard part of a client website is rarely the first screen. The real work starts when that screen must match the approved design, work on mobile, reuse existing components, connect to real data, and stay maintainable after handoff.&lt;/p&gt;

&lt;p&gt;AI design-to-code tools promise to speed this up, but they don't all solve the same problem. Some convert Figma layouts into frontend code. Some generate UI from text prompts. Others build whole applications or work inside your existing repository.&lt;/p&gt;

&lt;p&gt;This guide compares 11 tools across four workflows to show where each one fits. It is an editorial comparison based on documented capabilities and practical implementation concerns, not a benchmark where every tool was run against the same repository.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The main takeaway:&lt;/strong&gt; The best tool isn't the one that generates the most code. It's the one that gets you closest to the required result without creating more cleanup than it saves.&lt;/p&gt;

&lt;h2&gt;
  
  
  How the tools are compared
&lt;/h2&gt;

&lt;p&gt;Comparing a Figma integration directly with a full-stack app builder can mislead, because their jobs differ. So the tools are grouped by starting point and intended output, then judged on six criteria:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Design fidelity:&lt;/strong&gt; how well the layout, spacing, typography, colors, and responsive behavior are preserved.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Code integration:&lt;/strong&gt; whether the output fits an existing codebase and uses the project's components.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Iteration speed:&lt;/strong&gt; how easily a developer can change things after the first generation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Functional completeness:&lt;/strong&gt; whether you get only a visual interface or working application features.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Developer control:&lt;/strong&gt; whether the team can inspect, edit, test, and maintain the generated code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Handoff quality:&lt;/strong&gt; whether designers and developers can keep working together without losing design intent.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The 11 tools at a glance
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;Best for&lt;/th&gt;
&lt;th&gt;Suits&lt;/th&gt;
&lt;th&gt;Biggest watch-out&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Figma Make&lt;/td&gt;
&lt;td&gt;Figma-to-code&lt;/td&gt;
&lt;td&gt;Interactive prototypes from design context&lt;/td&gt;
&lt;td&gt;Designers&lt;/td&gt;
&lt;td&gt;Prototype code still needs engineering review&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Figma MCP Server&lt;/td&gt;
&lt;td&gt;Design context for agents&lt;/td&gt;
&lt;td&gt;Feeding structured Figma data to a coding agent&lt;/td&gt;
&lt;td&gt;Developers with a design system&lt;/td&gt;
&lt;td&gt;It supplies context; the agent writes the code&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Builder.io Visual Copilot&lt;/td&gt;
&lt;td&gt;Figma-to-code&lt;/td&gt;
&lt;td&gt;Code aligned with an existing design system&lt;/td&gt;
&lt;td&gt;Designers and developers&lt;/td&gt;
&lt;td&gt;Setup and component mapping take effort&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Anima&lt;/td&gt;
&lt;td&gt;Figma-to-code&lt;/td&gt;
&lt;td&gt;Exporting Figma layouts as React, Vue, or HTML&lt;/td&gt;
&lt;td&gt;Designers and developers&lt;/td&gt;
&lt;td&gt;Output may need refactoring and responsive QA&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;v0 by Vercel&lt;/td&gt;
&lt;td&gt;Prompt-based UI&lt;/td&gt;
&lt;td&gt;Polished web UI and app features&lt;/td&gt;
&lt;td&gt;Developers, especially React teams&lt;/td&gt;
&lt;td&gt;Output needs integration and testing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Google Stitch&lt;/td&gt;
&lt;td&gt;Prompt-based UI&lt;/td&gt;
&lt;td&gt;Exploring interface directions&lt;/td&gt;
&lt;td&gt;Designers, product teams&lt;/td&gt;
&lt;td&gt;A draft, not a production application&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Banani&lt;/td&gt;
&lt;td&gt;Prompt-based UI&lt;/td&gt;
&lt;td&gt;Editable multi-screen prototypes with export&lt;/td&gt;
&lt;td&gt;Designers, product teams&lt;/td&gt;
&lt;td&gt;Logic and architecture are separate work&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bolt.new&lt;/td&gt;
&lt;td&gt;App builder&lt;/td&gt;
&lt;td&gt;Interactive web apps from prompts&lt;/td&gt;
&lt;td&gt;Small teams, solo builders&lt;/td&gt;
&lt;td&gt;Awkward fit for an existing client repository&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lovable&lt;/td&gt;
&lt;td&gt;App builder&lt;/td&gt;
&lt;td&gt;MVPs and early product validation&lt;/td&gt;
&lt;td&gt;Small teams, solo builders&lt;/td&gt;
&lt;td&gt;Backend and access rules need real review&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cursor&lt;/td&gt;
&lt;td&gt;Coding agent&lt;/td&gt;
&lt;td&gt;Implementing designs in an existing repository&lt;/td&gt;
&lt;td&gt;Developers&lt;/td&gt;
&lt;td&gt;Needs clear instructions and diff review&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Claude Code&lt;/td&gt;
&lt;td&gt;Coding agent&lt;/td&gt;
&lt;td&gt;Multi-step implementation across a codebase&lt;/td&gt;
&lt;td&gt;Developers&lt;/td&gt;
&lt;td&gt;Needs clear constraints and test coverage&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Scorecard against the six criteria
&lt;/h2&gt;

&lt;p&gt;These ratings are my editorial judgment from documented capabilities, not measured results. "n/a" means the criterion doesn't apply because the tool doesn't start from an existing design. The agent-based rows assume good design context is supplied.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Design fidelity&lt;/th&gt;
&lt;th&gt;Code integration&lt;/th&gt;
&lt;th&gt;Iteration speed&lt;/th&gt;
&lt;th&gt;Functional completeness&lt;/th&gt;
&lt;th&gt;Developer control&lt;/th&gt;
&lt;th&gt;Handoff quality&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Figma Make&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Figma MCP + agent&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Visual Copilot&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Anima&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;v0&lt;/td&gt;
&lt;td&gt;n/a&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stitch&lt;/td&gt;
&lt;td&gt;n/a&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Banani&lt;/td&gt;
&lt;td&gt;n/a&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bolt.new&lt;/td&gt;
&lt;td&gt;n/a&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lovable&lt;/td&gt;
&lt;td&gt;n/a&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cursor&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Claude Code&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Converters score well on fidelity but low on functional completeness. App builders show the reverse. Only the agent-based workflows combine fidelity, integration, and completeness, and they do it by working inside your real project.&lt;/p&gt;

&lt;h2&gt;
  
  
  Category 1: Figma-to-code and design system integrations
&lt;/h2&gt;

&lt;p&gt;These tools fit when a designer has finished the interface and developers need to translate it without losing design decisions.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Figma Make
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhjwxfa8f1jlppa1rse61.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhjwxfa8f1jlppa1rse61.png" alt=" " width="800" height="365"&gt;&lt;/a&gt;&lt;br&gt;
Figma Make creates working, editable code and testable prototypes from prompts and design inputs, with built-in code inspection, sharing, and publishing.&lt;/p&gt;

&lt;p&gt;Say a client approves a dashboard design but wants to see how filters, navigation, and interactive cards behave before engineering commits. Make can turn that concept into a prototype for feedback. It also helps while the design is still changing, since the team can explore an idea without treating it as a production feature.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Watch out for:&lt;/strong&gt; a working prototype is not a maintainable application. Authentication, permissions, error handling, validation, and integration with the client's services still need real engineering.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Verdict:&lt;/strong&gt; the right tool for getting client sign-off on an experience. For implementation in an established repository, move to Figma MCP, Builder.io, or a coding agent.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Figma MCP Server
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8xeszkfn25xj3c5mqt9c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8xeszkfn25xj3c5mqt9c.png" alt=" " width="799" height="366"&gt;&lt;/a&gt;&lt;br&gt;
The Figma MCP Server changes how coding agents get design information. Instead of recreating a screen from a screenshot, an agent can receive structured context from Figma: selected layers, components, variables, and layout details. Figma's documentation makes it clear that the server provides context while the connected agent writes the final code, so quality depends on the context, instructions, and model.&lt;/p&gt;

&lt;p&gt;Picture a client with a React component library, spacing tokens, and reusable form elements. A generic prompt might produce yet another button with slightly different padding. With a well-configured MCP workflow, the agent can follow the project's component conventions instead. Code Connect and clear component documentation help further where available.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Watch out for:&lt;/strong&gt; the connection doesn't guarantee the agent picks the right components. You still need implementation rules, relevant codebase context, and verification steps.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Verdict:&lt;/strong&gt; the strongest starting point for a team with a mature Figma design system and developers already using an agent. It connects design context to the place where implementation actually happens.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Builder.io (Visual Copilot)
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8bfz3vn6fa6txnf003c5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8bfz3vn6fa6txnf003c5.png" alt=" " width="800" height="363"&gt;&lt;/a&gt;&lt;br&gt;
Visual Copilot translates designs into code while accounting for existing components, tokens, and conventions. Its documentation describes support for frameworks and styling approaches including React, Vue, Angular, Tailwind CSS, and CSS Modules, with specifics depending on the workflow.&lt;/p&gt;

&lt;p&gt;It suits projects that can't simply replace their frontend with whatever an AI tool generates. If a client already has a navigation component, product card, pricing section, and type system, rebuilding them from scratch creates differences and maintenance costs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Watch out for:&lt;/strong&gt; the output is only as good as your mappings. Unclear component names, missing documentation, or inconsistent codebase patterns mean you may need to fix that foundation first.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Verdict:&lt;/strong&gt; the strongest converter to evaluate when consistency with an existing product matters as much as initial speed.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Anima
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcmrw1iv8kh13ji4op0ch.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcmrw1iv8kh13ji4op0ch.png" alt=" " width="800" height="361"&gt;&lt;/a&gt;&lt;br&gt;
Anima is the more direct option when you start with a Figma file and want frontend code. Its documented workflow covers React, Vue, and HTML, with styling and component options depending on the output, plus a playground to preview and refine the code.&lt;/p&gt;

&lt;p&gt;It works best on well-structured designs with clear layouts, where it can cut the effort of recreating pages and establishing a first responsive version.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Watch out for:&lt;/strong&gt; exported code isn't the same as code that follows your architecture. Inspect component structure, class names, responsive behavior, accessibility, and repeated elements before merging.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Verdict:&lt;/strong&gt; choose it when direct export is the main requirement. If reusing existing components is essential, compare its output against Builder.io and an MCP-based workflow.&lt;/p&gt;

&lt;h2&gt;
  
  
  Category 2: Prompt-based UI generators
&lt;/h2&gt;

&lt;p&gt;These help when the team needs to explore a direction or turn a product brief into an initial interface before the final design is settled.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. v0 by Vercel
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8b0estwdbba7fdzar1f3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8b0estwdbba7fdzar1f3.png" alt=" " width="800" height="362"&gt;&lt;/a&gt;&lt;br&gt;
v0 has grown beyond isolated snippets. Its documentation describes building UI, creating full-stack apps, connecting backends, and deploying through Vercel.&lt;/p&gt;

&lt;p&gt;A useful prompt describes a customer dashboard with a sidebar, recent activity summary, filter controls, and a responsive table. The developer then refines the result instead of building every element from a blank file.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Watch out for:&lt;/strong&gt; convincing components can still miss edge cases, loading states, error handling, and your design system's conventions. If you have custom components and tokens, name them in the prompt rather than letting v0 invent new ones.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Verdict:&lt;/strong&gt; the best fit here for moving fast from an interface brief to editable frontend code, especially on React projects.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Google Stitch (formerly Galileo AI)
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcavly4ihetdwrtf9ffl0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcavly4ihetdwrtf9ffl0.png" alt=" " width="800" height="364"&gt;&lt;/a&gt;&lt;br&gt;
Stitch generates interface concepts from text and visual references, and it is the successor to Galileo AI in this space. Current descriptions emphasize web and mobile concepts and handoff through export options.&lt;/p&gt;

&lt;p&gt;It helps when a client wants a cleaner onboarding flow but supplies only a short brief. The team can try several screen structures and visual directions before choosing one to refine.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Watch out for:&lt;/strong&gt; an attractive draft doesn't settle interaction behavior, responsive edge cases, data handling, or architecture.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Verdict:&lt;/strong&gt; use it early, when the open question is what the interface should look like, not as a way to deliver the finished application.&lt;/p&gt;

&lt;h3&gt;
  
  
  7. Banani
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frkortpiufs0vxvv12j9f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frkortpiufs0vxvv12j9f.png" alt=" " width="800" height="366"&gt;&lt;/a&gt;&lt;br&gt;
Banani generates editable UI designs from prompts, screenshots, and references. Documented workflows include editing generated screens, building multi-screen prototypes, exporting to Figma or HTML/CSS, and connecting designs to coding agents through MCP.&lt;/p&gt;

&lt;p&gt;That suits a client who needs more than one landing page concept, such as a dashboard, settings page, account screen, and onboarding flow. Editing before export means the team can align on layout and hierarchy first.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Watch out for:&lt;/strong&gt; a multi-screen prototype isn't an implemented application. Navigation, authentication, API behavior, validation, and persistence still need building.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Verdict:&lt;/strong&gt; a good bridge between concept exploration and code. Pair it with a coding agent for production work.&lt;/p&gt;

&lt;h2&gt;
  
  
  Category 3: Full-Stack &amp;amp; App Builders (Vibe Coding)
&lt;/h2&gt;

&lt;p&gt;These go beyond a design or exported markup. They aim to create working applications from prompts, making them useful for prototypes and MVPs, but reviews have to cover more than visual accuracy.&lt;/p&gt;

&lt;h3&gt;
  
  
  8. Bolt.new
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ff9rs0hcputwyfrhq6mis.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ff9rs0hcputwyfrhq6mis.png" alt=" " width="799" height="370"&gt;&lt;/a&gt;&lt;br&gt;
Bolt.new lets you describe an app, generate code, preview it, and keep refining in the same workflow. The product also highlights integrated hosting, databases, and authentication.&lt;/p&gt;

&lt;p&gt;It fits briefs that include interaction rather than a static page, such as a small booking app with a service-selection screen, a booking form, and a basic data-backed flow.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Watch out for:&lt;/strong&gt; bundled infrastructure is convenient for a new project but pulls against an existing one. If the client already has a repository, hosting, and database, you'll spend time reconciling Bolt's setup with theirs. As the app grows, review the data structure, permissions, validation, and deployment configuration; a working preview doesn't mean it's secure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Verdict:&lt;/strong&gt; excellent for a fast, standalone prototype. For a client with an established codebase, check fit before starting.&lt;/p&gt;

&lt;h3&gt;
  
  
  9. Lovable
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fss1kkyjrbpfb9g9g5i23.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fss1kkyjrbpfb9g9g5i23.png" alt=" " width="799" height="384"&gt;&lt;/a&gt;&lt;br&gt;
Lovable creates applications from natural-language instructions and suits early validation, such as a customer portal, booking workflow, or internal dashboard built to gather feedback before a larger investment.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Watch out for:&lt;/strong&gt; the sharpest risk is the backend. If you connect a hosted database, the data-access rules, such as row-level security policies, determine who can read and write what, and a generated app can look fine while exposing data. Review authentication, authorization, secret handling, and failure states, and confirm how code ownership and export work if the project must move into the client's repository.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Verdict:&lt;/strong&gt; a strong choice for validating a product concept quickly. Don't treat a generated MVP as production-ready until its code and security assumptions have been reviewed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Category 4: AI coding agents
&lt;/h2&gt;

&lt;p&gt;Agents don't generate a design or an app from scratch. They work inside your repository, which is why they suit most established client projects.&lt;/p&gt;

&lt;h3&gt;
  
  
  10. Cursor
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2tj0j7s6c9diz1kydsj5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2tj0j7s6c9diz1kydsj5.png" alt=" " width="799" height="366"&gt;&lt;/a&gt;&lt;br&gt;
Cursor is an AI coding environment. Its agent can inspect a codebase, edit multiple files, run terminal commands, and help debug, and Cursor documents workflows for using screenshots and Figma context to guide implementation.&lt;/p&gt;

&lt;p&gt;Suppose a client has a working React app and wants a redesigned pricing page. Rather than generating a separate application, you ask the agent to find the relevant components, use the design context, and build the page within the current structure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Watch out for:&lt;/strong&gt; the agent can pick the wrong abstraction, touch unrelated files, or introduce regressions. Keep changes small, write your component and styling conventions into the project's rules, inspect the diffs, run tests, and check the result in a browser.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Verdict:&lt;/strong&gt; a natural first choice for developers who want AI assistance inside an editor with changes visible as they happen.&lt;/p&gt;

&lt;h3&gt;
  
  
  11. Claude Code
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwqyzhwuu7h8db7jhlszp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwqyzhwuu7h8db7jhlszp.png" alt=" " width="800" height="364"&gt;&lt;/a&gt;&lt;br&gt;
Claude Code is an agentic coding tool that works through development tasks in a codebase: reading files, editing code, running commands, and connecting to external tools through supported integrations. Figma's documentation lists Claude Code among the clients that can use its MCP server.&lt;/p&gt;

&lt;p&gt;A typical task: implement a Figma screen, reuse existing components, add validation, update the tests, and fix whatever verification turns up. That works best with a clear specification and access to the right project context.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Watch out for:&lt;/strong&gt; it needs boundaries. State which files and conventions matter, what must not change, and what tests to run, and put standing conventions in the project's instruction file. Review the commands and file changes it proposes, especially around sensitive configuration.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Verdict:&lt;/strong&gt; strong when the work is several connected engineering tasks rather than a single layout.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cursor or Claude Code?
&lt;/h2&gt;

&lt;p&gt;The practical difference is mostly workflow. Cursor is editor-centered, so you see and steer changes inline as you work. Claude Code is terminal-centered, which suits longer multi-step tasks you can specify up front and review afterward. Both can read your repository, use Figma context through MCP, and run tests. Many teams choose based on habit and tooling preference, and some use both. Try each on the same small task in your own repository.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Disclosure:&lt;/strong&gt; This guide was edited with help from Claude, made by Anthropic, which also makes Claude Code. I've rated the two agents equally for that reason; judge them on your own repository.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Pricing, learning curve, and team fit.
&lt;/h2&gt;

&lt;p&gt;I haven't listed prices because plans and limits change often. A few patterns hold in general:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Editor and agent tools typically use per-seat or usage-based plans. Cost scales with how much your developers lean on them.&lt;/li&gt;
&lt;li&gt;App builders are often credit- or usage-based. Heavy iteration can burn through allowances, so budget for the number of revisions a real project needs.&lt;/li&gt;
&lt;li&gt;Design-side tools may gate export, code, or MCP features by plan. Confirm that the feature you need is on the plan you'd buy.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On the learning curve, Figma Make, Stitch, and Banani are approachable for designers. Converters need some developer setup. App builders are quick to start but reward someone who can read and fix the code. Agents give the most control and ask the most of the developer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Which should you choose?
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;If your priority is...&lt;/th&gt;
&lt;th&gt;Shortlist&lt;/th&gt;
&lt;th&gt;Why&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;An interactive prototype for client sign-off&lt;/td&gt;
&lt;td&gt;Figma Make&lt;/td&gt;
&lt;td&gt;Shows behavior before engineering commits&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Design context inside a real codebase&lt;/td&gt;
&lt;td&gt;Figma MCP Server + Cursor or Claude Code&lt;/td&gt;
&lt;td&gt;Connects the approved design to repository-level coding&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Code that matches an existing design system&lt;/td&gt;
&lt;td&gt;Builder.io Visual Copilot&lt;/td&gt;
&lt;td&gt;Maps designs to your components and tokens&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Direct Figma export&lt;/td&gt;
&lt;td&gt;Anima&lt;/td&gt;
&lt;td&gt;Fast path to editable React, Vue, or HTML&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Prompt-generated, polished UI&lt;/td&gt;
&lt;td&gt;v0&lt;/td&gt;
&lt;td&gt;Quick iteration on component-driven interfaces&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Exploring visual directions&lt;/td&gt;
&lt;td&gt;Stitch or Banani&lt;/td&gt;
&lt;td&gt;Cheap to generate and compare concepts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A functional prototype or MVP&lt;/td&gt;
&lt;td&gt;Bolt.new or Lovable&lt;/td&gt;
&lt;td&gt;Includes application behavior&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Implementing in an existing project&lt;/td&gt;
&lt;td&gt;Cursor or Claude Code&lt;/td&gt;
&lt;td&gt;Works with your files, components, and tests&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;If the client has approved Figma designs and an existing codebase, start with the Figma MCP Server connected to Cursor or Claude Code. It isn't one-click, but it gives the agent real design context while keeping it inside your actual components. If the client has only an idea, try v0, Stitch, or Banani first. If the goal is a working MVP with several flows, compare Bolt.new and Lovable on the same brief.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to check before handing code to a client
&lt;/h2&gt;

&lt;p&gt;Treat AI-generated code as a starting point. Whatever tool produced it, run the same checks.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Design fidelity:&lt;/strong&gt; Compare the build with the approved design at the same viewport size. Check typography, spacing, alignment, image cropping, button sizes, and hierarchy.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Responsive behavior:&lt;/strong&gt; Test several widths, including narrow phones. Confirm navigation collapses, cards reflow, forms stay usable, and long content doesn't overflow.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Functionality:&lt;/strong&gt; Click buttons, submit forms, open menus, trigger validation, and inspect loading and error states. A screenshot can't tell you whether the interface works.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Component reuse:&lt;/strong&gt; Confirm the build uses your design system's buttons, inputs, cards, and layout primitives instead of new variants.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Accessibility:&lt;/strong&gt; Test keyboard navigation, focus indicators, heading structure, form labels, and color contrast. Give meaningful images and charts text alternatives.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Maintainability and security:&lt;/strong&gt; Review code structure, dependencies, API usage, environment variables, and data handling. Never expose secrets in frontend code, and never assume generated auth rules are correct.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Client handoff:&lt;/strong&gt; Deliver the code, setup instructions, required environment variables, known limitations, and deployment steps. A clean handoff is part of the deliverable.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Final thoughts
&lt;/h2&gt;

&lt;p&gt;These tools don't compete head-to-head. Figma Make turns design ideas into prototypes. The Figma MCP Server feeds design context to coding agents. Visual Copilot and Anima translate existing designs into frontend code. v0, Stitch, and Banani generate and refine interfaces; Bolt.new and Lovable build whole applications. Cursor and Claude Code make changes inside an existing repository.&lt;/p&gt;

&lt;p&gt;For most client projects, my shortlist is the Figma MCP Server with a coding agent for existing codebases, v0 for prompt-driven UI, and Lovable or Bolt.new for functional MVPs. Pick based on your starting point and expected deliverable, then verify with visual comparison, responsive testing, functional checks, and code review.&lt;/p&gt;

&lt;p&gt;The real time-saver isn't the first generated screen. It's choosing the workflow that minimizes the work between that screen and code the client can confidently maintain.&lt;/p&gt;

&lt;h2&gt;
  
  
  Frequently asked questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What is the best AI design-to-code tool?
&lt;/h3&gt;

&lt;p&gt;It depends on your starting point: Builder.io Visual Copilot and Anima suit converting finished Figma designs into frontend code. The Figma MCP Server connects designs to coding agents, v0 suits prompt-driven UI work, and Lovable or Bolt.new suit functional application prototypes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can AI convert a Figma design into production-ready code?
&lt;/h3&gt;

&lt;p&gt;It can generate code from a design, but production readiness is never guaranteed. Developers still need to review layout fidelity, responsive behavior, component reuse, accessibility, security, error handling, and maintainability.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is the difference between Figma MCP Server and Figma Make?
&lt;/h3&gt;

&lt;p&gt;The MCP Server provides structured design context to external AI agents that write or modify code. Figma Make creates and refines code-backed prototypes inside Figma's own workflow. One feeds your existing development tools; the other is an integrated design-and-prototyping experience.&lt;/p&gt;

&lt;h3&gt;
  
  
  Are AI app builders better than Figma-to-code tools?
&lt;/h3&gt;

&lt;p&gt;Not necessarily. Figma-to-code tools are better at translating approved visual designs into frontend code. App builders are better when the task also involves application structure, backend integration, and data flows. Choose based on whether design fidelity or rapid application development matters more.&lt;/p&gt;

&lt;h3&gt;
  
  
  How do I test AI-generated frontend code before client delivery?
&lt;/h3&gt;

&lt;p&gt;Compare the rendered interface to the approved design, test responsive layouts at several widths, verify every interaction and form, check accessibility, and run the project's build and test commands. Also review component reuse, dependencies, API calls, and sensitive-data handling before deploying or handing over the code.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>design</category>
      <category>nocode</category>
      <category>discuss</category>
    </item>
    <item>
      <title>Confused about AI governance frameworks? I compared NIST, ISO 42001, the EU AI Act, and more to help you understand their differences, requirements, and use cases. Explore which framework fits your organization! 🚀</title>
      <dc:creator>Dev Stack Hub</dc:creator>
      <pubDate>Mon, 26 Oct 2026 16:54:11 +0000</pubDate>
      <link>https://dev.to/devstackhub/confused-about-ai-governance-frameworks-i-compared-nist-iso-42001-the-eu-ai-act-and-more-to-2obn</link>
      <guid>https://dev.to/devstackhub/confused-about-ai-governance-frameworks-i-compared-nist-iso-42001-the-eu-ai-act-and-more-to-2obn</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/devstackhub/ai-governance-frameworks-nist-iso-eu-ai-act-more-compared-3j3l" class="crayons-story__hidden-navigation-link"&gt;AI Governance Frameworks: NIST, ISO, EU AI Act &amp;amp; More Compared&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/devstackhub" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4081963%2F9d10c5fa-c76e-4c9d-88af-e3f2fced5a62.jpg" alt="devstackhub profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/devstackhub" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Dev Stack Hub
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Dev Stack Hub
                
                
              
              &lt;div id="story-author-preview-content-4898968" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/devstackhub" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4081963%2F9d10c5fa-c76e-4c9d-88af-e3f2fced5a62.jpg" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Dev Stack Hub&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/devstackhub/ai-governance-frameworks-nist-iso-eu-ai-act-more-compared-3j3l" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Oct 22&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/devstackhub/ai-governance-frameworks-nist-iso-eu-ai-act-more-compared-3j3l" id="article-link-4898968"&gt;
          AI Governance Frameworks: NIST, ISO, EU AI Act &amp;amp; More Compared
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag crayons-tag--filled  " href="/t/discuss"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;discuss&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/ai"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;ai&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/security"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;security&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/devdiscuss"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;devdiscuss&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/devstackhub/ai-governance-frameworks-nist-iso-eu-ai-act-more-compared-3j3l" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/fire-f60e7a582391810302117f987b22a8ef04a2fe0df7e3258a5f49332df1cec71e.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/raised-hands-74b2099fd66a39f2d7eed9305ee0f4553df0eb7b4f11b01b6b1b499973048fe5.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;25&lt;span class="hidden s:inline"&gt;&amp;nbsp;reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/devstackhub/ai-governance-frameworks-nist-iso-eu-ai-act-more-compared-3j3l#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              6&lt;span class="hidden s:inline"&gt;&amp;nbsp;comments&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            12 min read
          &lt;/small&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
      <category>ai</category>
      <category>management</category>
      <category>security</category>
    </item>
    <item>
      <title>Exploring Respan Gateway 🚀 Discover how its plugin-first architecture approaches LLM routing, fallbacks, and MCP tools. I explored its features and limitations for developers building AI applications. Read my findings!</title>
      <dc:creator>Dev Stack Hub</dc:creator>
      <pubDate>Mon, 26 Oct 2026 16:33:09 +0000</pubDate>
      <link>https://dev.to/devstackhub/exploring-respan-gateway-discover-how-its-plugin-first-architecture-approaches-llm-routing-4cbh</link>
      <guid>https://dev.to/devstackhub/exploring-respan-gateway-discover-how-its-plugin-first-architecture-approaches-llm-routing-4cbh</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/devstackhub/i-explored-respan-gateway-a-plugin-first-open-source-llm-gateway-4d6b" class="crayons-story__hidden-navigation-link"&gt;I Explored Respan Gateway: A Plugin-First, Open-Source LLM Gateway&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
      &lt;a href="https://dev.to/devstackhub/i-explored-respan-gateway-a-plugin-first-open-source-llm-gateway-4d6b" class="crayons-article__context-note crayons-article__context-note__feed"&gt;&lt;p&gt;Born from a 100k-line legacy code nightmare&lt;/p&gt;

&lt;/a&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/devstackhub" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4081963%2F9d10c5fa-c76e-4c9d-88af-e3f2fced5a62.jpg" alt="devstackhub profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/devstackhub" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Dev Stack Hub
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Dev Stack Hub
                
                
              
              &lt;div id="story-author-preview-content-4891117" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/devstackhub" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4081963%2F9d10c5fa-c76e-4c9d-88af-e3f2fced5a62.jpg" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Dev Stack Hub&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/devstackhub/i-explored-respan-gateway-a-plugin-first-open-source-llm-gateway-4d6b" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Oct 21&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/devstackhub/i-explored-respan-gateway-a-plugin-first-open-source-llm-gateway-4d6b" id="article-link-4891117"&gt;
          I Explored Respan Gateway: A Plugin-First, Open-Source LLM Gateway
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag crayons-tag--filled  " href="/t/discuss"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;discuss&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/ai"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;ai&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/llm"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;llm&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/opensource"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;opensource&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/devstackhub/i-explored-respan-gateway-a-plugin-first-open-source-llm-gateway-4d6b" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/fire-f60e7a582391810302117f987b22a8ef04a2fe0df7e3258a5f49332df1cec71e.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/raised-hands-74b2099fd66a39f2d7eed9305ee0f4553df0eb7b4f11b01b6b1b499973048fe5.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;48&lt;span class="hidden s:inline"&gt;&amp;nbsp;reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/devstackhub/i-explored-respan-gateway-a-plugin-first-open-source-llm-gateway-4d6b#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              48&lt;span class="hidden s:inline"&gt;&amp;nbsp;comments&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            8 min read
          &lt;/small&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
      <category>ai</category>
      <category>llm</category>
      <category>mcp</category>
      <category>opensource</category>
    </item>
    <item>
      <title>AI Governance Frameworks: NIST, ISO, EU AI Act &amp; More Compared</title>
      <dc:creator>Dev Stack Hub</dc:creator>
      <pubDate>Thu, 22 Oct 2026 17:58:34 +0000</pubDate>
      <link>https://dev.to/devstackhub/ai-governance-frameworks-nist-iso-eu-ai-act-more-compared-3j3l</link>
      <guid>https://dev.to/devstackhub/ai-governance-frameworks-nist-iso-eu-ai-act-more-compared-3j3l</guid>
      <description>&lt;p&gt;Artificial intelligence is becoming part of everyday business operations, from customer support chatbots and recruitment systems to fraud detection and generative AI tools.&lt;/p&gt;

&lt;p&gt;But as organizations adopt AI, a difficult question follows: how do you make sure these systems are safe, fair, transparent, and accountable without slowing innovation? That is where AI governance frameworks come in. Frameworks such as the NIST AI Risk Management Framework, ISO/IEC 42001, and the EU AI Act help organizations understand AI risks, establish accountability, and manage compliance.&lt;/p&gt;

&lt;p&gt;However, they serve different purposes. Some provide practical risk management guidance, others establish auditable management systems, and some introduce legally binding requirements. Understanding these differences is the first step toward building an AI governance strategy that works in the real world.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Are AI Governance Frameworks?
&lt;/h2&gt;

&lt;p&gt;AI governance frameworks are structured guidelines, standards, or legal requirements that help organizations develop, deploy, monitor, and manage artificial intelligence responsibly.&lt;/p&gt;

&lt;p&gt;They establish how organizations should make AI decisions, who is accountable, what risks to evaluate, and how to respond when an AI system produces harmful or unreliable results.&lt;/p&gt;

&lt;p&gt;A practical AI governance framework may cover:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Risk management:&lt;/strong&gt; Identifying potential harm before and after deployment.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data governance:&lt;/strong&gt; Managing data quality, privacy, security, and access.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Transparency:&lt;/strong&gt; Documenting how AI systems work and communicate their limitations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Accountability:&lt;/strong&gt; Assigning clear responsibilities for AI-related decisions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Human oversight:&lt;/strong&gt; Defining when people must review or override AI outputs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Monitoring:&lt;/strong&gt; Evaluating system performance, reliability, and emerging risks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Compliance:&lt;/strong&gt; Meeting applicable legal, regulatory, and contractual obligations.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example, a company using AI to screen job applications needs more than a model that produces quick results. It must consider discrimination, data protection, explainability, human review, and the consequences of incorrect decisions.&lt;/p&gt;

&lt;p&gt;AI governance provides a structure for addressing these concerns before they become expensive operational, legal, or reputational problems.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The key distinction:&lt;/strong&gt; an AI governance framework is not necessarily a law. Some frameworks are voluntary guidance, some are certifiable standards, and others are enforceable legislation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Top AI Governance Frameworks Compared
&lt;/h2&gt;

&lt;p&gt;Organizations do not have to choose a single framework and ignore the rest. In many cases, the most practical approach is to combine a risk management framework, a formal management standard, and the legal requirements that apply to the business.&lt;/p&gt;

&lt;p&gt;The following are the major frameworks and related governance instruments worth understanding.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. NIST AI Risk Management Framework (AI RMF)
&lt;/h3&gt;

&lt;p&gt;The NIST AI Risk Management Framework is a voluntary framework developed by the US National Institute of Standards and Technology. Published in January 2023, AI RMF 1.0 helps organizations identify, assess, and manage risks throughout the AI lifecycle. NIST also provides a companion profile for generative AI.&lt;/p&gt;

&lt;p&gt;Its central structure consists of four functions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Govern:&lt;/strong&gt; Establish policies, accountability, leadership responsibilities, and an organizational culture for managing AI risk.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Map:&lt;/strong&gt; Understand the system's purpose, context, intended users, and potential impacts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Measure:&lt;/strong&gt; Evaluate risks through testing, analysis, assessment, and ongoing monitoring.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Manage:&lt;/strong&gt; Prioritize risks and decide how to mitigate, monitor, transfer, or accept them.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Consider a business deploying an AI customer support assistant. The Govern function establishes who owns the system. Map identifies the users, data, and situations in which the assistant may fail. Measure evaluates incorrect answers, privacy risks, and reliability. Manage establishes escalation rules, monitoring, and corrective actions.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Best for:&lt;/strong&gt; Organizations that need a practical, adaptable process for managing AI risk.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Main advantage:&lt;/strong&gt; It provides a clear structure without requiring an organization to adopt a particular technology or purchase a certification.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Limitation:&lt;/strong&gt; NIST AI RMF is not a certification standard and does not, by itself, demonstrate compliance with every applicable law.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For many teams, NIST is a sensible starting point because its functions can be adapted to different business sizes, industries, and AI use cases.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. ISO/IEC 42001: AI Management System Standard
&lt;/h3&gt;

&lt;p&gt;ISO/IEC 42001 is an international standard published in 2023 that specifies requirements for establishing, implementing, maintaining, and continually improving an artificial intelligence management system, commonly called an AIMS.&lt;/p&gt;

&lt;p&gt;Unlike a framework that primarily guides risk assessment, ISO/IEC 42001 focuses on how an organization systematically manages AI.&lt;/p&gt;

&lt;p&gt;It encourages businesses to establish an AI policy, define responsibilities, assess risks and impacts, document relevant processes, evaluate performance, and improve controls over time.&lt;/p&gt;

&lt;p&gt;For example, a software company developing AI-powered analytics could use ISO/IEC 42001 to formalize how it approves models, reviews risks, documents changes, and evaluates the effectiveness of its governance program.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Best for:&lt;/strong&gt; Enterprises, AI vendors, SaaS providers, and organizations seeking a structured, auditable AI management system.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Main advantage:&lt;/strong&gt; Organizations can pursue third-party certification through an appropriate certification process, giving customers and business partners evidence that an AI management system has been independently assessed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Limitation:&lt;/strong&gt; Implementing and maintaining the system requires documentation, resources, internal ownership, and audit preparation. Certification does not automatically establish compliance with every AI-related regulation.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ISO/IEC 42001 is particularly relevant when customers ask how you manage AI risks, who approves AI deployments, and whether you independently assess governance practices.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. EU AI Act
&lt;/h3&gt;

&lt;p&gt;The EU AI Act differs from NIST and ISO/IEC 42001 because it is legislation, not voluntary guidance.&lt;/p&gt;

&lt;p&gt;Adopted as Regulation (EU) 2024/1689, it establishes legally binding requirements for covered AI systems and models, with obligations depending on the system's risk classification, the organization's role, and other factors. Its scope can affect organizations outside the EU when their activities fall within the regulation's reach.&lt;/p&gt;

&lt;p&gt;The Act takes a risk-based approach. Broadly, it distinguishes between prohibited AI practices, high-risk systems, systems subject to specific transparency obligations, and other AI uses that may face fewer requirements under this legislation.&lt;/p&gt;

&lt;p&gt;Examples of areas requiring careful assessment include certain employment-related AI systems, education, access to essential services, and biometric applications.&lt;/p&gt;

&lt;p&gt;The exact obligations depend on the system and the relevant provisions. Organizations may need to address risk management, data governance, technical documentation, record-keeping, human oversight, accuracy, robustness, transparency, or conformity assessment.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Best for:&lt;/strong&gt; Organizations developing, supplying, importing, distributing, or deploying AI within the regulation's scope.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Main advantage:&lt;/strong&gt; It establishes enforceable legal requirements rather than relying solely on voluntary commitments.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Limitation:&lt;/strong&gt; Compliance can be complex because obligations vary by AI category, organizational role, and applicable implementation dates.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Businesses should not assume that ISO certification or alignment with NIST automatically satisfies the EU AI Act. They need a separate assessment of the legal obligations that apply to their systems.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. OECD AI Principles
&lt;/h3&gt;

&lt;p&gt;The OECD AI Principles provide a high-level foundation for trustworthy AI policy and governance.&lt;/p&gt;

&lt;p&gt;They promote AI systems that respect human rights and democratic values, support transparency and explainability, remain robust and secure, and have clear accountability.&lt;/p&gt;

&lt;p&gt;Unlike the EU AI Act, the OECD principles are not themselves a directly enforceable AI compliance regime. Their value lies in providing shared principles that governments, businesses, and other institutions can translate into policies and practices.&lt;/p&gt;

&lt;p&gt;For example, an organization can use these principles to develop internal rules for responsible AI use, communicate its expectations to suppliers, and evaluate the broader social effects of its AI products.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Best for:&lt;/strong&gt; Organizations building responsible AI policies, ethical principles, and high-level governance commitments.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Main advantage:&lt;/strong&gt; The principles provide a broad foundation that can complement technical risk controls and legal compliance programs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Limitation:&lt;/strong&gt; They do not provide a complete operational control framework or a certification mechanism.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  5. ISO/IEC 23894: AI Risk Management Guidance
&lt;/h3&gt;

&lt;p&gt;ISO/IEC 23894 guides the management of AI-related risks.&lt;/p&gt;

&lt;p&gt;It is useful for organizations that need to incorporate AI-specific considerations into their wider enterprise risk management processes. These considerations can include uncertain model behavior, data limitations, changing operating conditions, and unintended consequences.&lt;/p&gt;

&lt;p&gt;For example, a financial services company introducing an AI forecasting system can use risk management guidance to identify failure scenarios, assess their potential impact, assign mitigation measures, and monitor residual risk.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Best for:&lt;/strong&gt; Risk managers, compliance teams, AI developers, and organizations integrating AI risk into existing enterprise risk processes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Main advantage:&lt;/strong&gt; It provides AI-focused risk management guidance that can complement broader standards and governance systems.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Limitation:&lt;/strong&gt; It is guidance rather than a standalone AI management system certification standard or a substitute for applicable legal obligations.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  6. ISO/IEC 27001: Information Security Management
&lt;/h3&gt;

&lt;p&gt;ISO/IEC 27001 is an information security management standard, not an AI-specific governance framework. Nevertheless, it can play an important supporting role.&lt;/p&gt;

&lt;p&gt;AI systems depend on data, infrastructure, access controls, software, and external services. Weaknesses in any of these areas can expose sensitive information or allow unauthorized manipulation.&lt;/p&gt;

&lt;p&gt;An organization using AI may therefore need security controls for training datasets, model access, credentials, deployment pipelines, logging, and third-party integrations.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Best for:&lt;/strong&gt; Organizations that need to integrate AI governance with information security and data protection practices.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Main advantage:&lt;/strong&gt; It provides a structured approach to managing information security risks across the organization.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Limitation:&lt;/strong&gt; Information security controls alone do not address every AI-specific concern, including algorithmic discrimination, explainability, or broader societal impacts.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ISO/IEC 27001 and ISO/IEC 42001 can complement each other, but they address different management objectives.&lt;/p&gt;

&lt;h3&gt;
  
  
  7. OWASP Top 10 for LLM Applications
&lt;/h3&gt;

&lt;p&gt;The OWASP Top 10 for LLM Applications focuses on security risks in applications built with large language models.&lt;/p&gt;

&lt;p&gt;It is especially relevant to organizations deploying AI assistants, retrieval-augmented generation systems, AI agents, and applications that connect language models to business data or external tools.&lt;/p&gt;

&lt;p&gt;Relevant security concerns include prompt injection, sensitive information disclosure, improper output handling, excessive agency, and weaknesses in the way an application manages model inputs and outputs.&lt;/p&gt;

&lt;p&gt;For example, test an AI assistant connected to an internal knowledge base to determine whether a malicious prompt could expose information the requesting user is not authorized to access.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Best for:&lt;/strong&gt; AI engineers, application security teams, and organizations deploying LLM-powered applications.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Main advantage:&lt;/strong&gt; It translates AI application security concerns into practical testing and mitigation areas.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Limitation:&lt;/strong&gt; It is not a complete organizational governance framework and does not replace a broader risk management or compliance program.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;OWASP is most effective as a technical security resource within a broader governance approach.&lt;/p&gt;

&lt;h2&gt;
  
  
  AI Governance Frameworks Comparison Table
&lt;/h2&gt;

&lt;p&gt;Here is a quick comparison of the seven frameworks and standards, based on their primary purpose rather than treating them as interchangeable alternatives.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Framework&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Primary Focus&lt;/th&gt;
&lt;th&gt;Certification&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;NIST AI RMF&lt;/td&gt;
&lt;td&gt;Voluntary framework&lt;/td&gt;
&lt;td&gt;AI risk management&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ISO/IEC 42001&lt;/td&gt;
&lt;td&gt;International standard&lt;/td&gt;
&lt;td&gt;AI management system&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;EU AI Act&lt;/td&gt;
&lt;td&gt;Binding legislation&lt;/td&gt;
&lt;td&gt;Legal AI requirements&lt;/td&gt;
&lt;td&gt;No general organizational certification&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OECD AI Principles&lt;/td&gt;
&lt;td&gt;International principles&lt;/td&gt;
&lt;td&gt;Responsible AI values&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ISO/IEC 23894&lt;/td&gt;
&lt;td&gt;Risk management guidance&lt;/td&gt;
&lt;td&gt;AI-specific risks&lt;/td&gt;
&lt;td&gt;Not a standalone certification standard&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ISO/IEC 27001&lt;/td&gt;
&lt;td&gt;International standard&lt;/td&gt;
&lt;td&gt;Information security&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OWASP Top 10 for LLM Applications&lt;/td&gt;
&lt;td&gt;Security guidance&lt;/td&gt;
&lt;td&gt;LLM application risks&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;These instruments address different needs. ISO/IEC 42001 offers a certifiable management system, NIST provides a risk management structure, and the EU AI Act establishes legal obligations for covered systems.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Choose the Right AI Governance Framework
&lt;/h2&gt;

&lt;p&gt;The best starting point depends on your organization’s size, industry, AI use cases, customer expectations, and regulatory exposure.&lt;/p&gt;

&lt;h3&gt;
  
  
  For startups and small businesses
&lt;/h3&gt;

&lt;p&gt;Start with NIST AI RMF principles to establish basic AI policies, inventory your systems, identify risks, and assign accountability. You can introduce more formal controls as your AI usage grows.&lt;/p&gt;

&lt;h3&gt;
  
  
  For enterprises seeking independent assurance
&lt;/h3&gt;

&lt;p&gt;Consider ISO/IEC 42001 if you need a documented AI management system and want the option of independent certification. This can be useful when enterprise customers evaluate supplier governance.&lt;/p&gt;

&lt;h3&gt;
  
  
  For businesses serving EU customers
&lt;/h3&gt;

&lt;p&gt;Start by determining whether the EU AI Act applies to your organization and each relevant AI system. Identify your role, risk classification, applicable obligations, and deadlines before deciding which additional controls are needed.&lt;/p&gt;

&lt;h3&gt;
  
  
  For teams building generative AI applications
&lt;/h3&gt;

&lt;p&gt;Combine an organization-wide governance approach with technical security guidance such as OWASP's LLM resources. Pay particular attention to sensitive data, prompt injection, access permissions, output validation, and human review.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Implement an AI Governance Framework
&lt;/h2&gt;

&lt;p&gt;Choosing a framework is only the beginning. The real challenge is turning its recommendations into repeatable processes that teams actually follow.&lt;/p&gt;

&lt;p&gt;A practical implementation plan can be broken into six steps.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Create an AI System Inventory
&lt;/h3&gt;

&lt;p&gt;Identify every AI system used, developed, purchased, or embedded in business processes.&lt;/p&gt;

&lt;p&gt;Record the system's purpose, owner, vendor, data sources, users, business impact, and deployment status. Include third-party AI tools and employee use of generative AI where relevant.&lt;/p&gt;

&lt;p&gt;Without an inventory, it is difficult to assess risk or determine which requirements apply.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Assess AI Risks and Impacts
&lt;/h3&gt;

&lt;p&gt;Evaluate what could go wrong and who could be affected.&lt;/p&gt;

&lt;p&gt;Consider inaccurate outputs, discrimination, privacy violations, cybersecurity threats, unreliable decisions, intellectual property concerns, and excessive dependence on automated recommendations.&lt;/p&gt;

&lt;p&gt;Risk assessments should consider both the likelihood of an issue and the severity of its consequences.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Define Roles and Responsibilities
&lt;/h3&gt;

&lt;p&gt;Decide who approves AI use cases, performs risk assessments, monitors performance, and responds to incidents.&lt;/p&gt;

&lt;p&gt;Depending on the organization, these responsibilities may involve legal, compliance, security, engineering, product, HR, and senior leadership teams.&lt;/p&gt;

&lt;p&gt;A governance policy is ineffective if nobody is responsible for enforcing it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Establish Policies and Controls
&lt;/h3&gt;

&lt;p&gt;Develop clear rules for AI development, procurement, deployment, and ongoing use.&lt;/p&gt;

&lt;p&gt;For example, employees may need approval before entering confidential information into external AI tools. High-impact AI applications may require additional testing, documented approval, and human oversight.&lt;/p&gt;

&lt;p&gt;Controls should reflect the actual risks rather than imposing the same process on every AI use case.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5: Monitor Systems After Deployment
&lt;/h3&gt;

&lt;p&gt;AI governance does not end when a model passes its initial evaluation.&lt;/p&gt;

&lt;p&gt;Monitor accuracy, reliability, security, unexpected behavior, user complaints, and performance changes. Define escalation procedures and set clear triggers for when a system must be corrected, restricted, or withdrawn.&lt;/p&gt;

&lt;p&gt;Generative AI applications also require attention to changing model behavior, retrieval sources, prompts, and connected tools.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 6: Document Evidence and Improve Continuously
&lt;/h3&gt;

&lt;p&gt;Maintain records of risk assessments, approvals, test results, policies, incidents, corrective actions, and monitoring activities.&lt;/p&gt;

&lt;p&gt;Review these records regularly and update controls when business requirements, AI capabilities, or applicable regulations change.&lt;/p&gt;

&lt;p&gt;This evidence supports internal accountability and can also help with external audits, customer assessments, and regulatory inquiries.&lt;/p&gt;

&lt;h2&gt;
  
  
  Can Organizations Use Multiple AI Governance Frameworks Together?
&lt;/h2&gt;

&lt;p&gt;Yes. Using multiple frameworks is often more practical than relying on one instrument to cover every governance requirement.&lt;/p&gt;

&lt;p&gt;For example, a company might use NIST AI RMF to structure risk assessments, ISO/IEC 42001 to organize its management system, ISO/IEC 27001 to support information security, and the EU AI Act to identify legally required controls.&lt;/p&gt;

&lt;p&gt;The important part is to map overlapping requirements rather than build separate processes for each framework.&lt;/p&gt;

&lt;p&gt;A single risk register, AI inventory, control library, and evidence repository can support several frameworks, as long as the records meet each relevant requirement.&lt;/p&gt;

&lt;p&gt;However, overlapping controls do not automatically satisfy every requirement. The organization must still document gaps and verify that the controls meet the applicable standard or law.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common AI Governance Mistakes to Avoid
&lt;/h2&gt;

&lt;p&gt;Even well-intentioned organizations can struggle to turn governance principles into effective practices.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Treating every framework as a law:&lt;/strong&gt; NIST and OECD guidance are not equivalent to binding legislation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Assuming certification guarantees legal compliance:&lt;/strong&gt; An ISO/IEC 42001 certificate does not automatically satisfy every regulatory obligation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Focusing only on model accuracy:&lt;/strong&gt; Privacy, security, fairness, transparency, and human impact matter too.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Creating policies without assigning owners:&lt;/strong&gt; Every important control needs a responsible person or team.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ignoring third-party AI:&lt;/strong&gt; External models and embedded AI features can introduce risks even when an organization did not develop them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Treating governance as a one-time project:&lt;/strong&gt; AI systems and their operating environments change, so governance must evolve with them.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The goal is not to produce the largest collection of policies. It is to build a system that helps people make informed decisions, manage risks, and demonstrate accountability.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;AI governance frameworks help organizations adopt artificial intelligence with clearer responsibilities, better risk controls, and greater accountability. However, NIST AI RMF, ISO/IEC 42001, the EU AI Act, OECD AI Principles, ISO/IEC 23894, ISO/IEC 27001, and OWASP's LLM security guidance are not direct substitutes for one another.&lt;/p&gt;

&lt;p&gt;NIST AI RMF is a practical starting point for managing AI risk, ISO/IEC 42001 provides a structured and certifiable AI management system, and the EU AI Act establishes legal requirements for AI activities within its scope. The remaining instruments can strengthen ethical decision-making, information security, risk assessment, and application-level protection.&lt;/p&gt;

&lt;p&gt;For most organizations, the sensible approach is to identify applicable legal obligations first, establish a consistent governance process, and map relevant standards and guidance into that process. This creates a more manageable way to support innovation while keeping AI risks visible, measurable, and accountable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Frequently Asked Questions (FAQs)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. What are the main AI governance frameworks?
&lt;/h3&gt;

&lt;p&gt;The main AI governance frameworks and related instruments include the NIST AI Risk Management Framework, ISO/IEC 42001, the EU AI Act, and the OECD AI Principles. ISO/IEC 23894, ISO/IEC 27001, and OWASP's LLM security guidance provide additional risk management and technical security support.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. What is the difference between NIST AI RMF and ISO 42001?
&lt;/h3&gt;

&lt;p&gt;NIST AI RMF provides voluntary guidance for identifying, measuring, and managing AI risks through its Govern, Map, Measure, and Manage functions. ISO/IEC 42001 specifies requirements for an AI management system that organizations can implement and pursue third-party certification against.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Is the EU AI Act mandatory for all companies using AI?
&lt;/h3&gt;

&lt;p&gt;No. The EU AI Act applies according to its scope, the AI system involved, and the organization's role. Certain obligations may affect organizations outside the EU, but businesses must assess their specific activities and applicable requirements rather than assume that every AI use is subject to identical rules.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Can ISO 42001 certification replace EU AI Act compliance?
&lt;/h3&gt;

&lt;p&gt;No. ISO/IEC 42001 certification demonstrates that an organization's AI management system has been assessed against the standard. It does not automatically establish compliance with the EU AI Act, which may impose additional system-specific legal requirements.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. How can a company choose the right AI governance framework?
&lt;/h3&gt;

&lt;p&gt;A company should begin by identifying its AI systems, applicable legal obligations, risk exposure, industry requirements, and customer expectations. NIST AI RMF can help structure risk management, ISO/IEC 42001 can formalize governance, and relevant laws and technical standards can supply additional requirements.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>security</category>
      <category>devdiscuss</category>
      <category>discuss</category>
    </item>
    <item>
      <title>🚀 Exploring Respan Gateway, an open-source LLM gateway with a plugin-first architecture for model routing, fallbacks, and MCP tools. An interesting project for AI developers to explore!</title>
      <dc:creator>Dev Stack Hub</dc:creator>
      <pubDate>Wed, 21 Oct 2026 17:58:49 +0000</pubDate>
      <link>https://dev.to/devstackhub/exploring-respan-gateway-an-open-source-llm-gateway-with-a-plugin-first-architecture-for-model-5ec8</link>
      <guid>https://dev.to/devstackhub/exploring-respan-gateway-an-open-source-llm-gateway-with-a-plugin-first-architecture-for-model-5ec8</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/devstackhub/i-explored-respan-gateway-a-plugin-first-open-source-llm-gateway-4d6b" class="crayons-story__hidden-navigation-link"&gt;I Explored Respan Gateway: A Plugin-First, Open-Source LLM Gateway&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
      &lt;a href="https://dev.to/devstackhub/i-explored-respan-gateway-a-plugin-first-open-source-llm-gateway-4d6b" class="crayons-article__context-note crayons-article__context-note__feed"&gt;&lt;p&gt;Born from a 100k-line legacy code nightmare&lt;/p&gt;

&lt;/a&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/devstackhub" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4081963%2F9d10c5fa-c76e-4c9d-88af-e3f2fced5a62.jpg" alt="devstackhub profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/devstackhub" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Dev Stack Hub
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Dev Stack Hub
                
                
              
              &lt;div id="story-author-preview-content-4891117" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/devstackhub" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4081963%2F9d10c5fa-c76e-4c9d-88af-e3f2fced5a62.jpg" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Dev Stack Hub&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/devstackhub/i-explored-respan-gateway-a-plugin-first-open-source-llm-gateway-4d6b" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Oct 21&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/devstackhub/i-explored-respan-gateway-a-plugin-first-open-source-llm-gateway-4d6b" id="article-link-4891117"&gt;
          I Explored Respan Gateway: A Plugin-First, Open-Source LLM Gateway
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag crayons-tag--filled  " href="/t/discuss"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;discuss&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/ai"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;ai&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/llm"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;llm&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/opensource"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;opensource&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/devstackhub/i-explored-respan-gateway-a-plugin-first-open-source-llm-gateway-4d6b" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/fire-f60e7a582391810302117f987b22a8ef04a2fe0df7e3258a5f49332df1cec71e.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/raised-hands-74b2099fd66a39f2d7eed9305ee0f4553df0eb7b4f11b01b6b1b499973048fe5.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;48&lt;span class="hidden s:inline"&gt;&amp;nbsp;reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/devstackhub/i-explored-respan-gateway-a-plugin-first-open-source-llm-gateway-4d6b#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              48&lt;span class="hidden s:inline"&gt;&amp;nbsp;comments&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            8 min read
          &lt;/small&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
      <category>ai</category>
      <category>llm</category>
      <category>mcp</category>
      <category>opensource</category>
    </item>
    <item>
      <title>I Explored Respan Gateway: A Plugin-First, Open-Source LLM Gateway</title>
      <dc:creator>Dev Stack Hub</dc:creator>
      <pubDate>Wed, 21 Oct 2026 17:39:05 +0000</pubDate>
      <link>https://dev.to/devstackhub/i-explored-respan-gateway-a-plugin-first-open-source-llm-gateway-4d6b</link>
      <guid>https://dev.to/devstackhub/i-explored-respan-gateway-a-plugin-first-open-source-llm-gateway-4d6b</guid>
      <description>&lt;p&gt;Every time I connect an application to a new LLM provider, I run into the same problem: how much provider-specific code do I have to add? One SDK expects one request format, another provider uses a different API, and switching models can mean changing far more than a model name. Then come retries, fallback logic, usage tracking, and debugging when a request fails.&lt;/p&gt;

&lt;p&gt;Most gateways answer this with the same pitch: one endpoint, many providers. Respan Gateway is more interesting for how it’s built than for that pitch. It is an open-source Rust gateway with a very small core, where nearly everything, including the providers, is a plugin. I went through the documentation and the launch post to see what that design means for a developer and what I’d check before relying on it.&lt;/p&gt;

&lt;p&gt;One caveat first: the project describes itself as pre-alpha, and its config and APIs may still change.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the Architecture Is the Interesting Part
&lt;/h2&gt;

&lt;p&gt;Respan says it ran its hosted gateway on LiteLLM since 2024. As customers needed different API-key settings, routes, and limits, its wrapper code grew to more than 100,000 lines. Then, this January, a routine LiteLLM upgrade caused intermittent connection errors and forced a rollback. Respan says it never found the cause and now backports upstream fixes by hand. That is the company’s own account, and I haven’t tested LiteLLM myself. It does explain the design goal: add features without breaking existing ones.&lt;/p&gt;

&lt;p&gt;The result is a small core inspired by Nginx, responsible only for dispatching events. Providers, protocols (chat, real-time voice, MCP), modalities (images, video), and gateway features like routing and fallbacks are all plugins. A provider is a short file. Respan cites Mistral’s at 22 lines, and provider quirks, such as Groq reporting token usage in its own field, are separate plugins with their own tests.&lt;/p&gt;

&lt;p&gt;Here is how a request moves through it:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A client adapter hands each HTTP or WebSocket connection to the first client format that recognizes it.&lt;/li&gt;
&lt;li&gt;That format exposes the request as a two-way stream of typed events.&lt;/li&gt;
&lt;li&gt;Plugins run in registration order. They can choose a provider, filter either stream, make extra calls, return a response, or refuse the request.&lt;/li&gt;
&lt;li&gt;A provider format opens the upstream connection.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;When client and provider speak the same API, and no plugin changes the payload, the original bytes and provider-specific fields pass through untouched. When the formats differ, requests translate through shared types for messages, tool calls, results, streams, and usage.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The trade-off:&lt;/strong&gt; plugins are natively compiled Rust crates, not dynamically loaded or interpreted. Config-only template plugins cover common needs like adding a vendor or format without a rebuild. Custom logic means compiling it in. In the Hacker News launch thread, a commenter pointed out that this means a CI step instead of pulling a container image, and raised supply-chain concerns. Respan replied that compiled-in crates can at least be reviewed, and said it pulled its Docker image before launch because some APIs may still change. Official builds are prebuilt binaries on GitHub Releases.&lt;/p&gt;

&lt;h2&gt;
  
  
  Installing and Making a First Request
&lt;/h2&gt;

&lt;p&gt;Respan Gateway can be installed in three ways: by downloading a binary from GitHub Releases, using Cargo, or installing the Python package from PyPI. Linux x86_64 is shown here (macOS, arm64, and Windows builds are also available), using the version shown in the release:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;VERSION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;v0.1.0
curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; &lt;span class="s2"&gt;"https://github.com/respanai/respan-gateway/releases/download/&lt;/span&gt;&lt;span class="nv"&gt;$VERSION&lt;/span&gt;&lt;span class="s2"&gt;/respan-gateway-&lt;/span&gt;&lt;span class="nv"&gt;$VERSION&lt;/span&gt;&lt;span class="s2"&gt;-x86_64-unknown-linux-gnu.tar.gz"&lt;/span&gt; | &lt;span class="nb"&gt;tar &lt;/span&gt;xz
&lt;span class="nb"&gt;sudo mv&lt;/span&gt; &lt;span class="s2"&gt;"respan-gateway-&lt;/span&gt;&lt;span class="nv"&gt;$VERSION&lt;/span&gt;&lt;span class="s2"&gt;-x86_64-unknown-linux-gnu/respan-gateway"&lt;/span&gt; /usr/local/bin/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Alternatively, install it with Cargo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;cargo &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--git&lt;/span&gt; https://github.com/respanai/respan-gateway respan-gateway
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or install the Python package from PyPI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;respan-gateway
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A source build has no web UI unless you build it first. Check the current release before copying the version number.&lt;/p&gt;

&lt;p&gt;With no config file, every provider key in your environment becomes a provider:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;OPENAI_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"sk-..."&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;ANTHROPIC_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"sk-ant-..."&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;GEMINI_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;
respan-gateway
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;OPENAI_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"sk-..."&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;ANTHROPIC_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"sk-ant-..."&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;GEMINI_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;
respan-gateway
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The gateway prints the providers it found, the endpoints it serves, and a request to try. The model name in each request decides where it goes, and a prefix like &lt;code&gt;openai/gpt-5.5&lt;/code&gt; forces a provider. The docs’ curl example sends a request in OpenAI format to Gemini:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl http://localhost:8080/v1/chat/completions &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;'{"model": "gemini-3.6-flash", "messages": [{"role": "user", "content": "Hello!"}]}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The same idea works from an SDK. Here, the OpenAI client talks to Claude:&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;openai&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;OpenAI&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;OpenAI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;base_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;http://localhost:8080/v1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;unused&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;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;completions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;claude-sonnet-4-5-20250929&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;messages&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;role&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;user&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;content&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;Hello!&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="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="n"&gt;choices&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="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With no client keys configured, the gateway answers only on your own machine.&lt;/p&gt;

&lt;p&gt;The documentation describes the startup output and request log available at &lt;code&gt;/ui/requests&lt;/code&gt;. I have not included a screenshot or claimed a local test result here.&lt;/p&gt;

&lt;h2&gt;
  
  
  One File: gateway.toml
&lt;/h2&gt;

&lt;p&gt;Everything beyond relaying a request is configured in one &lt;code&gt;gateway.toml&lt;/code&gt;. &lt;code&gt;respan-gateway init&lt;/code&gt; writes a commented starter from the keys in your environment. This block is adapted from the configure docs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="py"&gt;client_keys&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"env:GATEWAY_KEY"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="nn"&gt;[providers.openai]&lt;/span&gt;
&lt;span class="py"&gt;vendor&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"openai"&lt;/span&gt;
&lt;span class="py"&gt;key&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"env:OPENAI_API_KEY"&lt;/span&gt;

&lt;span class="nn"&gt;[providers.anthropic]&lt;/span&gt;
&lt;span class="py"&gt;vendor&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"anthropic"&lt;/span&gt;
&lt;span class="py"&gt;key&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"env:ANTHROPIC_API_KEY"&lt;/span&gt;

&lt;span class="nn"&gt;[providers.gemini]&lt;/span&gt;
&lt;span class="py"&gt;vendor&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"gemini"&lt;/span&gt;
&lt;span class="py"&gt;key&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"env:GEMINI_API_KEY"&lt;/span&gt;

&lt;span class="nn"&gt;[routes.cheap-and-good]&lt;/span&gt;
&lt;span class="py"&gt;to&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"openai/gpt-5.5"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"anthropic/claude-sonnet-4-5-20250929"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"gemini/gemini-3.6-flash"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="py"&gt;pick&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"cheapest"&lt;/span&gt;
&lt;span class="py"&gt;needs&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"tools"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="py"&gt;tries&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;

&lt;span class="nn"&gt;[routes.fast]&lt;/span&gt;
&lt;span class="py"&gt;to&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"anthropic/claude-sonnet-4-5-20250929"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"openai/gpt-5.5"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="py"&gt;hedge&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"800ms"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A client asks for &lt;code&gt;cheap-and-good&lt;/code&gt; as if it were a model name. These are the documented behaviors that matter:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;pick&lt;/code&gt; sets the order candidates are tried in: first, cheapest (input price plus three times output price), fastest, weighted, or random.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;needs&lt;/code&gt; filters candidates to models that list capabilities like tools, reasoning, vision, or pdf.&lt;/li&gt;
&lt;li&gt;Fallback is on by default, so a failed candidate falls through to the next.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;tries&lt;/code&gt; allows 1 to 10 attempts per candidate. Backoff starts at 250ms, doubles each time, and caps at 10s.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;hedge&lt;/code&gt; races the candidates instead. Each starts after the delay, and the first answer wins.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Fallback models may differ in context length, tool support, or quality. Retries can waste tokens and add latency. Hedging deliberately sends more than one upstream request, which can increase cost.&lt;/p&gt;

&lt;p&gt;Run &lt;code&gt;respan-gateway check&lt;/code&gt; before starting (&lt;code&gt;--online&lt;/code&gt; also calls each provider and MCP server). You can reload without a restart by sending SIGHUP, starting with &lt;code&gt;--watch&lt;/code&gt;, or using the web UI. An invalid file is refused while the old config keeps serving. To see where a model would go and why, open &lt;code&gt;/ui/api/routes/explain?model=cheap-and-good&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Before deployment, run &lt;code&gt;respan-gateway check&lt;/code&gt; and inspect &lt;code&gt;/ui/api/routes/explain?model=cheap-and-good&lt;/code&gt; to confirm the configuration and routing behavior in your environment.&lt;/p&gt;

&lt;p&gt;For a failure test, configure an invalid key or unreachable provider as the first candidate, send a request, and inspect the request log to verify the actual retry and fallback behavior.&lt;/p&gt;

&lt;h2&gt;
  
  
  What It Can Proxy
&lt;/h2&gt;

&lt;p&gt;The documented scope is chat, images, speech (including transcription), video, and real-time voice. The docs give examples like the Anthropic SDK calling a GPT model, and an OpenAI Realtime client talking to Gemini Live. It handles tools, images, streaming, and reasoning parameters across formats. Embeddings aren’t on that list, so retrieval pipelines will still need a separate path.&lt;/p&gt;

&lt;p&gt;Sixteen providers are built in, including OpenAI, Anthropic, Gemini, Groq, DeepSeek, Mistral, xAI, OpenRouter, and Ollama. Any other OpenAI-, Anthropic-, or Gemini-style endpoint works with a &lt;code&gt;base_url&lt;/code&gt;. Translation has limits, and Respan itself says not every feature carries over between providers. Test the exact models and parameters you use.&lt;/p&gt;

&lt;h2&gt;
  
  
  MCP Tools Behind the Gateway
&lt;/h2&gt;

&lt;p&gt;Each &lt;code&gt;[mcp.NAME]&lt;/code&gt; block puts an MCP server behind the gateway, with its credentials staying on the gateway. The &lt;code&gt;tool_loop&lt;/code&gt; plugin lets any chat model use those tools mid-request. The gateway runs the tool, feeds the result back, and returns only the final answer, so the client needs no MCP support.&lt;/p&gt;

&lt;p&gt;This is powerful and worth treating carefully. By default, any client key can make the model call any tool you offer, using the server’s credentials. Use deny lists for destructive tools, and per-key &lt;code&gt;tools&lt;/code&gt; globs to limit what each client can reach.&lt;/p&gt;

&lt;h2&gt;
  
  
  Request Logs and Tracing
&lt;/h2&gt;

&lt;p&gt;The web UI shows each request’s provider, latency, tokens, and cost. By default, it keeps the last 1,000 requests in memory. The &lt;code&gt;request_log&lt;/code&gt; plugin can also append each finished request to a JSONL file, without bodies or keys.&lt;/p&gt;

&lt;p&gt;The optional &lt;code&gt;respan&lt;/code&gt; plugin sends each request to Respan as a trace. Its &lt;code&gt;content&lt;/code&gt; setting defaults to &lt;code&gt;true&lt;/code&gt;, meaning messages and replies are sent if your prompts contain sensitive data; set &lt;code&gt;content = false&lt;/code&gt; to send metadata only.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where It Fits
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;LiteLLM&lt;/strong&gt; is the established self-hosted option, and Respan’s own history is built on it. A newer pre-alpha project can’t match its track record, and Respan’s pitch is mainly about extensibility.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;OpenRouter&lt;/strong&gt; is a hosted router with one API and one bill. It isn’t a competitor in the self-hosted sense, since it’s one of the gateway’s built-in vendors and you can route to it from here.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Respan’s hosted platform&lt;/strong&gt; runs the gateway for you, adds observability and evals, and bills centrally. That is how the company makes money. The open-source binary is the self-run version, and you can combine them by adding a Respan key as a provider.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I’ve kept these comparisons to differences I could support. Check each project’s current docs before relying on them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Before Using It in Production
&lt;/h2&gt;

&lt;p&gt;Because it’s pre-alpha, I’d treat this as a proof of concept rather than a critical dependency for now. My checklist:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Stability:&lt;/strong&gt; Expect config and API changes. Pin versions and read release notes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Provider compatibility:&lt;/strong&gt; Test streaming, tool calls, structured output, images, and any provider-specific parameters your feature uses.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Failure behavior:&lt;/strong&gt; Simulate rate limits, timeouts, and bad keys. Confirm what retries, what falls back, and what happens when every candidate fails.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Latency:&lt;/strong&gt; Measure end-to-end with and without the gateway, including time to first token.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deployment:&lt;/strong&gt; There’s no official Docker image right now, and custom plugins mean a rebuild. Plan for how you’ll build and ship it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security:&lt;/strong&gt; Set &lt;code&gt;client_keys&lt;/code&gt; before exposing it beyond localhost (it refuses to start on a public address without them). Review the auth code you depend on. A commenter on the launch thread questioned the project’s MCP OAuth implementation, and Respan said it used Claude to generate code but reviewed every line.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;State:&lt;/strong&gt; Conversation state for translated Responses API clients lives in one gateway’s memory, so multi-instance setups need clients pinned to the right instance.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Privacy:&lt;/strong&gt; Decide what the request log and the &lt;code&gt;respan&lt;/code&gt; plugin may record.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Verdict
&lt;/h2&gt;

&lt;p&gt;Respan Gateway is worth a look if you want a small, extensible gateway with swappable providers and features as plugins, and you’re comfortable with compiled Rust extensions. It’s less suited to teams that need a drop-in container and a stable API today. Run it against your own models, break it on purpose, and then decide.&lt;/p&gt;

&lt;p&gt;To explore it yourself, start with the &lt;a href="https://github.com/respanai/respan-gateway" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt;, then the &lt;a href="https://www.respan.ai/docs/gateway/open-source/introduction" rel="noopener noreferrer"&gt;open-source gateway introduction&lt;/a&gt; and &lt;a href="https://www.respan.ai/docs/gateway/open-source/quickstart" rel="noopener noreferrer"&gt;quickstart&lt;/a&gt;. For how it relates to the hosted offering, see the &lt;a href="https://www.respan.ai/ai-gateway" rel="noopener noreferrer"&gt;Respan AI Gateway overview&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Frequently Asked Questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What is Respan Gateway?
&lt;/h3&gt;

&lt;p&gt;An open-source Rust LLM gateway with a small core in which providers, protocols, and features are plugins. It translates between API formats and supports routing, retries, fallbacks, MCP tools, and a request log.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is it open source?
&lt;/h3&gt;

&lt;p&gt;Yes, under your choice of MIT or Apache-2.0. Confirm against the repository’s license files.&lt;/p&gt;

&lt;h3&gt;
  
  
  How do I install it?
&lt;/h3&gt;

&lt;p&gt;Respan Gateway can be installed by downloading a binary from GitHub Releases, using &lt;code&gt;cargo install --git https://github.com/respanai/respan-gateway respan-gateway&lt;/code&gt;, or installing the Python package from PyPI with &lt;code&gt;pip install respan-gateway&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  What does it support?
&lt;/h3&gt;

&lt;p&gt;Chat, images, speech, video, and real-time voice across OpenAI, Anthropic, Gemini, and other providers, plus MCP tools. Embeddings aren’t supported.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is it production-ready?
&lt;/h3&gt;

&lt;p&gt;The project calls itself pre-alpha. Validate compatibility, failure handling, security, and deployment before relying on it.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>opensource</category>
      <category>discuss</category>
    </item>
  </channel>
</rss>
