API zgodne z OpenAI

Jeden adres bazowy. Każdy model.

Skieruj dowolne SDK OpenAI na unbottled.ai. 202 modeli, streaming, wywoływanie narzędzi. Twój klucz, twoje kredyty, żadnego nowego klienta do nauki.

Szybki start

Pierwsze wywołanie w 60 sekund

API mówi formatem OpenAI. Jeśli twój kod działa z OpenAI, zadziała i tu po dwóch zmianach: adres bazowy i klucz.

1

Weź klucz

Utwórz klucz sk-unb w Ustawienia > Klucze API. Widać go tylko raz. API jest częścią każdego płatnego planu: odblokowuje je aktywna subskrypcja albo zakup pakietu kredytów.

Adres bazowy: http://localhost:3000/api/pl-GLOBAL/v1

W ścieżce możesz użyć dowolnego języka (en-US, de-DE, pl-PL). Zmienia tylko język komunikatów o błędach.

2

Wyślij zapytanie

Czyste HTTP. Działa w każdym języku.

curl
curl http://localhost:3000/api/pl-GLOBAL/v1/chat/completions \
  -H "Authorization: Bearer sk-unb-YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "kimi-k2",
    "messages": [
      { "role": "system", "content": "You answer in one sentence." },
      { "role": "user", "content": "Why is the sky blue?" }
    ]
  }'
3

Albo użyj SDK OpenAI

Zmień baseURL i apiKey. Nic więcej.

Python
from openai import OpenAI

client = OpenAI(
    base_url="http://localhost:3000/api/pl-GLOBAL/v1",
    api_key="sk-unb-YOUR_KEY",
)

reply = client.chat.completions.create(
    model="kimi-k2",
    messages=[{"role": "user", "content": "Why is the sky blue?"}],
)
print(reply.choices[0].message.content)
Node.js
import OpenAI from "openai";

const client = new OpenAI({
  baseURL: "http://localhost:3000/api/pl-GLOBAL/v1",
  apiKey: process.env.UNBOTTLED_API_KEY, // sk-unb-YOUR_KEY
});

const reply = await client.chat.completions.create({
  model: "kimi-k2",
  messages: [{ role: "user", content: "Why is the sky blue?" }],
});
console.log(reply.choices[0].message.content);
Tylko plany płatne. Tworzenie kluczy i wywołania /v1 wymagają płatnego konta: aktywnej subskrypcji albo co najmniej jednego zakupu kredytów. Konta na planie darmowym dostają HTTP 402 z kodem paid_plan_required, dokumentację nadal mogą czytać. Kredyty zostają twoje: kupujesz pakiet raz, a API działa, dopóki saldo się nie wyczerpie.
Uwierzytelnianie

Uwierzytelnianie

Wysyłaj klucz w każdym zapytaniu jako token Bearer. Klucze zaczynają się od sk-unb-.

header
Authorization: Bearer sk-unb-TWOJ_KLUCZ

Przechowujemy hash, nie klucz. Zgubisz go, tworzysz nowy.

Klucz działa tylko na /v1. Nie dotknie konta, płatności ani ustawień.

Do 20 aktywnych kluczy na konto. Każdy klucz możesz unieważnić w dowolnej chwili: przestaje działać natychmiast.

Brak klucza albo klucz unieważniony zwraca 401. Klucz konta bez płatnego planu zwraca 402.

Modele

GET /v1/models

Zwraca modele, które twój klucz może teraz wywołać, z rozmiarem kontekstu, obsługą narzędzi i ceną w kredytach za milion tokenów. Użyj id jako pola model w zapytaniach.

request
curl http://localhost:3000/api/pl-GLOBAL/v1/models \
  -H "Authorization: Bearer sk-unb-YOUR_KEY"
200
{
  "object": "list",
  "data": [
    {
      "id": "kimi-k2",
      "object": "model",
      "created": 1735689600,
      "owned_by": "moonshot",
      "name": "Kimi K2",
      "context_length": 131072,
      "supports_tools": true,
      "input_modalities": ["text"],
      "output_modalities": ["text"],
      "pricing": {
        "input_credits_per_million_tokens": 8,
        "output_credits_per_million_tokens": 32
      }
    }
  ]
}

Lista zależy od twojego konta. Nieznane lub niedostępne id zwracają 404 model_not_found. API nigdy po cichu nie podmienia modelu.

Chat completions

POST /v1/chat/completions

Standardowe chat completions w formacie OpenAI. Wiadomości na wejściu, jedna wiadomość asystenta na wyjściu. Wejście tekstowe i obrazy (image_url jako adres https albo base64 data URL).

Bez streamingu

Tryb domyślny. Dostajesz jedno ciało JSON, gdy model skończy. usage.credits_charged pokazuje, ile kosztowało wywołanie.

request
curl http://localhost:3000/api/pl-GLOBAL/v1/chat/completions \
  -H "Authorization: Bearer sk-unb-YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "kimi-k2",
    "messages": [
      { "role": "system", "content": "You answer in one sentence." },
      { "role": "user", "content": "Why is the sky blue?" }
    ]
  }'
200
{
  "id": "chatcmpl-3f9c1a...",
  "object": "chat.completion",
  "created": 1767225600,
  "model": "kimi-k2",
  "choices": [
    {
      "index": 0,
      "message": { "role": "assistant", "content": "Air scatters blue light more than red." },
      "finish_reason": "stop"
    }
  ],
  "usage": {
    "prompt_tokens": 31,
    "completion_tokens": 9,
    "total_tokens": 40,
    "credits_charged": 0.1
  }
}

Streaming

Z stream: true dostajesz zdarzenia server-sent w formacie chunków OpenAI, zakończone przez data: [DONE]. Dodaj stream_options.include_usage, by dostać ostatni chunk z liczbą tokenów i credits_charged. Zamknięcie połączenia zatrzymuje generowanie. Płacisz tylko za to, co się wykonało.

request
curl -N http://localhost:3000/api/pl-GLOBAL/v1/chat/completions \
  -H "Authorization: Bearer sk-unb-YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "kimi-k2",
    "stream": true,
    "stream_options": { "include_usage": true },
    "messages": [{ "role": "user", "content": "Count to three." }]
  }'
200
data: {"id":"chatcmpl-3f9c...","object":"chat.completion.chunk","choices":[{"index":0,"delta":{"role":"assistant","content":""},"finish_reason":null}]}

data: {"id":"chatcmpl-3f9c...","object":"chat.completion.chunk","choices":[{"index":0,"delta":{"content":"One, "},"finish_reason":null}]}

data: {"id":"chatcmpl-3f9c...","object":"chat.completion.chunk","choices":[{"index":0,"delta":{},"finish_reason":"stop"}]}

data: {"id":"chatcmpl-3f9c...","object":"chat.completion.chunk","choices":[],"usage":{"prompt_tokens":12,"completion_tokens":7,"total_tokens":19,"credits_charged":0.1}}

data: [DONE]
Python
stream = client.chat.completions.create(
    model="kimi-k2",
    messages=[{"role": "user", "content": "Count to three."}],
    stream=True,
)
for chunk in stream:
    if chunk.choices and chunk.choices[0].delta.content:
        print(chunk.choices[0].delta.content, end="")
Node.js
const stream = await client.chat.completions.create({
  model: "kimi-k2",
  messages: [{ role: "user", content: "Count to three." }],
  stream: true,
});
for await (const chunk of stream) {
  process.stdout.write(chunk.choices[0]?.delta?.content ?? "");
}

Pola zapytania

PoleDziałanie
modelWymagane. Id modelu z GET /v1/models.
messagesWymagane. Wiadomości system, developer, user, assistant i tool. Treść user to string albo części text i image_url.
streamtrue dla zdarzeń server-sent. Domyślnie false.
stream_options{ include_usage: true } dodaje końcowy chunk z użyciem.
max_tokensGórna granica generowanych tokenów. max_completion_tokens ma pierwszeństwo. Limit 32768.
temperatureOd 0 do 2. Ignorowane przez modele, które tego nie obsługują.
top_pOd 0 do 1.
stopCiąg zatrzymujący albo ich lista.
nObsługiwane jest tylko 1. Inne wartości zwracają 400.
toolsNarzędzia funkcyjne, które model może wywołać. Zobacz wywoływanie narzędzi.
tool_choicenone, auto, required albo jedna wskazana funkcja.
Wywoływanie narzędzi

Wywoływanie narzędzi

Function calling działa jak w OpenAI. Opisujesz funkcje, model decyduje się na wywołanie, ty je wykonujesz i odsyłasz wynik. Twoich narzędzi nigdy nie uruchamiamy.

Pętla

Wyślij tools razem z wiadomościami.

Model odpowiada z tool_calls i finish_reason: tool_calls. Każde wywołanie ma id, nazwę funkcji i argumenty jako string JSON.

Wykonaj funkcję samodzielnie. Dołącz wiadomość asystenta, a potem po jednej wiadomości tool na wywołanie, z jej tool_call_id.

Wywołaj ponownie. Model użyje wyników i odpowie.

1. Zapytanie z narzędziami

request
curl http://localhost:3000/api/pl-GLOBAL/v1/chat/completions \
  -H "Authorization: Bearer sk-unb-YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "kimi-k2",
    "messages": [{ "role": "user", "content": "Weather in Berlin?" }],
    "tools": [{
      "type": "function",
      "function": {
        "name": "get_weather",
        "description": "Current weather for a city",
        "parameters": {
          "type": "object",
          "properties": { "city": { "type": "string" } },
          "required": ["city"]
        }
      }
    }],
    "tool_choice": "auto"
  }'

2. Model prosi o wywołanie

200
{
  "object": "chat.completion",
  "model": "kimi-k2",
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": null,
        "tool_calls": [
          {
            "id": "call_8x2k",
            "type": "function",
            "function": { "name": "get_weather", "arguments": "{\"city\":\"Berlin\"}" }
          }
        ]
      },
      "finish_reason": "tool_calls"
    }
  ]
}

3. Odeślij wynik

request
# Run get_weather yourself, then send the result back:
curl http://localhost:3000/api/pl-GLOBAL/v1/chat/completions \
  -H "Authorization: Bearer sk-unb-YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "kimi-k2",
    "messages": [
      { "role": "user", "content": "Weather in Berlin?" },
      { "role": "assistant", "content": null, "tool_calls": [
        { "id": "call_8x2k", "type": "function",
          "function": { "name": "get_weather", "arguments": "{\"city\":\"Berlin\"}" } }
      ]},
      { "role": "tool", "tool_call_id": "call_8x2k", "content": "18C, cloudy" }
    ],
    "tools": [ ...same tools as before... ]
  }'

Ta sama pętla w SDK Pythona

Python
import json

tools = [{
    "type": "function",
    "function": {
        "name": "get_weather",
        "description": "Current weather for a city",
        "parameters": {
            "type": "object",
            "properties": {"city": {"type": "string"}},
            "required": ["city"],
        },
    },
}]
messages = [{"role": "user", "content": "Weather in Berlin?"}]

first = client.chat.completions.create(
    model="kimi-k2", messages=messages, tools=tools
)
call = first.choices[0].message.tool_calls[0]
result = get_weather(**json.loads(call.function.arguments))  # your code

messages += [
    first.choices[0].message,
    {"role": "tool", "tool_call_id": call.id, "content": result},
]
final = client.chat.completions.create(
    model="kimi-k2", messages=messages, tools=tools
)
print(final.choices[0].message.content)

Wywołania narzędzi w streamie

Z stream: true wywołania przychodzą jako chunki delta.tool_calls, adresowane przez index. Pierwszy chunk danego indeksu niesie id i function.name. Kolejne dopisują do function.arguments. Złóż je w całość i sparsuj JSON, gdy finish_reason to tool_calls. Każde SDK OpenAI robi to za ciebie.

Zasady

Model musi obsługiwać narzędzia: sprawdź supports_tools w GET /v1/models. Pozostałe zwracają 400 tools_not_supported.

tool_choice none, auto i required działają jak w OpenAI. Funkcja spoza tools zwraca 400.

Nazwy funkcji: litery, cyfry, podkreślenie, myślnik, do 64 znaków.

Definicje narzędzi i ich wyniki to tokeny wejściowe. Wywołania to tokeny wyjściowe. Rozliczane są jak wszystkie inne tokeny.

Kredyty i rozliczenia

Kredyty i rozliczenia

API czerpie z tych samych kredytów co czat, po tych samych cenach. Jedno saldo, jeden rachunek.

Koszt = tokeny wejściowe plus wyjściowe po cenie modelu w kredytach. Ceny za milion tokenów podaje GET /v1/models.

Każda odpowiedź zawiera usage.credits_charged. Saldo znajdziesz w aplikacji w zakładce Kredyty.

Przed wywołaniem porównujemy saldo z oszacowaniem na najgorszy przypadek. Za mało: HTTP 402 insufficient_credits, nic się nie wykonuje, nic nie jest pobierane.

Po wywołaniu płacisz za rzeczywiste zużycie tokenów zgłoszone przez dostawcę, nie za oszacowanie.

Skończyły się kredyty? Kup pakiet albo odnów plan. Następne wywołanie działa od razu.

402
HTTP/1.1 402 Payment Required
{
  "error": {
    "message": "Insufficient credits. This call needs about 1.2, you have 0.4. Top up to continue.",
    "type": "insufficient_quota",
    "param": null,
    "code": "insufficient_credits"
  }
}
Limity zapytań

Limity zapytań

Każdy klucz domyślnie pozwala na 60 zapytań na minutę. Powyżej limitu dostajesz HTTP 429 z nagłówkiem Retry-After w sekundach. Odczekaj tyle i spróbuj ponownie. Limity są na klucz, więc większe obciążenia rozłóż na kilka kluczy.

429
HTTP/1.1 429 Too Many Requests
Retry-After: 12
{ "error": { "type": "rate_limit_error", "code": "rate_limit_exceeded", ... } }
Błędy

Błędy

Błędy mają kopertę OpenAI, więc SDK zgłaszają swoje zwykłe wyjątki. Opieraj się na error.code, nie na treści komunikatu.

error
{
  "error": {
    "message": "Invalid or revoked API key. ...",
    "type": "authentication_error",
    "param": null,
    "code": "invalid_api_key"
  }
}
HTTP / codeZnaczenie
401 missing_api_keyBrak nagłówka Authorization. Wyślij Bearer sk-unb-...
401 invalid_api_keyKlucz jest błędny albo unieważniony.
402 paid_plan_requiredKonto nie ma płatnego planu. Wykup subskrypcję albo kup kredyty.
402 insufficient_creditsSaldo za niskie na to wywołanie. Doładuj.
402 free_tier_cap_reachedOsiągnięty dzienny limit planu darmowego. Kredyty to naprawiają.
404 model_not_foundModel nie istnieje albo nie jest dla ciebie dostępny.
400 invalid_value / unsupported_parameter / tools_not_supportedBłędne zapytanie: nieobsługiwany parametr, zły tool_choice, zły adres obrazu.
429 rate_limit_exceededPonad 60 zapytań na minutę dla tego klucza. Przeczytaj Retry-After.
502 upstream_errorDostawca modelu zawiódł. Ponowienie jest bezpieczne. Streamy kończą się zdarzeniem błędu.
503 balance_unavailableNie udało się odczytać salda, więc nic nie ruszyło. Ponów.
Błędy w trakcie streamu przychodzą jako zdarzenie data: z tym samym obiektem błędu, po którym następuje data: [DONE].

Buduj na każdym modelu.

Utwórz klucz, przełącz SDK, wypuść.