Developers

Tutorial — from zero to first call

Send your first request through the SchneeAI Gateway in under five minutes. Then go further: streaming, named prompts, production error handling, and cost tracking.

Prerequisites

  • A SchneeAI account and API key. Email [email protected] to request design-partner access.
  • Any HTTP client. The examples below use curl, Python 3.9+, and TypeScript 5+.

Step 1 — Your first request

The Gateway exposes an OpenAI-compatible endpoint at POST /v1/chat/completions. The simplest call:

curl https://api.schneeai.com/v1/chat/completions \
  -H "Authorization: Bearer $SCHNEEAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "auto",
    "messages": [
      { "role": "user", "content": "Summarize SchneeAI in one sentence." }
    ]
  }'

The model: "auto" value tells SchneeAI’s router to pick the best model for the workload based on your routing policy. To pin a specific model, replace "auto" with a model ID from the Model Directory.

Response shape (abridged):

{
  "id": "chatcmpl-...",
  "choices": [{
    "message": { "role": "assistant", "content": "SchneeAI is a..." }
  }],
  "usage": { "prompt_tokens": 12, "completion_tokens": 18, "total_tokens": 30 },
  "model": "auto→gpt-4o-mini",
  "request_id": "req_01HQ..."
}

The model field echoes what the router actually selected. The request_id is your trace handle for support.

Step 2 — Stream tokens as they arrive

For chat UIs, streaming is essential. Set stream: true and the Gateway returns Server-Sent Events (SSE).

Python

import json
import requests

res = requests.post(
    "https://api.schneeai.com/v1/chat/completions",
    headers={
        "Authorization": f"Bearer {API_KEY}",
        "Content-Type": "application/json",
    },
    json={
        "model": "auto",
        "messages": [{"role": "user", "content": "Write a haiku about snow."}],
        "stream": True,
    },
    stream=True,
)
for line in res.iter_lines():
    if not line or not line.startswith(b"data: "):
        continue
    chunk = line[6:]
    if chunk == b"[DONE]":
        break
    data = json.loads(chunk)
    delta = data["choices"][0]["delta"].get("content", "")
    print(delta, end="", flush=True)

TypeScript (browser / Node 18+)

const res = await fetch("https://api.schneeai.com/v1/chat/completions", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    model: "auto",
    messages: [{ role: "user", content: "Write a haiku about snow." }],
    stream: true,
  }),
});

const reader = res.body!.getReader();
const decoder = new TextDecoder();
let buffer = "";

while (true) {
  const { done, value } = await reader.read();
  if (done) break;
  buffer += decoder.decode(value, { stream: true });
  const lines = buffer.split("\n");
  buffer = lines.pop()!;
  for (const line of lines) {
    if (!line.startsWith("data: ")) continue;
    const chunk = line.slice(6);
    if (chunk === "[DONE]") return;
    const delta = JSON.parse(chunk).choices[0].delta?.content ?? "";
    process.stdout.write(delta);
  }
}

Step 3 — Use a named prompt

Hardcoded prompts rot. Register a prompt in the PromptOps UI, version it, then call it by name:

curl https://api.schneeai.com/v1/chat/completions \
  -H "Authorization: Bearer $SCHNEEAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "prompt": "support-reply",
    "variables": {
      "user_message": "My order #1234 hasn't arrived.",
      "tone": "empathetic"
    }
  }'

SchneeAI resolves the active version of support-reply, fills in {{user_message}} and {{tone}}, applies the registered model and parameters, and returns the completion. See the Prompt Template Library for prompt patterns that work well across models.

Step 4 — Handle errors correctly

Network calls fail. Production code must:

  1. Retry only on 429, 500, 502, 503, 504.
  2. Honor Retry-After when present.
  3. Send Idempotency-Key so retries do not double-charge.
import uuid
import requests
from tenacity import retry, stop_after_attempt, wait_exponential_jitter, retry_if_exception_type

class RetryableError(Exception): pass

@retry(stop=stop_after_attempt(5),
       wait=wait_exponential_jitter(initial=1, max=60, jitter=2),
       retry=retry_if_exception_type(RetryableError))
def chat(messages, model="auto"):
    res = requests.post(
        "https://api.schneeai.com/v1/chat/completions",
        headers={
            "Authorization": f"Bearer {API_KEY}",
            "Idempotency-Key": str(uuid.uuid4()),
            "Content-Type": "application/json",
        },
        json={"model": model, "messages": messages},
        timeout=60,
    )
    if res.status_code in {429, 500, 502, 503, 504}:
        raise RetryableError(res.text)
    res.raise_for_status()
    return res.json()

Full error code reference, retry rules, and idempotency details: Error Handling.

Step 5 — Track cost and usage

Every response includes a usage object. SchneeAI records structured usage server-side too, queryable via GET /v1/usage:

curl https://api.schneeai.com/v1/usage?since=2026-07-01 \
  -H "Authorization: Bearer $SCHNEEAI_API_KEY"

To estimate cost before sending, paste your prompt into the Token Counter, then pass the resulting token count to the Cost Calculator.

Next steps


Interactive version coming: a CodeMirror-based playground where you can edit and run each snippet live in the browser is on the roadmap. Today, copy any block into your terminal or editor to run it.