Voltar para o blog

Unified AI API Quickstart: One Key, Every Model

A 3-step quickstart for a unified AI API: create a key, point your base URL at the gateway, and call any model with curl or the OpenAI SDK — streaming and usage included.

Apiko Team

If you're calling more than one model provider today, you already know the tax: a different SDK per vendor, a different auth header shape, a different bill to reconcile at the end of the month. A unified AI API collapses that into one base URL, one key, and one OpenAI-compatible request shape — you swap the model field, not your integration code.

This is a hands-on quickstart. In three steps you'll go from a fresh account to a working request, then extend that into streaming and usage tracking — the parts that actually matter once you move past a single test call.

What "unified AI API" means in practice

Concretely, a unified AI API is a single endpoint that speaks the OpenAI protocol and routes each request to the right upstream model behind the scenes. You don't maintain N SDKs, N sets of credentials, or N separate usage dashboards. You maintain one:

  • One base URL{gateway}/v1, the same shape as api.openai.com/v1.
  • One key — a single sk-… credential authenticates every model in the catalog.
  • One bill — usage across providers lands in the same ledger, billed by real token counts.

If your code already speaks the OpenAI protocol — the official openai SDK, LangChain, LlamaIndex, a raw fetch/requests call — switching is a one-line change: point the base URL at the gateway and swap the key.

Step 1: Create an account and an API key

Sign up, then open Console → API Keys and create a key. The plaintext key (sk-…) is shown once at creation time and is never stored in retrievable form on the site — copy it somewhere safe immediately.

New accounts receive trial credit automatically on signup, with no credit card required, so you can run the examples below before deciding on anything else. A key can also carry its own spending limit and model restrictions; requests outside those bounds are rejected rather than silently billed.

Step 2: Point your base URL at the gateway

There are two distinct surfaces here, and it's worth keeping them straight:

  • The gateway (data plane) — this is what your application code talks to. It's the OpenAI-compatible API: /chat/completions, /models, authenticated with your sk-… key.
  • The console (control plane) — this is where you manage keys, view usage, and top up balance. It does not proxy /v1 traffic — your API calls never round-trip through it.

Set the gateway's base URL once, wherever your SDK exposes it:

  • JavaScript SDK: baseURL
  • Python SDK: base_url
  • Raw HTTP: prefix every path with the gateway host
The host shown in the examples below is a placeholder (an IETF-reserved .example domain). Replace it with your deployment's real gateway address.

That's the entire migration for most codebases: change one config value, keep everything else — retries, timeouts, streaming handling — untouched.

Step 3: Call any model

With the key and base URL in place, here's a minimal chat completion over curl:

curl https://api.apiko.example/v1/chat/completions \
  -H "Authorization: Bearer $APIKO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "claude-sonnet-4-5-20250929",
    "messages": [{"role": "user", "content": "Hello"}]
  }'

The model field is the only thing that changes between providers. Model ids come from the catalog — check it (or GET /v1/models with your own key) for the exact ids your key can call, since catalog entries expand over time as new channels come online.

Using the OpenAI SDK

Because the gateway speaks the OpenAI protocol, you don't need a new client library — just repoint the existing one.

JavaScript:

import OpenAI from "openai";

const client = new OpenAI({
  baseURL: "https://api.apiko.example/v1",
  apiKey: process.env.APIKO_API_KEY,
});

const res = await client.chat.completions.create({
  model: "claude-sonnet-4-5-20250929",
  messages: [{ role: "user", content: "Hello" }],
});

Python:

import os
from openai import OpenAI

client = OpenAI(
    base_url="https://api.apiko.example/v1",
    api_key=os.environ["APIKO_API_KEY"],
)

res = client.chat.completions.create(
    model="claude-sonnet-4-5-20250929",
    messages=[{"role": "user", "content": "Hello"}],
)

Swap model to any id in the catalog and the rest of the call is identical — same auth header, same request shape, same response envelope. This is the actual payoff of a unified AI API: the integration work you do once covers every model you'll add later, not just the one you started with.

Streaming, and where the real usage number comes from

Set "stream": true to get server-sent events instead of a single JSON blob — each chunk is a data: line, terminated by data: [DONE]. For anything long-running, like a chat UI where users expect the response to start appearing right away, this is what keeps the response feeling responsive.

The detail worth calling out: pass stream_options.include_usage, and the final chunk carries real token usage — the exact number billing uses. That matters because request-time token estimates and final usage don't always match; reading it off the stream itself removes the guesswork.

curl -N https://api.apiko.example/v1/chat/completions \
  -H "Authorization: Bearer $APIKO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "claude-sonnet-4-5-20250929",
    "messages": [{"role": "user", "content": "Hello"}],
    "stream": true,
    "stream_options": {"include_usage": true}
  }'

The same pattern in Python:

stream = client.chat.completions.create(
    model="claude-sonnet-4-5-20250929",
    messages=[{"role": "user", "content": "Hello"}],
    stream=True,
    stream_options={"include_usage": True},
)
for chunk in stream:
    if chunk.choices and chunk.choices[0].delta.content:
        print(chunk.choices[0].delta.content, end="")
    if chunk.usage:  # final chunk carries real token usage
        print(chunk.usage)

Billing follows a straightforward three-step mechanism regardless of whether you stream: a pre-hold is taken from your balance when the request starts (based on an estimate), it settles to the provider's real reported usage once the response completes, and if the upstream call fails outright, the pre-hold is refunded automatically — you're not charged for a request that never succeeded.

Handling errors without guessing

Errors follow the standard OpenAI error shape ({"error": {"message", "type", "code"}}), so existing error-handling code mostly just works. The status codes worth having explicit handling for:

| Status | Typical cause | What to do | |---|---|---| | 401 | Missing or invalid key | Check the Authorization: Bearer header and that the key hasn't been revoked | | 402 / 403 | Insufficient balance | Top up from the console and retry | | 404 | Unknown model id | Check the id against the catalog and your key's model restrictions | | 429 | Rate limited | Back off exponentially; spread bursts across time | | 5xx | Upstream provider failure | The gateway retries across channels automatically; if it still fails, the pre-hold is refunded and you're not billed |

That last row is the one people don't expect: a 5xx here doesn't mean you eat the cost of a failed call. Retries happen behind the single endpoint you're already calling, and failure is refunded, not charged.

From quickstart to production

Once the first request works, a few small habits keep things predictable as usage grows:

  1. Read usage from the API, not just the dashboard. The final streaming chunk (or the non-streaming response body) already carries the real token counts — log them alongside your own request IDs if you need to reconcile spend programmatically.
  2. Scope keys per surface. Because a key can carry its own spending limit and model allowlist, it's worth issuing separate keys for a staging environment vs. production, or per internal service, rather than sharing one key everywhere.
  3. Treat the model id as configuration, not a constant. Since switching models is a one-line change, keep it in an env var or config value from day one — it makes testing a newer model, or falling back to another one, a non-event.
  4. Check /models before you assume availability. The catalog reflects what's actually callable right now; confirm an id is listed before you build a feature around it.

None of this requires new infrastructure — it's the same request shape from Step 3, applied consistently.

Try it

The fastest way to see whether a unified AI API fits your stack is to run the three steps above end to end: create a key, point your existing OpenAI-compatible client at the gateway, and make one real call. New accounts get trial credit automatically, so there's nothing to configure before your first request. From there, check /pricing for the current per-model token rates, or head to /sign-up to get a key.