<?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: you.bot</title>
    <description>The latest articles on DEV Community by you.bot (@youbot).</description>
    <link>https://dev.to/youbot</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%2F4040201%2F1b889a5a-d62c-4c5c-b18e-9f7e9689a99f.png</url>
      <title>DEV Community: you.bot</title>
      <link>https://dev.to/youbot</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/youbot"/>
    <language>en</language>
    <item>
      <title>Cut AI API Costs by Up to 80% with a Safe Provider Fallback</title>
      <dc:creator>you.bot</dc:creator>
      <pubDate>Wed, 29 Jul 2026 09:20:45 +0000</pubDate>
      <link>https://dev.to/youbot/cut-ai-api-costs-by-up-to-80-with-a-safe-provider-fallback-2npa</link>
      <guid>https://dev.to/youbot/cut-ai-api-costs-by-up-to-80-with-a-safe-provider-fallback-2npa</guid>
      <description>&lt;p&gt;Lower AI API prices are useful only when the routing design preserves predictable behavior.&lt;/p&gt;

&lt;p&gt;The practical pattern is:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;use the lower-cost route as primary;&lt;/li&gt;
&lt;li&gt;keep the existing provider integration as fallback;&lt;/li&gt;
&lt;li&gt;fall back only after a confirmed terminal outcome; and&lt;/li&gt;
&lt;li&gt;reconcile unknown task states instead of duplicating them.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;With you.bot, failed or resultless media tasks are refunded. For model configurations where its listed price is below the standard comparison price, a successful primary call costs less, while a refunded failure followed by fallback has the same model execution cost as calling the standard provider directly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Price snapshot: up to 80% less
&lt;/h2&gt;

&lt;p&gt;The largest differences are easiest to understand side by side. This selection includes both the highest-saving configurations and widely used text, image, and video models:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Model and configuration&lt;/th&gt;
&lt;th&gt;Standard comparison&lt;/th&gt;
&lt;th&gt;you.bot listed price&lt;/th&gt;
&lt;th&gt;Max savings with bonus&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Grok Imagine Video 1.5, image-to-video, 480p (per second)&lt;/td&gt;
&lt;td&gt;$0.0800&lt;/td&gt;
&lt;td&gt;$0.0178&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;80%&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GPT Image 2, text-to-image, 1K (per generation)&lt;/td&gt;
&lt;td&gt;$0.2190&lt;/td&gt;
&lt;td&gt;$0.0564&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;77%&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GPT-5.6 Luna, input (per 1M tokens)&lt;/td&gt;
&lt;td&gt;$1.0000&lt;/td&gt;
&lt;td&gt;$0.5097&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;54%&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Claude Sonnet 5, input (per 1M tokens)&lt;/td&gt;
&lt;td&gt;$3.0000&lt;/td&gt;
&lt;td&gt;$1.5367&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;53%&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gemini 3.6 Flash, input (per 1M tokens)&lt;/td&gt;
&lt;td&gt;$1.5000&lt;/td&gt;
&lt;td&gt;$0.7871&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;52%&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gemini Omni, 4s 4K video without video input (per generation)&lt;/td&gt;
&lt;td&gt;$1.8667&lt;/td&gt;
&lt;td&gt;$1.0565&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;49%&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;Max savings with bonus&lt;/code&gt; includes the qualifying 10% bonus on the $1,250 top-up. It is calculated as &lt;code&gt;1 - ((you.bot listed price / 1.10) / standard comparison price)&lt;/code&gt; and rounded to the nearest whole percent. Without that bonus, the corresponding base-price differences are 77.8%, 74.2%, 49.0%, 48.8%, 47.5%, and 43.4%.&lt;/p&gt;

&lt;p&gt;Prices are a point-in-time snapshot and vary by exact model, operation, resolution, duration, and billing unit. Check the &lt;a href="https://you.bot/pricing" rel="noopener noreferrer"&gt;current you.bot price table&lt;/a&gt; before making a routing decision.&lt;/p&gt;

&lt;h2&gt;
  
  
  The cost model
&lt;/h2&gt;

&lt;p&gt;Let:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Y = you.bot price
S = standard provider price
p = primary-route success rate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;route_first_cost = pY + (1 - p)S
direct_only_cost = S
savings = p(S - Y)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If &lt;code&gt;Y &amp;lt; S&lt;/code&gt;, the route-first architecture reduces blended spend whenever some requests succeed through the primary route.&lt;/p&gt;

&lt;p&gt;For example, the current GPT Image 2 snapshot lists 1K text-to-image generation at $0.0564 through you.bot and $0.219 as the standard comparison price, a 74.2% difference for that configuration.&lt;/p&gt;

&lt;p&gt;Do not generalize one row to the entire catalog. Compare the same model, operation, resolution, duration, and billing unit.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why a normal timeout is not a fallback signal
&lt;/h2&gt;

&lt;p&gt;Assume an application creates an asynchronous video task and waits 60 seconds. The client times out, but the task continues running. If the application immediately creates the same video with another provider, it can receive and pay for two outputs.&lt;/p&gt;

&lt;p&gt;The correct response to an unknown state is reconciliation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;FinalResult&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;completed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;outputUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;route&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;you.bot&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fallback&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;failed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;reason&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;PrimaryState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;completed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;outputUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;terminal_failure&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;reason&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;running&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;taskId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;unknown&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;taskId&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;generateWithSafeFallback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;GenerationInput&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;FinalResult&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;primary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;createWithYouBot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;primary&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;completed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;primary&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;route&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;you.bot&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;reconciled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="nx"&gt;primary&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;running&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;primary&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;unknown&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
      &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;reconcileYouBotTask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;primary&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;taskId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;primary&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;reconciled&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;completed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;reconciled&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;route&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;you.bot&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;reconciled&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;terminal_failure&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Primary execution state is unknown; fallback is not safe yet.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fallback&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;createWithExistingProvider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;fallback&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;route&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fallback&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The adapter functions above are intentionally separated. Each one should normalize provider-specific responses into a small internal state machine.&lt;/p&gt;

&lt;h2&gt;
  
  
  Persist state before polling
&lt;/h2&gt;

&lt;p&gt;Store at least:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;RoutedGeneration&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;localRequestId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;primaryTaskId&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;modelId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;primaryState&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;creating&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;running&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;completed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;terminal_failure&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;unknown&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;fallbackState&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;not_started&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;running&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;completed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;failed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;fallbackReason&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;primaryPriceUsd&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;fallbackPriceUsd&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;createdAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;updatedAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&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;Persist the primary task ID before the worker begins polling. That lets a replacement worker reconcile the original task instead of creating a new one after a crash.&lt;/p&gt;

&lt;h2&gt;
  
  
  Separate user latency from execution state
&lt;/h2&gt;

&lt;p&gt;A product can stop making the user wait without declaring the generation failed.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;after 45 seconds, return a pending response to the frontend;&lt;/li&gt;
&lt;li&gt;continue reconciliation in a background worker;&lt;/li&gt;
&lt;li&gt;notify the user through a webhook, WebSocket, or status page;&lt;/li&gt;
&lt;li&gt;use a longer execution deadline before considering manual intervention.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This keeps user experience responsive without producing duplicate billable work.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use documented failure classes
&lt;/h2&gt;

&lt;p&gt;Create an allowlist of outcomes that can trigger fallback. Examples might include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;task reached a documented terminal failed state;&lt;/li&gt;
&lt;li&gt;completed response contains no usable result and is refund-eligible;&lt;/li&gt;
&lt;li&gt;create request was definitively rejected before acceptance; or&lt;/li&gt;
&lt;li&gt;an operator has reconciled an otherwise unknown task.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Do not use a broad &lt;code&gt;catch&lt;/code&gt; block that sends every exception to the fallback provider.&lt;/p&gt;

&lt;p&gt;Authentication errors, invalid inputs, insufficient balance, and policy failures may also fail through the fallback route. Retrying those problems can add latency without improving completion.&lt;/p&gt;

&lt;h2&gt;
  
  
  Measure blended cost
&lt;/h2&gt;

&lt;p&gt;The metric that matters is not the advertised price in isolation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;blended cost per successful output =
  (primary charges + fallback charges) / successful outputs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Track it by exact model configuration together with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;primary completion rate;&lt;/li&gt;
&lt;li&gt;fallback rate;&lt;/li&gt;
&lt;li&gt;unknown-state rate;&lt;/li&gt;
&lt;li&gt;end-to-end latency;&lt;/li&gt;
&lt;li&gt;refunded-task reconciliation; and&lt;/li&gt;
&lt;li&gt;duplicate-output rate.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The goal is both economic and operational: capture the lower primary price while preserving an independent route for continuity.&lt;/p&gt;

&lt;p&gt;The current you.bot price table is available at &lt;a href="https://you.bot/pricing" rel="noopener noreferrer"&gt;https://you.bot/pricing&lt;/a&gt;, and its task lifecycle is documented at &lt;a href="https://you.bot/docs" rel="noopener noreferrer"&gt;https://you.bot/docs&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>api</category>
      <category>architecture</category>
      <category>devops</category>
    </item>
    <item>
      <title>What I learned building one API in front of 70+ AI models (LLM, image, video, music)</title>
      <dc:creator>you.bot</dc:creator>
      <pubDate>Tue, 21 Jul 2026 14:14:47 +0000</pubDate>
      <link>https://dev.to/youbot/what-i-learned-building-one-api-in-front-of-70-ai-models-llm-image-video-music-54e1</link>
      <guid>https://dev.to/youbot/what-i-learned-building-one-api-in-front-of-70-ai-models-llm-image-video-music-54e1</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Disclosure up front: I work on &lt;strong&gt;&lt;a href="https://you.bot" rel="noopener noreferrer"&gt;you.bot&lt;/a&gt;&lt;/strong&gt;, the gateway described here. This is a build-log about the engineering problems, not a sales pitch — the patterns apply whether you build your own gateway or use one.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The problem that started it
&lt;/h2&gt;

&lt;p&gt;Every AI feature I shipped came with its own tax. The LLM had one SDK and one dashboard. The image model had another. Video was a third provider with a totally different async flow. Music was a fourth. Four SDKs, four invoices, four sets of retry logic, four ways to handle failures.&lt;/p&gt;

&lt;p&gt;The moment I wanted to A/B two image models, or swap an LLM for a cheaper one, I was rewriting integration code and reconciling another bill. So we built a single endpoint that fronts 70+ models across text, image, video and music, where switching models is a one-line change. Here's what turned out to be hard.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Every provider has a different idea of "a request"
&lt;/h2&gt;

&lt;p&gt;LLMs are mostly request/response. Image is sometimes sync, sometimes not. Video is almost always a long-running job — you submit, you poll or wait for a webhook, and it can take minutes. Music (like &lt;a href="https://you.bot/models/suno-v4-5" rel="noopener noreferrer"&gt;Suno&lt;/a&gt;) is the same: you don't get a song back on the open socket.&lt;/p&gt;

&lt;p&gt;If you expose these differences to the caller, you've moved the complexity onto them. The decision that mattered most: &lt;strong&gt;normalize everything to one create-then-poll task shape.&lt;/strong&gt; You create a task, then poll it until it succeeds (text models return their result inline in the create response, so you can skip the poll):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;POST https://you.bot/api/v1/generate
Authorization: Bearer YOUR_API_KEY

{ "modelId": "suno-v4-5",
  "input": { "prompt": "lofi beat, rainy night", "title": "Rainy Lofi", "style": "lofi, chill" } }

→ { "taskId": "t_abc", "creditsCharged": 13 }
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;GET https://you.bot/api/v1/task/t_abc?model=suno-v4-5

→ { "state": "success", "resultUrls": ["https://…"] }
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Same two-call contract for a fast LLM call and a three-minute video render. Callers can poll, or pass a &lt;code&gt;callbackUrl&lt;/code&gt; and get a signed webhook on completion. Switching from music to video (&lt;a href="https://you.bot/models/kling-3-0" rel="noopener noreferrer"&gt;Kling&lt;/a&gt;) or image (&lt;a href="https://you.bot/models/gpt-image-2-text-to-image" rel="noopener noreferrer"&gt;GPT Image 2&lt;/a&gt;) is just a different &lt;code&gt;modelId&lt;/code&gt; and a different &lt;code&gt;input&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. The modelId is the only thing that should change
&lt;/h2&gt;

&lt;p&gt;The whole point is that trying a new model shouldn't be a project. The surface a caller touches is deliberately tiny: one endpoint, one &lt;code&gt;Authorization&lt;/code&gt; header, and a &lt;code&gt;modelId&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

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

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;generate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;inp&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;post&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://you.bot/api/v1/generate&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;Authorization&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Bearer &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;API_KEY&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;modelId&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;model_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;input&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;inp&lt;/span&gt;&lt;span class="p"&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="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;   &lt;span class="c1"&gt;# -&amp;gt; {"taskId": "...", "creditsCharged": ...}
&lt;/span&gt;
&lt;span class="c1"&gt;# swap models by changing one field
# (input fields vary per model — each model page documents its own)
&lt;/span&gt;&lt;span class="nf"&gt;generate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gemini-3-1-pro&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;prompt&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;...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="nf"&gt;generate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;nano-banana-pro&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;prompt&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;...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="nf"&gt;generate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;kling-3-0&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;prompt&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;...&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;Every model has a page with its own inputs, price and an in-browser playground (e.g. &lt;a href="https://you.bot/models/gemini-3-1-pro" rel="noopener noreferrer"&gt;Gemini 3.1 Pro&lt;/a&gt;, &lt;a href="https://you.bot/models/nano-banana-pro" rel="noopener noreferrer"&gt;Nano Banana Pro&lt;/a&gt;) so you can test with your own prompt before writing a line of integration code. That "try before you wire it up" step removed most of our support questions.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Only charging for successful output changes your whole billing model
&lt;/h2&gt;

&lt;p&gt;If a generation fails, errors, or comes back empty, the user should not pay for it. Sounds obvious; it's annoying to implement because "success" is defined differently per provider, and refunds have to be idempotent so a retried webhook doesn't double-credit.&lt;/p&gt;

&lt;p&gt;We ended up with a per-model success predicate and a ledger where every debit can be reversed by task id. Credits come from one prepaid wallet shared across all models (the create response tells you &lt;code&gt;creditsCharged&lt;/code&gt; per call), so there's no per-provider balance to juggle — and because you only pay on success, the effective price drops below the sticker price.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Reliability: keep the caller's existing key as a fallback
&lt;/h2&gt;

&lt;p&gt;The honest objection to any gateway is "now you're a single point of failure between me and the model." The pattern that made people comfortable: &lt;strong&gt;let the app keep its existing provider key as a fallback route.&lt;/strong&gt; Primary traffic goes through the gateway; if a call fails, it falls back to the provider directly. Multi-provider routing targets high uptime, but the caller never bets reliability on us alone.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Make the docs machine-readable
&lt;/h2&gt;

&lt;p&gt;A lot of "how do I call X model" questions now start in ChatGPT or Perplexity, not Google. So every model page also ships a plain-markdown version (append &lt;code&gt;/md&lt;/code&gt; to the URL, e.g. &lt;code&gt;/models/suno-v4-5/md&lt;/code&gt;) that an LLM can read cleanly. If assistants are going to answer developer questions anyway, the least you can do is give them accurate, structured source material.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd tell my past self
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Normalize to one create-then-poll task shape early. Retrofitting it later is painful.&lt;/li&gt;
&lt;li&gt;Define "success" per provider before you promise charge-on-success billing.&lt;/li&gt;
&lt;li&gt;Make refunds idempotent from day one.&lt;/li&gt;
&lt;li&gt;A playground per model is worth more than another paragraph of docs.&lt;/li&gt;
&lt;li&gt;Assume an LLM will read your docs before a human does.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The model catalog with per-model pricing and playgrounds is at &lt;a href="https://you.bot/market" rel="noopener noreferrer"&gt;you.bot/market&lt;/a&gt;. Happy to answer architecture questions in the comments — especially on fallback-routing and charge-on-success, since those drove the most debate on our side.&lt;/p&gt;

</description>
      <category>api</category>
      <category>ai</category>
      <category>webdev</category>
      <category>architecture</category>
    </item>
  </channel>
</rss>
