NormaHub knowledge base

First request with Python

OpenAI SDK with the NormaHub base_url: setup, sync and streaming requests, error handling and budget control.

1. Setup and key

Work inside a virtual environment and keep the key in process environment variables, not in code. For local development add .env to .gitignore and read it via python-dotenv, so the key never lands in a repository.

python -m venv .venv
source .venv/bin/activate
python -m pip install openai python-dotenv
export NORMAHUB_API_KEY="your_api_key"

Create an API key in the NormaHub dashboard. Copy the value once — it is not shown again. Use separate keys with spend limits for scripts and agents, so one leak cannot drain the shared balance. Russian version of this guide: AI API на Python.

2. First request

The OpenAI SDK is fully compatible with NormaHub: only the base_url and the key change. Pass the exact model ID from the current catalog and cap the answer with max_tokens to control the cost of every call.

import os
from openai import OpenAI

client = OpenAI(
    api_key=os.environ["NORMAHUB_API_KEY"],
    base_url="https://api.normahub.cc/v1",
)

response = client.chat.completions.create(
    model="MODEL_ID",
    messages=[{"role": "user", "content": "Hello"}],
    max_tokens=512,
)
print(response.choices[0].message.content)

Verify the answer, then check the actual model and usage. If the SDK raises, look at the HTTP status instead of guessing from the message — all codes are covered in the API errors guide.

3. Streaming responses

For chats and agents, render the answer as it generates: users see the first words in a fraction of a second instead of waiting for the whole response. On interruption, keep the received prefix and decide separately whether the operation counts as complete — the server may have finished the job.

stream = client.chat.completions.create(
    model="MODEL_ID",
    messages=[{"role": "user", "content": "Explain briefly"}],
    stream=True,
)
for chunk in stream:
    print(chunk.choices[0].delta.content or "", end="", flush=True)

Streaming is a property of the specific endpoint and model: check the model capabilities in the catalog before enabling it.

4. Error handling

Split errors into permanent and temporary. Permanent — 400 (bad request), 401 (key), 402 (balance), 403 (model unavailable to the key): retries never fix them, fix the cause. Temporary — 429 and 5xx: retry with delay and an attempt cap, keeping X-Request-Id for support.

from openai import APIError, AuthenticationError, RateLimitError

try:
    response = client.chat.completions.create(
        model="MODEL_ID",
        messages=[{"role": "user", "content": "Hello"}],
        timeout=30,
    )
except AuthenticationError:
    print("401: check NORMAHUB_API_KEY — invalid or revoked")
except RateLimitError:
    print("429: key limit hit, wait and retry later")
except APIError as e:
    print(f"HTTP {e.status_code}: see the API errors guide")

Set a timeout per request (the SDK default can wait long) and never blindly retry non-idempotent operations. Check balance and available models in the dashboard when errors appear.

5. Cost control

Every request costs input plus output at the model rate. The most expensive parts are long dialog histories (the full context is resent every time) and uncapped max_tokens. Trim context to what is needed, cache system instructions, and budget the full operation, not a single call. The formula and examples are in the token cost guide.

Частые вопросы

Where do I get the model ID?

Use the exact ID from the models catalog or from GET /v1/models with your key. IDs from third-party examples may be renamed or unavailable to your key.

Why do I get 401 invalid_api_key?

The key is missing from the environment, mistyped, or revoked in the dashboard. Check NORMAHUB_API_KEY and issue a new key while revoking the old one.

How do I stream responses?

Pass stream=True and read chunk.choices[0].delta.content in a loop. See the streaming guide for interruptions and completion handling.

Do I need a VPN from Russia?

No. Requests go to https://api.normahub.cc/v1 directly, and billing comes from your dashboard balance.