<?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: skedaddle</title>
    <description>The latest articles on DEV Community by skedaddle (@skedaddle).</description>
    <link>https://dev.to/skedaddle</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%2F3930342%2F9a034ca5-8f60-4f18-a671-86567eebb318.png</url>
      <title>DEV Community: skedaddle</title>
      <link>https://dev.to/skedaddle</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/skedaddle"/>
    <language>en</language>
    <item>
      <title>How to Self-Host an AI API Gateway With Sub2API</title>
      <dc:creator>skedaddle</dc:creator>
      <pubDate>Sun, 26 Jul 2026 14:16:15 +0000</pubDate>
      <link>https://dev.to/skedaddle/how-to-self-host-an-ai-api-gateway-with-sub2api-3i99</link>
      <guid>https://dev.to/skedaddle/how-to-self-host-an-ai-api-gateway-with-sub2api-3i99</guid>
      <description>&lt;p&gt;Sub2API can put one Base URL in front of several AI providers and accounts. Your clients use keys that you issue, while the gateway handles authentication, routing, usage accounting, and concurrency limits.&lt;/p&gt;

&lt;p&gt;Getting the containers online is the easy part. Once the gateway is running, you also own its database, Redis instance, TLS endpoint, credentials, logs, backups, and upgrades.&lt;/p&gt;

&lt;p&gt;This guide builds the smallest useful private setup and shows what to verify before giving anyone else access.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Sub2API adds
&lt;/h2&gt;

&lt;p&gt;The request path looks like this:&lt;/p&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%2F4b1m38jmz4h69spm8vky.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%2F4b1m38jmz4h69spm8vky.png" alt="Diagram showing Claude Code, Codex, Cursor, and an app sending requests through a self-hosted Sub2API gateway, which handles authentication, billing, routing, and logs before forwarding requests to multiple AI providers." width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Sub2API currently supports multiple upstream account types, platform API keys, token-level usage accounting, sticky-session scheduling, per-user and per-account concurrency controls, rate limits, composite groups, and an admin dashboard.&lt;/p&gt;

&lt;p&gt;This does not make you independent of the upstream provider. If an upstream account is suspended, overloaded, or incompatible with a request, the gateway cannot manufacture capacity or model access.&lt;/p&gt;

&lt;p&gt;The project README also warns that distributing quota from AI subscriptions may violate upstream terms of service. Keep the first deployment private, use accounts you are authorized to use, and review the provider terms before sharing or selling access.&lt;/p&gt;

&lt;h2&gt;
  
  
  Should you self-host it?
&lt;/h2&gt;

&lt;p&gt;Sub2API is useful when you need to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;keep upstream credentials off developer machines;&lt;/li&gt;
&lt;li&gt;issue separate, revocable keys to users or projects;&lt;/li&gt;
&lt;li&gt;set budgets, rate limits, and concurrency limits centrally;&lt;/li&gt;
&lt;li&gt;switch an upstream without reconfiguring every client;&lt;/li&gt;
&lt;li&gt;see usage and gateway failures in your own dashboard.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If one person calls one official API, adding a gateway usually makes the system more fragile. Self-hosting starts to pay off when access management and routing are already becoming a coordination problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deploy the smallest useful setup
&lt;/h2&gt;

&lt;p&gt;For the Docker path, you need a Linux server, Docker 20.10 or newer, Docker Compose v2, a domain, and at least one authorized upstream account or API key. PostgreSQL and Redis are included in the official Compose stack.&lt;/p&gt;

&lt;p&gt;The project provides a deployment preparation script. Download it first so you can inspect what it will run:&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;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; sub2api-deploy
&lt;span class="nb"&gt;cd &lt;/span&gt;sub2api-deploy

curl &lt;span class="nt"&gt;-fsSLo&lt;/span&gt; docker-deploy.sh &lt;span class="se"&gt;\&lt;/span&gt;
  https://raw.githubusercontent.com/Wei-Shaw/sub2api/main/deploy/docker-deploy.sh

less docker-deploy.sh
&lt;span class="nb"&gt;chmod&lt;/span&gt; +x docker-deploy.sh
./docker-deploy.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The script saves the Compose definition as &lt;code&gt;docker-compose.yml&lt;/code&gt;, creates &lt;code&gt;.env&lt;/code&gt;, generates the PostgreSQL password and application secrets, and prepares local data directories. It prints the generated credentials to the terminal, so do not paste that output into an issue or chat.&lt;/p&gt;

&lt;p&gt;Before starting the stack, edit &lt;code&gt;.env&lt;/code&gt;. If Nginx or Caddy will run on the same server, bind Sub2API to loopback so port 8080 is not exposed directly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;BIND_HOST=127.0.0.1
SERVER_PORT=8080
ADMIN_EMAIL=you@example.com
ADMIN_PASSWORD=replace-with-a-generated-password
TZ=UTC
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Keep the generated &lt;code&gt;JWT_SECRET&lt;/code&gt;, &lt;code&gt;TOTP_ENCRYPTION_KEY&lt;/code&gt;, and &lt;code&gt;POSTGRES_PASSWORD&lt;/code&gt; stable. Changing them later can invalidate sessions, break existing 2FA configuration, or disconnect the database.&lt;/p&gt;

&lt;p&gt;Start the services and check their status:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker compose up &lt;span class="nt"&gt;-d&lt;/span&gt;
docker compose ps
docker compose logs &lt;span class="nt"&gt;-f&lt;/span&gt; sub2api
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In another shell, check the local health endpoint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-i&lt;/span&gt; http://127.0.0.1:8080/health
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Confirm that the Sub2API, PostgreSQL, and Redis containers are healthy. Seeing the login page is not enough to prove that the dependencies are working.&lt;/p&gt;

&lt;h2&gt;
  
  
  Put HTTPS in front of it
&lt;/h2&gt;

&lt;p&gt;Use a domain and HTTPS for the public Base URL. Sub2API handles long-lived SSE and WebSocket traffic, so the reverse proxy must avoid buffering those responses or closing them too early.&lt;/p&gt;

&lt;p&gt;The relevant part of an Nginx configuration is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nginx"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Place these directives in the http block.&lt;/span&gt;
&lt;span class="k"&gt;underscores_in_headers&lt;/span&gt; &lt;span class="no"&gt;on&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;map&lt;/span&gt; &lt;span class="nv"&gt;$http_upgrade&lt;/span&gt; &lt;span class="nv"&gt;$connection_upgrade&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;default&lt;/span&gt; &lt;span class="s"&gt;upgrade&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;''&lt;/span&gt;      &lt;span class="s"&gt;close&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;server&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;listen&lt;/span&gt; &lt;span class="mi"&gt;443&lt;/span&gt; &lt;span class="s"&gt;ssl&lt;/span&gt; &lt;span class="s"&gt;http2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;server_name&lt;/span&gt; &lt;span class="s"&gt;api.example.com&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;# Add your TLS certificate configuration here.&lt;/span&gt;

    &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="n"&gt;/&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_pass&lt;/span&gt; &lt;span class="s"&gt;http://127.0.0.1:8080&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_http_version&lt;/span&gt; &lt;span class="mf"&gt;1.1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;Host&lt;/span&gt; &lt;span class="nv"&gt;$host&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;X-Real-IP&lt;/span&gt; &lt;span class="nv"&gt;$remote_addr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;X-Forwarded-For&lt;/span&gt; &lt;span class="nv"&gt;$remote_addr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;X-Forwarded-Proto&lt;/span&gt; &lt;span class="nv"&gt;$scheme&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;Upgrade&lt;/span&gt; &lt;span class="nv"&gt;$http_upgrade&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;Connection&lt;/span&gt; &lt;span class="nv"&gt;$connection_upgrade&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="kn"&gt;proxy_buffering&lt;/span&gt; &lt;span class="no"&gt;off&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_request_buffering&lt;/span&gt; &lt;span class="no"&gt;off&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_read_timeout&lt;/span&gt; &lt;span class="s"&gt;1800s&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_send_timeout&lt;/span&gt; &lt;span class="s"&gt;1800s&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;&lt;code&gt;underscores_in_headers on;&lt;/code&gt; matters because Nginx otherwise drops headers such as &lt;code&gt;session_id&lt;/code&gt;, which Sub2API uses for sticky-session routing.&lt;/p&gt;

&lt;p&gt;If you put a CDN in front of the server, restrict origin access and configure trusted proxy ranges instead of accepting forwarded IP headers from any client. The project's &lt;a href="https://github.com/Wei-Shaw/sub2api/blob/main/deploy/EDGE_SECURITY.md" rel="noopener noreferrer"&gt;edge security guide&lt;/a&gt; covers that setup in more detail.&lt;/p&gt;

&lt;p&gt;Verify the public route separately:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-i&lt;/span&gt; https://api.example.com/health
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Add one upstream and one client key
&lt;/h2&gt;

&lt;p&gt;Start with one route you can understand end to end. The dashboard labels may change between releases, but the setup is roughly:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Add one upstream account or API key in &lt;strong&gt;Account Management&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Create a group and attach the account to it.&lt;/li&gt;
&lt;li&gt;Expose one model alias through that group.&lt;/li&gt;
&lt;li&gt;Create a non-admin test user with a small balance or quota.&lt;/li&gt;
&lt;li&gt;Create a Sub2API key for that user and restrict it to the group.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;There are two different credentials in this setup:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Upstream credential  -&amp;gt; Sub2API uses it to call the provider
Sub2API key          -&amp;gt; your client uses it to call the gateway
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Give clients the second key. They should not need the upstream credential.&lt;/p&gt;

&lt;h2&gt;
  
  
  Verify more than a 200 response
&lt;/h2&gt;

&lt;p&gt;For an OpenAI Responses-compatible route, make a small request with the test user's key:&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_BASE_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"https://api.example.com/v1"&lt;/span&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;"your-sub2api-key"&lt;/span&gt;

curl &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$OPENAI_BASE_URL&lt;/span&gt;&lt;span class="s2"&gt;/responses"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$OPENAI_API_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "model": "your-model-alias",
    "input": "Return exactly: gateway-ok"
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Claude Code commonly uses:&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;ANTHROPIC_BASE_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"https://api.example.com"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;ANTHROPIC_AUTH_TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"your-sub2api-key"&lt;/span&gt;
claude
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For Codex, start with a clean shell:&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_BASE_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"https://api.example.com/v1"&lt;/span&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;"your-sub2api-key"&lt;/span&gt;
codex
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After the first request, open the dashboard and check the record. Confirm that the expected user, key, group, account, model, token count, and charge were used.&lt;/p&gt;

&lt;p&gt;Then test the behavior your real client depends on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;streaming and long responses;&lt;/li&gt;
&lt;li&gt;tool calls;&lt;/li&gt;
&lt;li&gt;model alias mapping;&lt;/li&gt;
&lt;li&gt;usage and cache accounting;&lt;/li&gt;
&lt;li&gt;concurrency limits;&lt;/li&gt;
&lt;li&gt;upstream error propagation.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Also try an invalid key, a user with no remaining budget, and an unavailable upstream. These tests tell you whether access stops cleanly and whether a failure can silently reach the wrong model.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep it maintainable
&lt;/h2&gt;

&lt;p&gt;Before using the gateway for regular work:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Pin the Sub2API image tag you tested instead of upgrading &lt;code&gt;latest&lt;/code&gt; unattended.&lt;/li&gt;
&lt;li&gt;Keep the admin account separate from API use and enable 2FA.&lt;/li&gt;
&lt;li&gt;Rotate a user's Sub2API key when that user or project no longer needs access.&lt;/li&gt;
&lt;li&gt;Monitor &lt;code&gt;/health&lt;/code&gt;, container restarts, disk space, upstream errors, latency, and certificate expiry.&lt;/li&gt;
&lt;li&gt;Back up &lt;code&gt;.env&lt;/code&gt;, the Compose file, application data, and PostgreSQL.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Do not copy a live PostgreSQL data directory and assume it is consistent. Use &lt;code&gt;pg_dump&lt;/code&gt;, a database-aware storage snapshot, or stop the stack before taking a filesystem archive. Encrypt backups and test a restore on another machine.&lt;/p&gt;

&lt;h2&gt;
  
  
  Self-host or let someone else run it
&lt;/h2&gt;

&lt;p&gt;Sub2API is a reasonable choice when you want to own the gateway policy and are willing to operate the supporting infrastructure.&lt;/p&gt;

&lt;p&gt;If you would rather not maintain PostgreSQL, Redis, TLS, backups, security, and upstream accounts, do not self-host merely because the code is available. You can compare existing AI API relay services, including services built with Sub2API, on &lt;a href="https://ccnavx.com/directory/sub2api-proxy" rel="noopener noreferrer"&gt;CCNavX&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/Wei-Shaw/sub2api" rel="noopener noreferrer"&gt;Sub2API repository and project notice&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Wei-Shaw/sub2api/blob/main/deploy/README.md" rel="noopener noreferrer"&gt;Official Sub2API deployment documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Wei-Shaw/sub2api/blob/main/deploy/EDGE_SECURITY.md" rel="noopener noreferrer"&gt;Official edge and HTTP ingress security guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Wei-Shaw/sub2api/releases" rel="noopener noreferrer"&gt;Sub2API releases&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>apigateway</category>
      <category>selfhosted</category>
      <category>opensource</category>
    </item>
    <item>
      <title>I Built an AI API Directory Because OpenAI-Compatible Is Not Enough</title>
      <dc:creator>skedaddle</dc:creator>
      <pubDate>Thu, 14 May 2026 03:19:13 +0000</pubDate>
      <link>https://dev.to/skedaddle/i-built-an-ai-api-directory-because-openai-compatible-is-not-enough-37l7</link>
      <guid>https://dev.to/skedaddle/i-built-an-ai-api-directory-because-openai-compatible-is-not-enough-37l7</guid>
      <description>&lt;p&gt;Developers do not need another “best AI API provider” list.&lt;/p&gt;

&lt;p&gt;Most lists collapse into the same problem: a few affiliate links, some vague pricing claims, and no clear way to verify whether a provider actually supports the model, Base URL pattern, payment method, or billing behavior a real project needs.&lt;/p&gt;

&lt;p&gt;So I built a more boring thing: an AI API directory.&lt;/p&gt;

&lt;p&gt;Not a leaderboard.&lt;br&gt;&lt;br&gt;
Not a recommendation engine.&lt;br&gt;&lt;br&gt;
A structured directory of observed facts.&lt;/p&gt;

&lt;p&gt;The idea is simple: before wiring a third-party AI API provider into Codex, Cursor, Claude Code, or your own app, you should be able to compare a few concrete fields:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;supported providers and models&lt;/li&gt;
&lt;li&gt;OpenAI-compatible or Anthropic-compatible API behavior&lt;/li&gt;
&lt;li&gt;custom Base URL support&lt;/li&gt;
&lt;li&gt;pricing notes&lt;/li&gt;
&lt;li&gt;payment methods&lt;/li&gt;
&lt;li&gt;invoice support&lt;/li&gt;
&lt;li&gt;referral or recharge rules&lt;/li&gt;
&lt;li&gt;public verification sources&lt;/li&gt;
&lt;li&gt;traffic and domain signals when available&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The important shift is this:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;“Compatible with OpenAI” describes an API shape. It does not describe trust, uptime, pricing, ownership, or support quality.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That distinction matters.&lt;/p&gt;

&lt;p&gt;A provider can expose an OpenAI-style endpoint and still differ wildly in model names, streaming behavior, rate limits, credit expiration, recharge rules, error formats, and whether pricing is even visible before login.&lt;/p&gt;

&lt;p&gt;For small experiments, that might be fine.&lt;/p&gt;

&lt;p&gt;For developer tools, internal agents, or production workflows, those details are the product.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why a Directory Instead of a Ranking?
&lt;/h2&gt;

&lt;p&gt;Ranking sounds useful, but it hides too much judgment.&lt;/p&gt;

&lt;p&gt;If I say “Provider A is better than Provider B,” that may be true for one person who needs Claude access, Alipay recharge, and a cheap test balance. It may be wrong for someone who needs invoices, stable OpenAI-compatible endpoints, or multi-model routing.&lt;/p&gt;

&lt;p&gt;So the directory takes a different approach:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Collect providers.&lt;/li&gt;
&lt;li&gt;Normalize the fields.&lt;/li&gt;
&lt;li&gt;Show what can be verified.&lt;/li&gt;
&lt;li&gt;Let people filter by their actual use case.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is closer to documentation than marketing.&lt;/p&gt;

&lt;p&gt;The page currently groups providers around search intents such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AI API directory&lt;/li&gt;
&lt;li&gt;OpenAI API proxy&lt;/li&gt;
&lt;li&gt;Claude API proxy&lt;/li&gt;
&lt;li&gt;cheap OpenAI API&lt;/li&gt;
&lt;li&gt;OpenRouter alternatives&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each intent becomes a landing page, but the underlying data stays structured.&lt;/p&gt;

&lt;p&gt;That means the same provider can appear in different contexts without duplicating the source of truth.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Engineering Lesson
&lt;/h2&gt;

&lt;p&gt;The surprisingly hard part was not building the UI.&lt;/p&gt;

&lt;p&gt;The hard part was deciding what counts as a useful fact.&lt;/p&gt;

&lt;p&gt;“Supports GPT-4” is less useful than:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;what exact model names are exposed?&lt;/li&gt;
&lt;li&gt;is the endpoint OpenAI-compatible?&lt;/li&gt;
&lt;li&gt;does it require a custom Base URL?&lt;/li&gt;
&lt;li&gt;does streaming work?&lt;/li&gt;
&lt;li&gt;is pricing public or login-gated?&lt;/li&gt;
&lt;li&gt;what payment methods are listed?&lt;/li&gt;
&lt;li&gt;when was the information last checked?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once those fields exist, the frontend becomes straightforward: search, filter, compare, and link to deeper provider profiles.&lt;/p&gt;

&lt;p&gt;The stack is intentionally simple:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Astro&lt;/li&gt;
&lt;li&gt;TypeScript&lt;/li&gt;
&lt;li&gt;Content Collections&lt;/li&gt;
&lt;li&gt;React only for interactive islands&lt;/li&gt;
&lt;li&gt;structured JSON content for provider profiles&lt;/li&gt;
&lt;li&gt;static output deployed to Cloudflare Workers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This works well because the data is mostly content, but the user experience needs interactivity.&lt;/p&gt;

&lt;p&gt;Astro handles the content pages.&lt;br&gt;&lt;br&gt;
React handles the searchable directory UI.&lt;br&gt;&lt;br&gt;
The content collection schema keeps the provider data from drifting into chaos.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Bigger Point
&lt;/h2&gt;

&lt;p&gt;AI infrastructure is moving fast, but developer decisions still need boring verification.&lt;/p&gt;

&lt;p&gt;A nice homepage is not enough.&lt;br&gt;&lt;br&gt;
A low price is not enough.&lt;br&gt;&lt;br&gt;
“OpenAI-compatible” is not enough.&lt;/p&gt;

&lt;p&gt;The real question is:&lt;/p&gt;

&lt;p&gt;Can this provider be understood, compared, tested, and replaced without turning the whole project into glue code?&lt;/p&gt;

&lt;p&gt;That is what the directory is trying to answer.&lt;/p&gt;

&lt;p&gt;Here is the page: &lt;a href="https://ccnavx.com/directory/ai-api-directory/" rel="noopener noreferrer"&gt;https://ccnavx.com/directory/ai-api-directory/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, I am thinking about adding more explicit test result fields: streaming behavior, error format, model alias mapping, and minimum viable curl examples for each provider.&lt;/p&gt;

&lt;p&gt;Because at the end of the day, the best AI API provider is not the one with the loudest claim.&lt;/p&gt;

&lt;p&gt;It is the one whose behavior can be verified before it touches production.&lt;/p&gt;

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