Back to cookbook

Migrate from OpenAI in 3 lines

Your OpenAI code keeps working. What it gains is the end customer's identity on every request, which is what buys you per-customer budgets and a margin report instead of one undifferentiated monthly bill. Three edits, no new app.

Python below; JS and Go are the same idea.

What you need

  • An FC API key (from /app/api-keys)
  • Your existing OpenAI-SDK code (Python, Node or any OpenAI-compatible SDK)

Full code

python
# before.py from openai import OpenAI client = OpenAI(api_key=os.environ["OPENAI_API_KEY"]) def chat(msg: str) -> str: r = client.chat.completions.create( model="gpt-4o-mini", messages=[{"role": "user", "content": msg}], ) return r.choices[0].message.content
python
# after.py, 3 lines changed. from openai import OpenAI client = OpenAI( api_key=os.environ["FC_API_KEY"], # 1. new key base_url="https://api.fightclub.pro/v1", # 2. base URL ) def chat(msg: str, customer: str) -> str: r = client.chat.completions.create( model="fc:openai/gpt-4o-mini", # required: prefix with fc: messages=[{"role": "user", "content": msg}], extra_headers={"FC-Customer": customer}, # 3. attribute the request ) return r.choices[0].message.content

JavaScript:

js
// after.js import OpenAI from 'openai'; const client = new OpenAI({ apiKey: process.env.FC_API_KEY, baseURL: 'https://api.fightclub.pro/v1', }); const r = await client.chat.completions.create({ model: 'fc:openai/gpt-4o-mini', messages: [{ role: 'user', content: 'hi' }], }, { headers: { 'FC-Customer': 'cus_alice' }, });

Environment-variable style (base URL + key move out of code):

bash
export OPENAI_BASE_URL=https://api.fightclub.pro/v1 export OPENAI_API_KEY=ko_0d7f2a91c4e35b86af10d2c7e94b6f3a5d81c02e7b4936af18d5c60e2a7f9b34

The base URL and key drop into env vars. Every model string still needs an fc: prefix in code (e.g. fc:openai/gpt-4o-mini); a bare gpt-4o returns 400 invalid_model_ref.

Walkthrough

What stays identical: chat.completions.create signatures, embeddings.create, the Assistants API surface, streaming semantics, response_format, tool calling and max_tokens. The request and response shapes are wire-compat. The only in-code change is the model string.

What's new: three Ringside-specific error codes you didn't have before. invalid_api_key (401) means your FC key is wrong or revoked. rate_limited (429) respects both per-dev limits and per-Customer RPM/TPM, check the Retry-After header. customer_budget_exceeded (402) fires once a Customer has spent past its cap; raise it with PATCH /v1/customers/:id and a new monthly_budget_usd. All three map cleanly to the SDK's exception classes.

What to do with the fc: prefix: required. Every model ref must start with fc: (a direct model, e.g. fc:openai/gpt-4o-mini), slot: (a dev-configured alias so you can swap the underlying model without redeploying) or match: (a declarative selector). A bare gpt-4o-mini returns 400 invalid_model_ref.

Per-customer attribution is where the value lives. Without FC-Customer, usage aggregates against your dev account and you are back to one bill. With it you get /v1/usage?group_by=customer, per-customer budgets and rate limits, plus /v1/margin for revenue against cost per customer. Almost every recipe in this cookbook hangs off that one header.

Run it

bash
export FC_API_KEY=ko_0d7f2a91c4e35b86af10d2c7e94b6f3a5d81c02e7b4936af18d5c60e2a7f9b34 python after.py

What's next