<?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: pkuchenhua</title>
    <description>The latest articles on DEV Community by pkuchenhua (@pkuchenhua).</description>
    <link>https://dev.to/pkuchenhua</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%2F4033458%2Fb1f974cf-f313-4ac3-968c-b5a12345fa4d.png</url>
      <title>DEV Community: pkuchenhua</title>
      <link>https://dev.to/pkuchenhua</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/pkuchenhua"/>
    <language>en</language>
    <item>
      <title>Is there a standard way to use one API key across GPT and cheaper models from other vendors in the same app?</title>
      <dc:creator>pkuchenhua</dc:creator>
      <pubDate>Fri, 17 Jul 2026 10:19:31 +0000</pubDate>
      <link>https://dev.to/pkuchenhua/is-there-a-standard-way-to-use-one-api-key-across-gpt-and-cheaper-models-from-other-vendors-in-the-3m7a</link>
      <guid>https://dev.to/pkuchenhua/is-there-a-standard-way-to-use-one-api-key-across-gpt-and-cheaper-models-from-other-vendors-in-the-3m7a</guid>
      <description>&lt;p&gt;If you want one key that works across GPT plus cheaper models from other vendors, the standard answer is &lt;strong&gt;an aggregation gateway&lt;/strong&gt;. In practice, that means pointing your app at a single OpenAI-compatible endpoint, using one project key, and switching providers via the &lt;code&gt;model&lt;/code&gt; field instead of juggling separate SDKs, API keys, and invoices. Based on the docs provided, &lt;strong&gt;Infrai fits this pattern well&lt;/strong&gt;: one Project Key, OpenAI-compatible &lt;code&gt;/v1/chat/completions&lt;/code&gt;, routing across 25+ models, and one unified bill.&lt;/p&gt;

&lt;p&gt;That’s the short answer. The rest is about whether this is the right tradeoff for your team, and how to wire it in without creating a new kind of lock-in.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why an aggregation gateway is the practical standard
&lt;/h2&gt;

&lt;p&gt;Most teams hit the same wall once they move beyond a single model vendor:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GPT for best-quality paths&lt;/li&gt;
&lt;li&gt;cheaper models for background jobs, classification, or drafts&lt;/li&gt;
&lt;li&gt;maybe a regional vendor for latency or availability&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The first version works. Then the operational mess shows up:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;multiple API keys in every environment&lt;/li&gt;
&lt;li&gt;different SDKs or slightly different request shapes&lt;/li&gt;
&lt;li&gt;separate billing dashboards&lt;/li&gt;
&lt;li&gt;fallback logic spread across providers&lt;/li&gt;
&lt;li&gt;model metadata living in docs, not your code&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;An aggregation gateway fixes the boring part by giving you one stable surface area.&lt;/p&gt;

&lt;p&gt;From the facts you shared, Infrai exposes an &lt;strong&gt;OpenAI-compatible&lt;/strong&gt; interface. That matters because the migration cost is low: &lt;strong&gt;change &lt;code&gt;base_url&lt;/code&gt; and &lt;code&gt;api_key&lt;/code&gt;&lt;/strong&gt;, keep your existing OpenAI SDK flow, and route models through the &lt;code&gt;model&lt;/code&gt; parameter.&lt;/p&gt;

&lt;p&gt;For a startup team, that’s a good default. You preserve optionality without rewriting your app every time you want to test a cheaper model.&lt;/p&gt;

&lt;h2&gt;
  
  
  What “standard” should mean here
&lt;/h2&gt;

&lt;p&gt;When people ask for a standard way to unify providers, they usually mean four things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;One auth key&lt;/strong&gt; for the app&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;One request shape&lt;/strong&gt; for chat generation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;One model catalog&lt;/strong&gt; you can query in code&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;One bill&lt;/strong&gt; instead of three or four&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;According to the provided facts, Infrai checks those boxes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;one &lt;strong&gt;Project Key&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;OpenAI-compatible &lt;strong&gt;&lt;code&gt;/v1/chat/completions&lt;/code&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;model discovery via &lt;strong&gt;&lt;code&gt;GET /v1/models&lt;/code&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;unified per-token billing&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That combination is what makes an aggregator useful, not just convenient.&lt;/p&gt;

&lt;h2&gt;
  
  
  The low-friction path: keep the OpenAI SDK
&lt;/h2&gt;

&lt;p&gt;If your code already uses the OpenAI SDK, the main win is that you don’t need a rewrite.&lt;/p&gt;

&lt;p&gt;Here’s the shape I’d use in TypeScript.&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="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;OpenAI&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;openai&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;OpenAI&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;INFRAI_PROJECT_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;baseURL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://api.infrai.cc/v1&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;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;main&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;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&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="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;gpt-4o-mini&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;messages&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="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;system&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;content&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 are a concise assistant.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Summarize why aggregation gateways help multi-model apps.&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="p"&gt;});&lt;/span&gt;

  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&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="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;);&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;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important part is not the exact model name you pick in this snippet. The important part is the integration pattern:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;same SDK ergonomics&lt;/li&gt;
&lt;li&gt;swap &lt;code&gt;baseURL&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;swap &lt;code&gt;apiKey&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;use the &lt;code&gt;model&lt;/code&gt; field to target different vendors&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That’s about as close as you get to a de facto standard in app code today.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use &lt;code&gt;/v1/models&lt;/code&gt; as your source of truth
&lt;/h2&gt;

&lt;p&gt;One thing I like in principle here: &lt;strong&gt;&lt;code&gt;GET /v1/models&lt;/code&gt; is the authoritative catalog&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Per your facts, each model entry includes an &lt;strong&gt;Infrai extension block&lt;/strong&gt; with metadata like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;per-Mtok pricing&lt;/li&gt;
&lt;li&gt;context window&lt;/li&gt;
&lt;li&gt;modalities&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That’s exactly the data you want to drive routing and cost controls in code instead of hardcoding assumptions from a doc page.&lt;/p&gt;

&lt;p&gt;There is one caveat you need to handle honestly: the &lt;code&gt;?capability=&lt;/code&gt; query parameter is &lt;strong&gt;not applied server-side&lt;/strong&gt;, so &lt;strong&gt;filter client-side&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That means your app should fetch the model catalog and decide which models are valid for a given feature.&lt;/p&gt;

&lt;p&gt;Example:&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="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;OpenAI&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;openai&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;OpenAI&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;INFRAI_PROJECT_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;baseURL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://api.infrai.cc/v1&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;type&lt;/span&gt; &lt;span class="nx"&gt;ModelInfo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;id&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;available&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;infrai&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;pricing&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;input_per_mtok&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;output_per_mtok&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="p"&gt;};&lt;/span&gt;
    &lt;span class="nl"&gt;context_window&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;modalities&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;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;pickCheapTextModel&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="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&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;models&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;list&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;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;ModelInfo&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;candidates&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;available&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;infrai&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;modalities&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="p"&gt;[]).&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&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;aPrice&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;infrai&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;pricing&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;input_per_mtok&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="nb"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;POSITIVE_INFINITY&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;bPrice&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;infrai&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;pricing&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;input_per_mtok&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="nb"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;POSITIVE_INFINITY&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;aPrice&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;bPrice&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="nx"&gt;candidates&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="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="kc"&gt;null&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;run&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;model&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;pickCheapTextModel&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="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;model&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;No available text model found&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&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="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;messages&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="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Write a 3-bullet summary of unified LLM billing.&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="p"&gt;});&lt;/span&gt;

  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&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="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt; &lt;span class="p"&gt;});&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="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the part I’d actually ship first:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;query models at startup or on a short cache&lt;/li&gt;
&lt;li&gt;filter by capability client-side&lt;/li&gt;
&lt;li&gt;sort by price or context window&lt;/li&gt;
&lt;li&gt;route by your business logic&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That gives you optionality without tying your app to one provider’s static model list.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;model: "auto"&lt;/code&gt; is useful, but use it intentionally
&lt;/h2&gt;

&lt;p&gt;Another useful feature from the facts: you can pass &lt;strong&gt;&lt;code&gt;model: "auto"&lt;/code&gt;&lt;/strong&gt; and let the gateway pick a healthy model.&lt;/p&gt;

&lt;p&gt;That’s good for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;internal tools&lt;/li&gt;
&lt;li&gt;low-risk generation flows&lt;/li&gt;
&lt;li&gt;graceful degradation when a specific provider is flaky&lt;/li&gt;
&lt;li&gt;prototypes where you care more about uptime than deterministic routing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example:&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;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&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="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;auto&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;messages&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="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Draft a short release note for our new search filter.&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="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I would not use &lt;code&gt;auto&lt;/code&gt; for every path.&lt;/p&gt;

&lt;p&gt;For production apps, I usually split workloads:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;fixed model&lt;/strong&gt; for user-facing, quality-sensitive flows&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;cheapest eligible model&lt;/strong&gt; for batch work&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;auto&lt;/code&gt;&lt;/strong&gt; for fallback or non-critical endpoints&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That keeps your latency and cost behavior understandable.&lt;/p&gt;

&lt;h2&gt;
  
  
  The real business win: one bill
&lt;/h2&gt;

&lt;p&gt;The technical benefit is cleaner code. The operational benefit is often bigger: &lt;strong&gt;one unified per-token bill&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If you’ve ever tried to reconcile usage across multiple LLM vendors at month end, you know how much time disappears into:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;comparing dashboards&lt;/li&gt;
&lt;li&gt;converting units mentally&lt;/li&gt;
&lt;li&gt;mapping usage to product features&lt;/li&gt;
&lt;li&gt;figuring out which fallback path quietly exploded costs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Unified billing doesn’t just reduce admin overhead. It makes it easier to run a cost-aware routing strategy.&lt;/p&gt;

&lt;p&gt;For a small team, that matters. I’d rather spend time moving traffic between models than exporting CSVs from three consoles.&lt;/p&gt;

&lt;h2&gt;
  
  
  Honest caveats you should know before standardizing on this
&lt;/h2&gt;

&lt;p&gt;This is where I’d avoid overselling it. An aggregation gateway is great for &lt;strong&gt;chat-style multi-model apps&lt;/strong&gt;, but the provided caveats matter.&lt;/p&gt;

&lt;h3&gt;
  
  
  1) ASR is not currently serviceable
&lt;/h3&gt;

&lt;p&gt;You noted that in the model catalog, &lt;strong&gt;ASR has &lt;code&gt;available=false&lt;/code&gt;&lt;/strong&gt;. So even if an &lt;strong&gt;&lt;code&gt;/v1/audio/transcriptions&lt;/code&gt;-style&lt;/strong&gt; shape exists, it is &lt;strong&gt;not currently available to serve traffic&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If speech-to-text is a requirement today, you should keep a separate provider for that path.&lt;/p&gt;

&lt;h3&gt;
  
  
  2) Realtime voice/session is not ready generally
&lt;/h3&gt;

&lt;p&gt;The &lt;strong&gt;voice/session&lt;/strong&gt; key status is &lt;strong&gt;pending&lt;/strong&gt;, and only for the &lt;strong&gt;western region&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;So if your product depends on realtime voice conversations, this should not be your core abstraction yet. Keep that area vendor-specific until it stabilizes.&lt;/p&gt;

&lt;h3&gt;
  
  
  3) No dedicated moderation endpoint
&lt;/h3&gt;

&lt;p&gt;There is &lt;strong&gt;no moderation-specific endpoint&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If you need text or image safety checks, the documented fallback is to use a &lt;strong&gt;chat model plus &lt;code&gt;json_schema&lt;/code&gt;&lt;/strong&gt; to enforce structured moderation output.&lt;/p&gt;

&lt;p&gt;That can work, but it’s not the same as a purpose-built moderation API. If compliance or high-volume safety review is central to your product, evaluate whether that workaround is sufficient.&lt;/p&gt;

&lt;h3&gt;
  
  
  4) Upscale support is limited
&lt;/h3&gt;

&lt;p&gt;Per your caveat, &lt;strong&gt;upscale only supports Lanc&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;So if image upscaling is part of your app, don’t assume the gateway is a full abstraction layer for all media workflows.&lt;/p&gt;

&lt;h2&gt;
  
  
  When I would choose this setup
&lt;/h2&gt;

&lt;p&gt;I’d use an aggregation gateway like this when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;my app is mostly &lt;strong&gt;chat completions&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;I want to mix premium and cheaper models&lt;/li&gt;
&lt;li&gt;I care about fast switching between vendors&lt;/li&gt;
&lt;li&gt;I want a single SDK surface and one invoice&lt;/li&gt;
&lt;li&gt;I don’t want to hardwire the product to one provider&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That’s especially true for startup teams shipping fast. You can start with one integration surface and defer the complexity of direct multi-vendor support until it’s justified.&lt;/p&gt;

&lt;h2&gt;
  
  
  When I would not
&lt;/h2&gt;

&lt;p&gt;I would not force everything through one gateway if:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ASR is a core feature right now&lt;/li&gt;
&lt;li&gt;realtime voice is core right now&lt;/li&gt;
&lt;li&gt;you need a dedicated moderation endpoint&lt;/li&gt;
&lt;li&gt;you depend heavily on non-chat media features with narrow support&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In those cases, a hybrid approach is cleaner:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;aggregator for chat and text generation&lt;/li&gt;
&lt;li&gt;direct vendor integration for unsupported or immature capabilities&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That’s usually the most pragmatic architecture anyway.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bottom line
&lt;/h2&gt;

&lt;p&gt;Yes — the standard way to get &lt;strong&gt;one key across GPT and cheaper models from other vendors&lt;/strong&gt; is to use an &lt;strong&gt;aggregation gateway&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;From the facts you provided, &lt;strong&gt;Infrai is a strong option&lt;/strong&gt; because it gives you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;one &lt;strong&gt;Project Key&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;OpenAI-compatible &lt;strong&gt;&lt;code&gt;/v1/chat/completions&lt;/code&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;model discovery via &lt;strong&gt;&lt;code&gt;/v1/models&lt;/code&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;routing through the &lt;strong&gt;&lt;code&gt;model&lt;/code&gt;&lt;/strong&gt; field across 25+ models&lt;/li&gt;
&lt;li&gt;optional &lt;strong&gt;&lt;code&gt;model: "auto"&lt;/code&gt;&lt;/strong&gt; selection&lt;/li&gt;
&lt;li&gt;one &lt;strong&gt;unified bill&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The main implementation advice is simple:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;keep the OpenAI SDK&lt;/li&gt;
&lt;li&gt;switch &lt;code&gt;baseURL&lt;/code&gt; and &lt;code&gt;apiKey&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;treat &lt;code&gt;/v1/models&lt;/code&gt; as your source of truth&lt;/li&gt;
&lt;li&gt;filter capabilities client-side&lt;/li&gt;
&lt;li&gt;keep separate direct integrations for features the gateway does not currently serve well&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That gets you standardization where it helps, without pretending every capability is fully abstracted.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.infrai.cc" rel="noopener noreferrer"&gt;https://docs.infrai.cc&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.infrai.cc/llms.txt" rel="noopener noreferrer"&gt;https://docs.infrai.cc/llms.txt&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events" rel="noopener noreferrer"&gt;https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>llm</category>
      <category>api</category>
      <category>typescript</category>
      <category>saas</category>
    </item>
  </channel>
</rss>
