# BazaarLink API - Developer Skill

> Load this skill to give your AI assistant full knowledge of the BazaarLink API.
> BazaarLink is a Taiwan AI API gateway with OpenAI-compatible, Anthropic-compatible, and agent-oriented surfaces.

- **OpenAI Base URL:** `https://bazaarlink.ai/api/v1`
- **Anthropic Base URL:** `https://bazaarlink.ai/api`
- **Primary Auth:** `Authorization: Bearer sk-bl-YOUR_API_KEY`
- **Anthropic Auth:** `x-api-key: sk-bl-YOUR_API_KEY`
- **Get a key:** https://bazaarlink.ai/keys
- **Model catalog + pricing:** https://bazaarlink.ai/models
- **Full docs:** https://bazaarlink.ai/docs

---

## Authentication

### OpenAI-compatible routes

```http
Authorization: Bearer sk-bl-YOUR_API_KEY
Content-Type: application/json
```

### Anthropic-compatible routes

```http
x-api-key: sk-bl-YOUR_API_KEY
anthropic-version: 2023-06-01
Content-Type: application/json
```

Management keys can manage API keys, but cannot call models.

---

## Core Endpoints

| Method | Path | Description |
|--------|------|-------------|
| POST | `/api/v1/chat/completions` | OpenAI-compatible chat completions |
| POST | `/api/v1/responses` | OpenAI Responses API compatible endpoint |
| POST | `/api/v1/embeddings` | OpenAI-compatible embeddings |
| POST | `/api/v1/audio/transcriptions` | Speech-to-text (Whisper API compatible) |
| POST | `/api/v1/audio/speech` | Text-to-speech (binary audio output) |
| POST | `/api/v1/messages` | Anthropic Messages API compatible endpoint |
| GET  | `/api/v1/models` | Public model catalog with pricing and capabilities |
| GET  | `/api/v1/key` | Key status, spend, remaining balance, rate limit tier |
| GET  | `/api/v1/auth/key` | OpenRouter-style API key info |
| GET  | `/api/v1/credits` | Current credits and lifetime usage |
| GET  | `/api/v1/usage` | Usage analytics for the authenticated user |
| GET  | `/api/v1/generation?id=...` | Generation metadata and cost breakdown |
| GET  | `/api/v1/keys` | List API keys for the current account |
| POST | `/api/v1/keys` | Create a standard or management API key |
| PATCH | `/api/v1/keys/:id` | Enable/disable a key or change spend limit |
| DELETE | `/api/v1/keys/:id` | Delete an API key |
| POST | `/api/v1/agents/register` | Self-register an agent account and claimable key |

---

## Model ID Format

**Always include the provider prefix. Bare model names return `400`.**

```text
openai/gpt-4.1
openai/gpt-4o
openai/gpt-4o-mini
anthropic/claude-opus-4-6
anthropic/claude-sonnet-4-6
anthropic/claude-haiku-4-5
google/gemini-2.5-pro
google/gemini-2.5-flash
deepseek/deepseek-chat
deepseek/deepseek-r1
meta-llama/llama-3.3-70b-instruct
qwen/qwen3-235b-a22b
```

Full list: `GET /api/v1/models`

---

## OpenAI Quick Start

### curl

```bash
curl https://bazaarlink.ai/api/v1/chat/completions \
  -H "Authorization: Bearer sk-bl-YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "openai/gpt-4o-mini",
    "messages": [{"role": "user", "content": "Hello!"}]
  }'
```

### Python

```python
from openai import OpenAI

client = OpenAI(
    base_url="https://bazaarlink.ai/api/v1",
    api_key="sk-bl-YOUR_API_KEY",
)

response = client.chat.completions.create(
    model="openai/gpt-4o-mini",
    messages=[{"role": "user", "content": "Hello!"}],
)

print(response.choices[0].message.content)
```

### TypeScript / Node.js

```typescript
import OpenAI from "openai";

const client = new OpenAI({
  baseURL: "https://bazaarlink.ai/api/v1",
  apiKey: "sk-bl-YOUR_API_KEY",
});

const response = await client.chat.completions.create({
  model: "anthropic/claude-sonnet-4-6",
  messages: [{ role: "user", content: "Hello!" }],
});

console.log(response.choices[0].message.content);
```

---

## Anthropic / Claude Code Quick Start

Use this when integrating Claude Code or the Anthropic SDK.

```bash
ANTHROPIC_BASE_URL=https://bazaarlink.ai/api
ANTHROPIC_API_KEY=sk-bl-YOUR_API_KEY
```

```python
from anthropic import Anthropic

client = Anthropic(
    base_url="https://bazaarlink.ai/api",
    api_key="sk-bl-YOUR_API_KEY",
)

message = client.messages.create(
    model="anthropic/claude-sonnet-4-6",
    max_tokens=256,
    messages=[{"role": "user", "content": "Hello!"}],
)
```

---

## Responses API

BazaarLink supports the OpenAI Responses API format at `POST /api/v1/responses`.

```typescript
const response = await client.responses.create({
  model: "openai/gpt-4.1",
  input: "Summarize this repository in 3 bullets.",
});
```

The route accepts `input`, `instructions`, multimodal content blocks, and tool-call replay items, then translates to chat completions upstream.

---

## Embeddings

```python
response = client.embeddings.create(
    model="openai/text-embedding-3-small",
    input="BazaarLink is an AI API gateway",
)

embedding = response.data[0].embedding
```

---

## Audio Transcriptions

Drop-in replacement for the OpenAI Whisper API. Supported models: `openai/whisper-1` (billed by duration) and `openai/gpt-4o-transcribe` (billed by tokens).

```python
from openai import OpenAI

client = OpenAI(
    base_url="https://bazaarlink.ai/v1",
    api_key="YOUR_API_KEY",
)

with open("audio.wav", "rb") as f:
    transcript = client.audio.transcriptions.create(
        model="openai/whisper-1",   # or "openai/gpt-4o-transcribe"
        file=f,
    )

print(transcript.text)
```

```typescript
import OpenAI from "openai";
import fs from "fs";

const client = new OpenAI({
  baseURL: "https://bazaarlink.ai/v1",
  apiKey: process.env.BAZAARLINK_API_KEY,
});

const transcript = await client.audio.transcriptions.create({
  model: "openai/whisper-1",
  file: fs.createReadStream("audio.wav"),
});

console.log(transcript.text);
```

**Billing:** `whisper-1` reports `usage.seconds`; `gpt-4o-transcribe` reports `usage.total_tokens`. Both return `usage.cost` for the exact charge.

---

## Text-to-Speech (TTS)

Synthesize speech from text. Returns binary audio.

```python
from openai import OpenAI

client = OpenAI(
    base_url="https://bazaarlink.ai/api/v1",
    api_key="sk-bl-YOUR_API_KEY",
)

with client.audio.speech.with_streaming_response.create(
    model="openai/gpt-4o-mini-tts-2025-12-15",
    voice="coral",
    input="Hello from BazaarLink.",
    response_format="mp3",
) as response:
    response.stream_to_file("hello.mp3")
```

```typescript
import OpenAI from "openai";
import fs from "fs";

const client = new OpenAI({
  baseURL: "https://bazaarlink.ai/api/v1",
  apiKey: process.env.BAZAARLINK_API_KEY,
});

const speech = await client.audio.speech.create({
  model: "openai/gpt-4o-mini-tts-2025-12-15",
  voice: "coral",
  input: "Hello from BazaarLink.",
  response_format: "mp3",
});
fs.writeFileSync("hello.mp3", Buffer.from(await speech.arrayBuffer()));
```

```bash
curl https://bazaarlink.ai/api/v1/audio/speech \
  -H "Authorization: Bearer sk-bl-YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"openai/gpt-4o-mini-tts-2025-12-15","voice":"coral","input":"Hello from BazaarLink.","response_format":"mp3"}' \
  --output hello.mp3
```

**Voices (openai/gpt-4o-mini-tts):** `alloy`, `ash`, `ballad`, `coral`, `echo`, `fable`, `nova`, `onyx`, `sage`, `shimmer`, `verse`. Female-leaning: `coral`, `nova`, `sage`, `shimmer`.

**Response formats:** `mp3` (default), `opus`, `aac`, `flac`, `wav`, `pcm`. Each model accepts a subset — `mp3` and `pcm` are the safest defaults.

**Limits:** `input` is 1–4096 characters per request.

**Steering (`instructions` field, gpt-4o-mini-tts only):** natural-language voice direction. Less is more — heavy prompts (e.g. "cute, melodic, singing-style") can over-emote; leave empty for natural delivery.

**Billing:** by character. `openai/gpt-4o-mini-tts*` is `$0.60 / 1M chars`. Cost is recorded server-side; query `/api/v1/usage` for spend.

---

## Streaming

### OpenAI-compatible streaming

```python
stream = client.chat.completions.create(
    model="openai/gpt-4o-mini",
    messages=[{"role": "user", "content": "Count to 5"}],
    stream=True,
)

for chunk in stream:
    print(chunk.choices[0].delta.content or "", end="")
```

### Anthropic-compatible streaming

`POST /api/v1/messages` also supports streaming and non-streaming responses.

---

## Tool Calling

### OpenAI-style tools

```python
tools = [{
    "type": "function",
    "function": {
        "name": "get_weather",
        "description": "Get current weather for a city",
        "parameters": {
            "type": "object",
            "properties": {
                "city": {"type": "string"}
            },
            "required": ["city"]
        }
    }
}]

response = client.chat.completions.create(
    model="openai/gpt-4o",
    messages=[{"role": "user", "content": "Weather in Taipei?"}],
    tools=tools,
)
```

### Anthropic-style tools

`POST /api/v1/messages` forwards Anthropic-format tool definitions and tool-use flows to the upstream provider.

---

## BazaarLink-Specific Routing Features

The OpenAI-compatible chat route supports BazaarLink/OpenRouter-style routing fields in addition to standard OpenAI fields.

| Field | Type | Purpose |
|-------|------|---------|
| `provider` | object | Provider preferences such as `order`, `only`, `ignore`, `allow_fallbacks`, `sort` |
| `models` | string[] | Ordered fallback model list |
| `route` | string | Routing mode, such as `fallback` |
| `transforms` | string[] | Request transforms such as context/window transforms |
| `debug` | object | Debug options for streaming requests |

### Auto Router

Set `model` to `"auto"` (paid) or `"auto:free"` (free) — the router analyzes the prompt and picks the best model automatically.

| Mode | Model Pool | Cost |
|------|-----------|------|
| `auto` | Premium models (GPT-5, Claude Opus, Gemini Pro…) | Charged at resolved model's rate |
| `auto:free` | Free models (DeepSeek, Gemini Flash Lite…) | No credits deducted; RPM + daily limits apply |

The router uses weighted scoring across English + Chinese keywords and structural features to classify prompts into 10 task types: `tool_call`, `trivial`, `simple_qa`, `coding`, `math_reasoning`, `deep_analysis`, `creative`, `instruction`, `complex_chat`, `general`.

The resolved model is returned in `X-Auto-Resolved-Model` response header and the `model` field of the response body.

```python
# Paid auto routing
response = client.chat.completions.create(
    model="auto",
    messages=[{"role": "user", "content": "Prove that √2 is irrational"}]
)
print(response.model)  # e.g. "anthropic/claude-opus-4-6"

# Free auto routing — no credits needed
response = client.chat.completions.create(
    model="auto:free",
    messages=[{"role": "user", "content": "Write a Python sort function"}]
)
print(response.model)  # e.g. "deepseek/deepseek-chat"
```

```bash
# cURL
curl https://bazaarlink.ai/api/v1/chat/completions \
  -H "Authorization: Bearer sk-bl-YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model": "auto:free", "messages": [{"role": "user", "content": "Hello"}]}'
```

### Provider preferences

```json
{
  "model": "openai/gpt-4.1",
  "messages": [{"role": "user", "content": "Hello"}],
  "provider": {
    "order": ["openai", "anthropic"],
    "allow_fallbacks": true,
    "sort": "latency"
  }
}
```

### Model fallback

```json
{
  "model": "openai/gpt-4.1",
  "models": [
    "openai/gpt-4.1",
    "anthropic/claude-sonnet-4-6",
    "google/gemini-2.5-flash"
  ],
  "route": "fallback",
  "messages": [{"role": "user", "content": "Write a haiku"}]
}
```

---

## Key and Usage Queries

### Check current key status

```bash
curl https://bazaarlink.ai/api/v1/key \
  -H "Authorization: Bearer sk-bl-YOUR_API_KEY"
```

### Check credits

```bash
curl https://bazaarlink.ai/api/v1/credits \
  -H "Authorization: Bearer sk-bl-YOUR_API_KEY"
```

### Check usage analytics

```bash
curl "https://bazaarlink.ai/api/v1/usage?period=month" \
  -H "Authorization: Bearer sk-bl-YOUR_API_KEY"
```

### Check a generation record

```bash
curl "https://bazaarlink.ai/api/v1/generation?id=gen_123" \
  -H "Authorization: Bearer sk-bl-YOUR_API_KEY"
```

---

## API Key Management

These endpoints are for logged-in sessions or management API keys.

### Create a management key

```bash
curl -X POST https://bazaarlink.ai/api/v1/keys \
  -H "Authorization: Bearer sk-bl-YOUR_MANAGEMENT_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "CI Management Key",
    "keyType": "management"
  }'
```

### Create a standard key with spend limit

```bash
curl -X POST https://bazaarlink.ai/api/v1/keys \
  -H "Authorization: Bearer sk-bl-YOUR_MANAGEMENT_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Production Key",
    "keyType": "standard",
    "limit": 50,
    "limit_reset": "monthly"
  }'
```

### Disable a key

```bash
curl -X PATCH https://bazaarlink.ai/api/v1/keys/key_123 \
  -H "Authorization: Bearer sk-bl-YOUR_MANAGEMENT_KEY" \
  -H "Content-Type: application/json" \
  -d '{"enabled": false}'
```

---

## Cursor IDE Integration

BazaarLink works as a drop-in replacement for OpenAI in Cursor. Cursor talks to BazaarLink using the OpenAI Chat Completions and Responses API formats; BazaarLink converts and routes to whichever provider you choose (OpenAI, Anthropic, Google, etc.).

### Quick setup

1. **Cursor Settings → Models → Override OpenAI Base URL**:
   ```
   https://bazaarlink.ai/v1
   ```
   (Legacy URL `https://bazaarlink.ai/v1/cursor` still works — it's a back-compat re-export.)
2. **Override OpenAI API Key**: paste your `sk-bl-...` BazaarLink key.
3. **Model name**: any model id BazaarLink supports. See the `bz-` prefix below for Claude models.

### `bz-` prefix convention (for Claude)

Cursor's client-side validation reroutes any model name starting with `claude-` through Cursor's own Anthropic integration, bypassing your Override URL. Workaround: prefix the model name with `bz-`. The server strips the prefix and resolves the rest via the alias map.

| In Cursor | What BazaarLink resolves to |
|-----------|------------------------------|
| `bz-claude-sonnet-4.6` | `anthropic/claude-sonnet-4.6` |
| `bz-claude-opus-4-7` | `anthropic/claude-opus-4-7` |
| `gpt-4o` (no prefix needed) | `openai/gpt-4o` |
| `gemini-2.5-flash` | `google/gemini-2.5-flash` |

The dot-vs-hyphen variants are normalized: `bz-claude-sonnet-4.6` and `bz-claude-sonnet-4-6` both resolve to the same model if the alias map only contains one of them.

### `CURSOR_MODEL_MAP` env var (operator override)

Set the env var on your BazaarLink deployment to remap arbitrary Cursor model names:

```bash
CURSOR_MODEL_MAP=gpt-claude-sonnet:anthropic/claude-sonnet-4.6,gpt-opus:anthropic/claude-opus-4-7
```

Now `gpt-claude-sonnet` typed in Cursor maps to `anthropic/claude-sonnet-4.6` server-side. Useful when you want Cursor to think a model is GPT-family (so it routes through the Override URL) while you actually serve Claude.

### What happens automatically

When a request hits `/api/v1/chat/completions` (the unified endpoint), BazaarLink:

1. **Auto-detects Responses API bodies** — if the body has `input` instead of `messages`, converts it to Chat Completions shape (Cursor sends Responses API format for GPT-family models).
2. **Wraps flat tool definitions** — `{ name, description, parameters }` (Cursor Agent's format) becomes `{ type: "function", function: { name, description, parameters } }` so Anthropic doesn't reject as `Tool '' not found in provided tools`.
3. **Coerces malformed `tool_choice`** — Cursor sends `{ type: "auto" }` (object form, no function). Spec is string form for auto/none/required, so we coerce to `"auto"` to keep OpenRouter→Anthropic conversion sane.
4. **Strips OpenAI-only fields for non-OpenAI providers** — `parallel_tool_calls`, `logprobs`, `top_logprobs`, `logit_bias`, `service_tier`, `user` are OpenAI-only. When the resolved model is Anthropic/Google/etc., these are removed before forwarding (Anthropic returns 400 otherwise).
5. **Maps `max_output_tokens` → `max_tokens`** — Responses-API field name to Chat-Completions equivalent.
6. **Strips Responses-API-only fields** — `previous_response_id`, `truncation`, `background`, `store` are deleted on the way through. `reasoning` is kept for Chat-Completions-native bodies (it's a valid OpenRouter passthrough) and stripped only when the body originated as Responses API.

You don't need to do any of this client-side. Just call the endpoint and the request goes through cleanly.

### Cursor Agent mode

Tool calling works through standard Chat Completions tool flow:
- Cursor sends `tools` (Shell, Read, Write, Grep, etc.) + `tool_choice: "auto"`
- BazaarLink forwards to your chosen provider, which decides whether to call a tool
- Tool calls are returned as standard OpenAI `tool_calls` deltas
- Cursor executes locally and sends back a `tool` role message with the result
- Conversation continues until the model produces a final answer

This works the same regardless of whether you pick GPT-4o (native OpenAI) or `bz-claude-sonnet-4.6` (Anthropic via OpenRouter).

---

## Agentic Frameworks

### Vercel AI SDK

```typescript
import { createOpenAI } from "@ai-sdk/openai";
import { generateText } from "ai";

const bazaarlink = createOpenAI({
  baseURL: "https://bazaarlink.ai/api/v1",
  apiKey: "sk-bl-YOUR_API_KEY",
});

const { text } = await generateText({
  model: bazaarlink("anthropic/claude-sonnet-4-6"),
  prompt: "Summarize the latest AI news",
});
```

### LangChain (Python)

```python
from langchain_openai import ChatOpenAI

llm = ChatOpenAI(
    base_url="https://bazaarlink.ai/api/v1",
    api_key="sk-bl-YOUR_API_KEY",
    model="openai/gpt-4o",
)
```

### CrewAI

```python
from crewai import Agent
from langchain_openai import ChatOpenAI

llm = ChatOpenAI(
    base_url="https://bazaarlink.ai/api/v1",
    api_key="sk-bl-YOUR_API_KEY",
    model="anthropic/claude-sonnet-4-6",
)

agent = Agent(role="Researcher", goal="Research topics", llm=llm)
```

### AutoGen

```python
config_list = [{
    "model": "openai/gpt-4o",
    "base_url": "https://bazaarlink.ai/api/v1",
    "api_key": "sk-bl-YOUR_API_KEY",
}]
```

---

## Agent Auto-Registration

AI agents can register without a human in the loop:

```bash
curl -X POST https://bazaarlink.ai/api/v1/agents/register \
  -H "Content-Type: application/json" \
  -d '{"name": "MyAgent", "description": "What you do"}'
```

Response:

```json
{
  "api_key": "sk-bl-...",
  "credits": 0,
  "claim_token": "abc123...",
  "claim_expires": "2026-01-08T00:00:00.000Z",
  "upgrade_url": "https://bazaarlink.ai/claim?token=abc123...",
  "referral_code": "Xk9mQ2bL",
  "message": "No trial credits are included. Your owner must top up credits before use."
}
```

- Current behavior: new agent registrations receive `0` trial credits
- Current behavior: referral codes are issued, but no referral bonus credits are granted by default
- Share `upgrade_url` with the human owner to claim and fund the account
- Rate limit: one registration per IP per 24 hours
- **Agents with 0 credits can immediately use `model: "auto:free"` — no credits needed.** This lets a newly registered agent start working right away without waiting for the owner to top up.

```bash
# Newly registered agent — use auto:free to start immediately
curl https://bazaarlink.ai/api/v1/chat/completions \
  -H "Authorization: Bearer sk-bl-AGENT_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model": "auto:free", "messages": [{"role": "user", "content": "Hello"}]}'
```

---

## Error Codes

| Status | Meaning |
|--------|---------|
| 400 | Bad request, malformed body, or missing provider prefix |
| 401 | Invalid or missing API key |
| 402 | Insufficient credits or budget cap reached |
| 403 | Management key used for model call, or account is unavailable |
| 404 | Missing generation or key resource |
| 429 | Rate limit exceeded |
| 500 | Internal server error |
| 502 | Upstream provider unavailable |
| 503 | No upstream route available or temporary service interruption |

---

## Common Gotchas

1. **Model IDs must include the provider prefix**: use `openai/gpt-4o`, not `gpt-4o`. (For Cursor, the `bz-` prefix convention auto-resolves the canonical id — see the Cursor section.)
2. **Use `/api`, not `/api/v1`, for Anthropic SDK base URLs**: Anthropic SDKs call `/v1/messages`, so set `base_url` to `https://bazaarlink.ai/api`.
3. **Management keys cannot make model calls**: they are for key management only.
4. **Credits are stored in USD-equivalent values**: top-up may be in TWD, while API credit balances are shown as decimal USD-equivalent values.
5. **Check `/api/v1/models` for current context windows and pricing**: limits vary by model.
6. **Claude models in Cursor**: Cursor reroutes any name starting with `claude-` through its own Anthropic integration, ignoring your Override URL. Use the `bz-` prefix (e.g. `bz-claude-sonnet-4.6`) to bypass that — see Cursor IDE Integration above.
7. **Tool calling on Anthropic via OpenRouter**: BazaarLink auto-fixes the common Cursor-Agent shape issues (flat tool defs, malformed `tool_choice`, OpenAI-only fields). You usually don't need to do anything client-side. If you still see `Tool '' not found in provided tools`, check the admin Provider Health panel — every 4xx from upstream stores its full error body and a summary of what we forwarded.

---

## Links

- Dashboard: https://bazaarlink.ai/dashboard
- API Keys: https://bazaarlink.ai/keys
- Models: https://bazaarlink.ai/models
- Pricing: https://bazaarlink.ai/pricing
- Docs: https://bazaarlink.ai/docs
- Skill: https://bazaarlink.ai/skill.md
- LLM summary: https://bazaarlink.ai/llms.txt
- Support: support@bazaarlink.ai
