BazaarLink Documentation
BazaarLink is a unified AI API gateway for Taiwan — providing access to hundreds of models from OpenAI, Anthropic, Google, Meta, and more through a single, OpenAI-compatible API endpoint.
Read https://bazaarlink.ai/skill.md and follow the instructions to integrate BazaarLink into your app.Pricing
BazaarLink prices model usage at zero markup (identical to each provider's official list price). Platform fees are charged at top-up (deposit): a 10% transaction fee, plus 5% Taiwan VAT on TWD channels. Billed in USD with TWD quotes and electronic unified invoices. Self-serve pay-as-you-go top-ups; enterprises may arrange monthly billing (Net-30, negotiable).
How it works
- Consumption (debit): each API call is billed by actual token usage at the provider's official USD list price, deducted from your balance — zero markup, no extra fee on consumption.
- Top-up (deposit): TWD is converted to USD at the real-time selling rate and added to your balance; a 10% transaction fee is charged at top-up.
- • Credit card: an additional US$0.60 flat fee applies; a receipt is issued.
- • TWD channels: 5% Taiwan VAT is added and a Taiwan electronic unified invoice is issued.
- • Bank wire transfer: for larger or enterprise top-ups, contact us to arrange a wire transfer and custom invoicing.
- Invoicing: electronic unified invoices are supported for Taiwan expense workflows; companies needing procurement or monthly billing can arrange enterprise terms (Net-30, negotiable).
About the exchange rate
Foreign-exchange conversion uses the real-time rate; monthly billing uses the rate at billing (statement) time, while prepaid top-ups convert at the top-up-time rate. The rate and timestamp are retained with billing records.
Failed-request billing protection
If an upstream request fails without billable usage, BazaarLink automatically returns the entire reserved amount. You pay $0 for that attempt even if the stream had already started before it failed.
- The upstream cannot connect, rejects the request, or returns no usable response
- A stream stops before a final usage record is received, even after partial content
- The response has no usage data, or only an empty all-zero usage object
0 output tokens does not always mean free
If a request finishes normally and the provider returns valid usage, BazaarLink settles that usage. Do not decide whether a request was free from output tokens alone: a response with 0 output tokens can still be billed for input tokens or a valid upstream-reported cost. Check the returned usage.cost or the Activity record for the final charge.
Quick Start
Three Ways to Integrate
Get started in under 5 minutes. BazaarLink is fully compatible with the OpenAI SDK — just change the
Base URL
https://bazaarlink.ai/api/v1Using the OpenAI SDK
BazaarLink is fully compatible with the OpenAI SDK. Just change the base URL and API key — all other code stays the same.
sk-bl-.✗ gpt-4.1 claude-sonnet-4.6 gemini-2.5-flash
Bring Your Own Key (BYOK)
Attach your own upstream provider API keys (OpenAI- or Anthropic-compatible) to your account or organization — eligible requests then go upstream through your key, with seamless or strict fallback modes. Personal keys live on the BYOK tab of the keys page; organizations manage theirs in org settings. Go to BYOK settings →
Content filtering
Two-way content protection for your API traffic: requests carrying prompt-injection attempts are blocked (400), and sensitive data in prompts or responses (API keys, card numbers, national IDs, and more) is automatically redacted. Rules and an exemption allowlist are customizable, with usage statistics included. Go to content filter settings →
Migrate from OpenRouter
BazaarLink's API is OpenRouter-compatible — most integrations switch by changing two values: the base URL to https://bazaarlink.ai/api/v1 and the API key to a BazaarLink key starting with sk-bl-.
- Base URL: https://openrouter.ai/api/v1 → https://bazaarlink.ai/api/v1
- API key: sk-or-... → sk-bl-... (create one at /keys)
- Model IDs: same provider/model format (e.g. anthropic/claude-sonnet-4.6); full catalog at GET /api/v1/models
- models[] fallbacks, provider routing preferences, streaming, tool calling, and structured outputs use the same request shape
Authentication
All API requests require an Authorization header with your API key.
Authorization: Bearer sk-bl-YOUR_API_KEYGet your API key from the dashboard. Keep your key secure — do not expose it in client-side code.
Optional Headers
Principles
BazaarLink is designed around three core principles:
1. Unified Interface
One API, one SDK, hundreds of models. Switch between OpenAI, Anthropic, Google Gemini, Meta Llama, and others without changing your code — just change the model ID.
2. Price Optimization
BazaarLink automatically routes to the most cost-effective provider for your chosen model. You pay only for what you use, billed in USD with full invoicing support.
3. High Availability
Automatic failover means if a provider goes down, your requests are seamlessly rerouted. No code changes, no downtime.
Multimodal
BazaarLink supports multimodal inputs — send images, audio, and files alongside text to models that support them. Content is passed through to the upstream provider.
Supported Modalities
Examples:
Sending Images
Use the content array format with image_url parts. Supported formats: PNG, JPEG, WebP, and GIF (including animated). You can include multiple images in a single message — each as a separate image_url part:
Limits
BazaarLink enforces two independent limits: a rate limit on requests per minute, and a credit limit on account spending. Exceeding the rate limit returns HTTP 429; running out of credits returns HTTP 402.
Rate Limits
Rate limits are per user (not per key), measured in requests per minute (RPM). There is no daily cap. Tier is determined automatically by your account credit balance.
When a rate limit is exceeded you receive a 429 response with a Retry-After header. Implement exponential backoff when retrying requests.
Response Headers
Every successful response includes rate limit headers for client-side tracking:
X-RateLimit-Limit: 200 # Max requests per minute for your tier
X-RateLimit-Remaining: 198 # Remaining requests in current window
X-RateLimit-Reset: 1740000060 # Unix timestamp when the window resets
X-Request-Id: chatcmpl-abc123 # Unique request ID for debuggingCredit Limits
A 402 response means your account balance or a key's spending cap has hit zero — not that you're sending requests too fast. These responses carry no rate limit headers, and if the limit is hit mid-stream you'll receive an SSE error event instead of an HTTP status change.
Personal Circuit Breaker
A fixed 1-minute and 1-hour USD spend limit applied to all your API keys. New requests get HTTP 429 once a window threshold is hit; the window auto-resets on the clock boundary.
Image Generation
Generate images via /v1/chat/completions with modalities:["image"], or the OpenAI DALL·E-compatible /v1/images/generations.
curl -N https://bazaarlink.ai/api/v1/chat/completions \
-H "Authorization: Bearer $BAZAARLINK_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model":"openai/gpt-5.4-image-2","messages":[{"role":"user","content":"a red cat on a sofa"}],"modalities":["image","text"],"stream":true}'Full flow (streaming, image edits, SSE protocol, model list) →
Video Generation
Async 3-step flow (submit → poll → content). Video generation takes 30 seconds to 5 minutes.
curl https://bazaarlink.ai/api/v1/videos \
-H "Authorization: Bearer $BAZAARLINK_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model":"alibaba/wan2.7-t2v","prompt":"a bird flying over mountains","duration":3}'
# → 202 { "id": "vjob_xxx", "status": "pending" }PDF Inputs
Send PDF documents directly in messages for models that support PDF input natively (e.g. Claude, Gemini). BazaarLink forwards the file straight to the model — billed as ordinary input tokens, no extra charge or processing step.
Supported Formats
- PDF documents (text, images, tables, scanned)
- Base64-encoded data URL (`data:application/pdf;base64,...`)
- Multi-page documents
- Password-free PDFs only
Video Inputs
Send video files to models that support video input for analysis, captioning, or questions about scenes and events. Works with a direct URL or a base64 data URI — a URL is more efficient for publicly accessible video; base64 is for local files or private video.
Supported Formats
MP4 (H.264)MPEGMOVWebMManagement API Keys
Management keys are designed for programmatic key management. They can create, list, update, disable, and delete standard API keys — but cannot make AI model calls.
Creating a Management Key
Go to the Management API Keys page and click "Create" — it's a separate page from your standard API Keys, not a type selector on the same page.
List Keys
Create Sub-Key
Update Key
Revoke Key
DELETE https://bazaarlink.ai/api/v1/keys/:id
Authorization: Bearer sk-bl-YOUR_MGMT_KEY
# Returns 204 No Content on successQuery Balance
Query Usage
GET https://bazaarlink.ai/api/v1/usage?period=month
Authorization: Bearer sk-bl-YOUR_MGMT_KEY
# period: day | week | month | yearApp Attribution
Identify your application in request headers to enable usage tracking, dashboard visibility, and fine-grained analytics.
Available Headers
| Header | Description |
|---|---|
| HTTP-Referer | Your site URL, for usage tracking and analytics (optional) |
| X-Title | Your app name, shown in dashboards (optional) |
Error Codes
Error response format
Model inference endpoints return an OpenAI-compatible error envelope. The type field may vary or be omitted on specialized endpoints; use the HTTP status and error.code for program logic instead of parsing the message.
{
"error": {
"message": "Insufficient credits. Please top up to continue.",
"type": "invalid_request_error",
"code": "insufficient_credits"
}
}HTTP status and error.code
Before streaming starts, the HTTP status identifies the broad failure class. error.code is either that numeric status or a stable string for failures that require a specific remedy. Prefer a string code when present, fall back to the HTTP status, and treat error.message as human-readable text.
Machine-readable billing codes
A 402 response can represent different controls. These stable codes let clients show the correct next action.
Detailed error codes
When an API request fails, error.code tells you the specific reason. Two errors can both use HTTP 400 but require different fixes: unknown_model means the model name is wrong, while image_too_large means the image must be reduced. Use the table below to find the cause and next action.
Rate limits, budgets, and emergency brakes
These controls can reject an otherwise valid request. They are separate from provider failures and require different recovery actions.
Compatibility note: rate-limit and emergency-brake paths currently emit numeric error.code values. Do not assume undocumented string codes. Use HTTP status, Retry-After, and the documented response message until a stable string code is introduced.
Video and media resource state
Video validation commonly returns numeric code 400. A missing job returns 404, a retired model returns 410, unfinished video content returns 409, and an invalid video byte range returns 416. Poll until completion or correct the Range header before retrying.
Retry policy
Retry only failures that may recover without changing the request. When Retry-After is present, wait that long; otherwise use exponential backoff with jitter. Limit attempts and avoid layering manual retries on top of SDK automatic retries.
Handling Errors
Streaming Error Formats
Errors that occur before any tokens are streamed return a standard HTTP error response with a JSON body.
After a stream has started, the HTTP response is already 200. Parse every SSE data frame and treat a top-level error or choices[0].finish_reason === "error" as a failed, incomplete response.
If the stream fails mid-flight, BazaarLink emits a final SSE event with a top-level error object followed by data: [DONE]. Chunks relayed verbatim from some upstreams may instead carry the error on the choice (choices[0].finish_reason === "error") — handle both.
Tool Calling
Tool calling (also known as function calling) lets models invoke external functions you define. The model decides when to call a tool and generates structured arguments — your code executes the function and returns results to continue the conversation.
Supported Models
Most frontier models support tool calling. Here are some popular choices:
Defining Tools
Each tool is a JSON object describing a function the model can call. The parameters field uses JSON Schema.
tool_choice Options
Complete Flow
Tool calling is a multi-turn process: (1) send the request with tools → (2) model returns tool_calls → (3) execute functions → (4) send results back → (5) model generates final response.
Parallel Tool Calls
Some models can call multiple tools in a single response. Handle each tool call and return all results:
Streaming Tool Calls
When streaming, tool calls arrive as partial deltas indexed by position — accumulate each delta's argument string by index until finish_reason becomes "tool_calls", which signals the call is complete.
Simple Agentic Loop
A generic pattern that keeps calling the model while it keeps requesting tools, and stops once it returns a final answer — use max_iterations to guard against infinite loops.
Function Definition Best Practices
- Use specific, descriptive names — get_weather_forecast rather than just weather.
- Write a clear description of what the function does and when to use it — the model relies solely on this text to decide whether to call it.
- Constrain values with enum where possible and include an example in the description to reduce malformed arguments.
- Only mark fields required that are truly required — optional fields should be genuinely omittable.
Structured Output
Force the model to return valid JSON matching a schema. This is essential for building reliable applications that parse model outputs programmatically.
Method 1: response_format (JSON Schema)
to enforce strict JSON Schema compliance:
Tips
- Use clear, descriptive property names — the model uses them as context.
- Add descriptions to schema properties to guide the model.
- Set strict: true for guaranteed schema compliance (may increase latency slightly).
- Keep schemas simple — deeply nested schemas may reduce output quality.
- Test with different models — some handle complex schemas better than others.
Assistant Prefill
Add a partial assistant message as the last item to request continuation on compatible model routes.
Message Transforms
Automatically transform messages to fit within model context limits. When your messages exceed a model's context window, transforms intelligently condense the conversation by removing messages from the middle.
Usage
Transform Types
Default Behavior
Models with ≤8k context have middle-out enabled automatically. For larger context models, opt in explicitly. Anthropic Claude models also automatically enforce the 1,000-message limit regardless of the transforms setting.
Zero Data Retention
BazaarLink does not store your message content by default. This page describes how your data is handled. Suitable for applications processing sensitive data.
Current Data Handling
- Message content: not stored by default, discarded from memory after processing
- Billing metadata: token counts, timestamps, model IDs
- Usage logs: request statistics only, no message content
- Upstream forwarding: messages forwarded to upstream providers — subject to their privacy policies
Prompt Caching
Prompt caching reuses previously computed prompt tokens, significantly reducing cost and latency — especially for applications with large, repeated system prompts.
How It Works
Whether caching needs configuration depends on the provider. OpenAI-family models cache long, repeated prompt prefixes automatically — no request changes needed. Claude (Anthropic) models only cache when the request carries an explicit cache_control breakpoint; BazaarLink does not add this for you, so a Claude request with no marker is never cached. BazaarLink forwards whatever cache markers you send as-is and reports the resulting cache read/write tokens in the usage response.
Reasoning Tokens
Reasoning models (e.g., DeepSeek R1, o1 series) think internally before producing their final answer. These internal tokens are called reasoning tokens and are billed separately.
Reading Reasoning Tokens from Responses
Thinking Mode Control
Some models support toggling their "thinking" mode. Thinking mode generates internal reasoning tokens before producing the final answer, improving quality at the cost of more tokens.
| Model Family | Parameter | Default |
|---|---|---|
| qwen3-* | enable_thinking: boolean | false (platform default) |
| openai/o1, o3, o4-mini | reasoning_effort: "low" | "medium" | "high" | medium |
| deepseek/deepseek-r1 | — | Always enabled (cannot disable) |
Unified reasoning Object (New Format)
BazaarLink also supports the unified reasoning object, which works across all model families with a single consistent API:
| Field | Values | Applies to |
|---|---|---|
| reasoning.effort | "xhigh" | "high" | "medium" | "low" | "none" | OpenAI o-series, Grok |
| reasoning.max_tokens | integer | Anthropic Claude, Gemini |
| reasoning.exclude | boolean | Hide thinking from response (model still reasons) |
Latency & Performance
Optimizing AI API response latency is critical for user experience. Below are the key factors that affect latency in the BazaarLink architecture and best practices for optimization.
Factors That Affect Latency
- Model size: larger models (70B+) are generally slower to generate
- Provider load: varies across providers and time of day
- Token count: higher max_tokens means longer completion time
- Streaming vs. non-streaming: stream: true delivers the first token faster
- Context length: very long contexts increase pre-processing time
Optimization Tips
- Prefer streaming (stream: true) to improve perceived latency
- Use the :nitro variant to select high-throughput providers
- Choose smaller models (flash/mini/haiku) for latency-sensitive scenarios
- Use provider.sort: "latency" to automatically select the lowest-latency provider
- Enable prompt caching to reduce latency for repeated requests
Uptime Optimization
BazaarLink maximizes API availability through multiple layers: automatic failover, circuit breakers, and provider health monitoring.
Availability Mechanisms
- Circuit breaker: auto-detects and isolates failing providers
- Automatic failover: seamlessly switches to a backup provider — no code changes needed
- Provider health monitoring: continuously tracks error rates and latency per provider
- Retry logic: transient errors (5xx) are automatically retried
Circuit Breaker
Guardrails
Add content-safety mechanisms to your API requests to filter harmful content and enforce compliance policies. BazaarLink currently offers customizable content-filter guardrails only at the organization level; personal (non-org) API keys have no equivalent setting — content safety relies entirely on each upstream model provider's own built-in safety systems.
Planned Features (not yet available for either personal or org keys)
Current Behavior
Personal API keys: all upstream providers have their own content safety systems — model responses that trigger content filters return with finish_reason: "content_filter", and BazaarLink applies no additional filtering. Organization API keys: org_admin can configure custom rules (block/redact/flag) under "Content Filter Guardrails," applied before text reaches the model.
Cursor IDE Integration
Use BazaarLink as Cursor's OpenAI Override URL. Drop-in setup with auto-conversion of Responses API bodies, tool-format normalization, and the bz- prefix convention for Claude models.
Quick setup
In Cursor, open Settings → Models, then:
- Set Override OpenAI Base URL to https://bazaarlink.ai/v1
- Set Override OpenAI API Key to your sk-bl-... BazaarLink key
- Add the model name you want — see below for Claude (the bz- prefix).
The bz- prefix (for Claude models)
Cursor's client-side validation reroutes any model name starting with claude- through Cursor's own Anthropic integration, bypassing your Override URL. To force Cursor to send the request to BazaarLink, prefix the model name with bz-. The server strips the prefix and resolves the rest via the alias map.
The dot-vs-hyphen variants are normalized: bz-claude-sonnet-4.6 and bz-claude-sonnet-4-6 both resolve to the same model.
CURSOR_MODEL_MAP env var (operator override)
For self-hosted BazaarLink deployments, set this env var to remap arbitrary Cursor-side model names to catalog canonical ids:
CURSOR_MODEL_MAP=gpt-claude-sonnet:anthropic/claude-sonnet-4.6,gpt-opus:anthropic/claude-opus-4.7Now 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, BazaarLink applies these compatibility transforms transparently — you don't need to do anything client-side:
- Auto-detects Responses API bodies — if the body has input instead of messages, it's converted to Chat Completions shape (Cursor sends Responses API format for GPT-family models).
- Wraps flat tool definitions — Cursor Agent sends { name, description, parameters } without a function wrapper. We wrap it so Anthropic doesn't reject as Tool '' not found in provided tools.
- Coerces malformed tool_choice — Cursor sends { type: "auto" } (object form, no function). The OpenAI spec requires the string form for auto/none/required, so we coerce.
- Strips OpenAI-only fields when routing to non-OpenAI providers — parallel_tool_calls, logprobs, top_logprobs, logit_bias, service_tier, user are removed before forwarding (Anthropic returns 400 otherwise).
- Maps max_output_tokens → max_tokens and strips Responses-API-only fields (previous_response_id, truncation, background, store). The reasoning field is preserved for Chat-Completions-native bodies.
Cursor Agent mode
Tool calling works through the standard Chat Completions tool-call flow. Cursor sends tools (Shell, Read, Write, Grep, etc.) with tool_choice: "auto"; BazaarLink forwards to your chosen provider, which decides whether to call a tool. Tool calls return as standard OpenAI tool_calls deltas; Cursor executes locally and continues the conversation. Works the same regardless of whether you pick gpt-4o (native OpenAI) or bz-claude-sonnet-4.6.
Model Routing
BazaarLink uses the provider/model-name format to route requests to the correct upstream provider. This gives you access to all major models through a single API endpoint.
Model ID Format
{provider}/{model-name}
# Examples
openai/gpt-5.4-mini
anthropic/claude-sonnet-4.6
google/gemini-3-flash-preview
deepseek/deepseek-v3.2Routing Priority
When you send a request, BazaarLink resolves the upstream provider in this order:
- Exact match — looks for a model route matching the full model ID
- Provider wildcard — falls back to provider/* routes (e.g. openai/*)
- Global wildcard — falls back to * wildcard routes
- Default provider key — for a known catalog model only, uses enabled keys marked as default
Browse all available models on the Models page.
Auto Router
Auto Router v3 scores the request into one of 14 task tiers, then uses the current primary and fallback chain configured for that tier. The paid and free tables are managed separately in the admin console.
- auto — paid routing table. The successful resolved model is billed at its published price.
- auto:free — free routing table. Calls within the free quota cost $0; after the quota, funded accounts may switch to paid auto routing unless paid fallback is disabled.
How to Use
Set the model to "auto" (paid) or "auto:free" (free) to enable automatic routing:
curl https://bazaarlink.ai/api/v1/chat/completions \
-H "Authorization: Bearer $BAZAARLINK_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model":"auto","messages":[{"role":"user","content":"Review this TypeScript function"}]}'How v3 selects a tier
General tiers are simple, standard, complex and reasoning. Specialized tiers are coding, vision, image, video, data, search, social, email, calendar and trading. Low-confidence boundary results are promoted one level instead of guessing downward.
- Tier scoring: messages, tools, length, keywords and structural signals select one of 14 tiers
- Hard overrides: vision, formal reasoning and specialized tasks can select a tier directly
- Route lookup: the selected mode reads that tier's current primary and up to five fallbacks; a disabled tier returns 503
- Execution: BazaarLink tries the primary, then the configured fallback chain in order
- Response tracking: the resolved model is returned in the response body and X-Auto-Resolved-Model header
Current model tables
The tables below are read from the same live configuration used by inference and the admin console. Administrators can change each tier's primary model, fallback order and enabled state without a deployment.
auto
auto:free
Selected models offer a rate-limited free tier. Free eligibility is granted per model by the platform — call the model by its regular ID; the :free suffix is an optional alias (adding it to a paid model does not make it free).
Model Variants
Append a suffix to any model ID to change routing behaviour. BazaarLink supports 7 variant types.
Independent Model IDs
These variants exist as separate models with their own pricing and capabilities. BazaarLink tries the full model ID (with suffix) first, then falls back to the base model.
:free
:extended
:thinking
:exactoRouting Shortcuts
These suffixes modify provider selection without changing the model identity. The suffix is stripped before matching routes.
:floor # lowest listed input price first
:nitro # throughput-oriented shortcut
:online # enable web-search routingMulti-Provider Behaviour
For upstreams that support variants, suffixes are passed through as-is. For direct providers (e.g., direct OpenAI, Fireworks), the suffix is stripped and BazaarLink handles routing locally.
Free Models
Selected models offer a rate-limited free tier. Free eligibility is granted per model by the platform — call the model by its regular ID; the :free suffix is an optional alias (adding it to a paid model does not make it free).
- Call the regular model ID (e.g. deepseek/deepseek-v4-flash). Requests are automatically served free of charge while within the free quota.
- Free usage is limited per user by requests-per-minute and a daily cap. Limits scale with account tier (no credit / credited).
- When you exceed the free quota and have credits, requests automatically continue on the paid tier at the listed price. Send X-Free-Fallback: false to disable automatic fallback and receive 429 instead. Without credits, over-quota requests return 429.
- GET /api/v1/models lists a :free entry for every model with a free tier; auto:free always routes to a free model.
Models with a free tier right now
Call these model IDs directly to use the free quota. The list changes as models are added or retired — query the API for the live set.
deepseek/deepseek-v4-flashFree quota limits
Your daily budget = the daily request budget above × your account tier multiplier, counted separately per free model. auto:free additionally enforces a parallel per-IP cap. Individual models may carry tighter or looser limits set by the platform — the model page's "Free tier" block always shows the effective values.
After the free quota runs out
Once the quota is spent, requests continue automatically at the model's paid price as long as your account has balance — no service interruption, billed exactly like any paid call. If you would rather fail than be billed, send the X-Free-Fallback: false header or disable automatic fallback in the key settings; you then get a 429 instead. With no balance, over-quota requests always return 429.
# Return 429 instead of switching to paid routing
-H "X-Free-Fallback: false"Organization Management
When several people share BazaarLink, create an organization to group members into teams and manage API keys, allowed models, budgets, and billing in one place. The organization owns the shared balance, while teams and members can have their own monthly limits.
Three-Tier Budget System
Every API request checks member, team, and organization limits. Monthly budget or balance failures return HTTP 402; a spend emergency brake returns HTTP 429.
- Member monthly budget (OrgMember.monthlyBudget)
- Team monthly budget (Team.monthlyBudget)
- Org credits balance (Organization.credits)
Usage Reports
Open an organization and select Reports to view monthly costs in four ways:
- Overview: total spend, margin rate, daily trend chart
- By Team: per-team spend, share %, model breakdown, budget utilization
- By Model: per-model spend, avg price ($/1M tokens)
- By Member: per-member spend — org_admin only
All views support CSV export with BOM prefix for direct Excel compatibility.
Create & Manage Organizations
- Open Settings and create an organization in the Organizations section
- Select the organization name to open its management area
- Create teams and optionally assign cost-center codes and monthly budgets
- Invite members by email, assign a role and Team
- Issue API keys for members — usage is automatically tagged to the correct Team / member
- View the Reports page for monthly spend broken down by Team, Model, or Member
Member Roles
What else can an organization manage?
Beyond members and teams, the organization management area provides:
- API keys: create keys for an organization, team, or member and restrict their allowed models
- Content filtering: block, redact, or log sensitive text before it reaches a model
- Allowed Models: restrict models for the organization, a team, a member, or an individual API key
- Budgets and emergency brakes: set monthly limits and minute/hour spend protection
- Reports and billing: review spend, model usage, team allocation, balance, credit limits, and payments
- Change and security logs: track settings changes, content-filter hits, and security events
- Institution plans: education organizations can additionally manage student sessions and quotas
Content filtering
These are organization-owned rules that inspect text before it is sent to a model through an organization API key. An org_admin can enable, edit, and test rules under Settings → Content filtering.
- block: reject the whole request with HTTP 403 before it reaches the model
- redact: replace matched text with [REDACTED], then send the cleaned text
- flag: send the request unchanged and record the match in the organization audit log
- Use built-in sensitive-data and prompt-injection templates, or create keyword and regular-expression rules
- Up to 100 rules; regular expressions are safety-checked and rules can be previewed with test text
Management API (v1)
Use the Management API to list organizations, create teams, add members, or update budgets without clicking through the web UI. `/api/v1/orgs` handles organization and member management; cost reports use the separate `/api/orgs/:orgId/reports/*` paths below.
Organizations
/api/v1/orgsList all organizations the caller belongs to, with role and joinedAt.
/api/v1/orgs/:orgIdGet org detail including team and member counts.
curl https://bazaarlink.ai/api/v1/orgs \
-H "Authorization: Bearer sk-bl-YOUR_MANAGEMENT_KEY"Teams
/api/v1/orgs/:orgId/teamsList teams with member counts, ordered by name.
/api/v1/orgs/:orgId/teams/api/v1/orgs/:orgId/teams/:teamIdPartial update — include only the fields to change.
/api/v1/orgs/:orgId/teams/:teamId# Create a team
curl https://bazaarlink.ai/api/v1/orgs/{orgId}/teams \
-X POST \
-H "Authorization: Bearer sk-bl-YOUR_MANAGEMENT_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "Engineering", "costCenterCode": "ENG-001", "monthlyBudget": 500}'Members
/api/v1/orgs/:orgId/membersList all members with nested user (id/name/email) and team info.
/api/v1/orgs/:orgId/members404 if the email address has no BazaarLink account. 409 if already a member. Default role: member.
/api/v1/orgs/:orgId/members/:memberIdPartial update of role, teamId, or monthlyBudget.
/api/v1/orgs/:orgId/members/:memberIdReturns 400 if the target is the last org_admin.
Reports API
The Reports API powers the organization Reports page and can also feed internal dashboards, monthly reconciliation, or automated CSV downloads. org_admin can access every report; billing_viewer can access overview, team, and model reports, but not per-member spend. It accepts a signed-in session or Bearer management key.
Pass both year and month (1–12). overview and by-team default to the current month when omitted, while by-model, by-member, and export require both values. Supplying both every time avoids endpoint-specific surprises.
Error response reference
Allowed Models (Whitelist)
Restrict which models your organization, teams, or individual members can call. Useful for blocking expensive or unvetted models, enforcing model standards, or scoping a team to a single provider.
How it works
- Three independent layers — Organization, Team, Member — each holds its own list (String[] in the database).
- When all three layers are empty, every model is allowed (default behavior).
- When one or more layers are non-empty, the effective list is the intersection of the non-empty layers — a model must be allowed at every restricted layer to pass.
- Changes take effect within a few seconds (60s in-memory + 5min Redis cache; both are cleared on update).
Pattern format
- Exact match — e.g. openai/gpt-4o (this exact model only).
- Provider wildcard — e.g. openai/* (any model under the openai/ prefix).
- Lowercase only. Max 200 entries per list, 100 chars per entry.
Where to manage
Org Portal → Allowed Models. org_admin can edit org / team / member lists; team_admin can edit their own team and members within it.
Error response when blocked
Calls to a disallowed model return HTTP 403 with this body:
Management API
All endpoints accept Web Session or Bearer Management Key (sk-bl-...). PATCH replaces the entire list; pass [] to clear.
Circuit Breaker (Spend Kill Switch)
A dual-window spend cap that blocks further requests when upstream cost spikes. Designed to contain runaway scripts, infinite loops, or stolen-key abuse before they cost real money.
How it works
- Two fixed windows are tracked in Redis per scope: 1-minute and 1-hour upstream cost (USD).
- If either window's spend reaches its threshold, all subsequent requests in that scope are rejected until the window resets.
- Defaults: $5 / minute, $20 / hour, enabled by default.
- Counters live in Redis with TTL — recovery is automatic, no manual reset needed for org/team/member trips.
Scopes (member overrides team overrides org)
Each layer can set its own thresholds. Resolution order is member → team → org → platform default — the first non-null value wins per field (cbEnabled, cbMinuteUsd, cbHourlyUsd).
- Org level — applies to all keys under the organization. Set in Org Portal → Circuit Breaker.
- Team level — applies to all keys tagged to that team. Overrides org for those keys.
- Member level — applies only to keys tagged to that member. Overrides team and org.
Trip behavior
When tripped, requests fail fast (no upstream call is made). The response is HTTP 429 with this body:
Audit log
Every trip event and every configuration change is recorded:
- Trip events — actions org.cb.tripped / team.cb.tripped / org_member.cb.tripped. Deduplicated to one entry per scope+window per hour, so a sustained trip doesn't flood the log.
- Config changes — actions org.cb.update / team.cb.update / org_member.cb.update. Capture before/after values plus the actor.
Management API
Org admins can read and update settings via API. All endpoints accept Web Session or Bearer Management Key (sk-bl-...). Send any subset of fields in the PATCH body; null clears a field and falls back to the parent layer.
API Key Rotation
Regularly rotating API keys is a security best practice. BazaarLink supports zero-downtime key rotation — create a new key first, then migrate, then revoke the old one.
Rotation Steps
- Create a new API key
- Update your application or environment variables to use the new key
- Verify the new key is working correctly
- Disable or delete the old key
Activity Export
Download your complete API usage history as CSV for financial audits, cost analysis, or compliance reporting.
CSV Export
Log in and go to the Logs page. Click the Export CSV button in the top-right corner to download your full history as a CSV file. No API call required.
CSV Columns
JSON Usage API
For programmatic access, query aggregated stats grouped by period, model, or key:
Usage Accounting
Query detailed usage statistics via API, including token consumption, cost analysis, and request history.
Response Field Reference
| Field | Type | Description |
|---|---|---|
| model | string | Model ID used (e.g., openai/gpt-4o) |
| provider | string | Upstream provider name |
| prompt_tokens | number | Input tokens consumed |
| completion_tokens | number | Output tokens generated |
| total_tokens | number | Total tokens (prompt + completion) |
| reasoning_tokens | number | Reasoning tokens (for thinking models) |
| cached_tokens | number | Prompt tokens served from cache |
| cost | number | Total cost in USD credits |
| duration_ms | number | End-to-end latency in milliseconds |
| throughput | number | Generation speed in tokens/sec |
| finish_reason | string | stop | length | content_filter | error |
| status | number | HTTP status code from upstream |
| app_name | string | null | Application name (X-Title header) |
| key_name | string | API key name used for the request |
Institution Plan
The Institution Plan lets any institution (school, enterprise, conference, government, etc.) issue short-lived session tokens to its members from a single org-level key. Members don't need to create a platform account. The org controls which members can request tokens by email domain (e.g. nthu.edu.tw); all usage is billed to the org's account. This page uses the education scenario as an example — the same mechanism works for any institution that needs short-term, multi-user temporary access.
Architecture overview
- Institution Key — Starts with sk-edu-. Created by an org_admin on the org keys page. Cannot be used directly as a Bearer token to call the API — direct calls return 403.
- Member Session Token — Starts with edu-sess-. Members obtain it after email verification. Default lifetime is 24 hours; revocable by an org admin.
- Allowed Domains — The org configures which email domains (exact match, no suffix bypass) may request a session.
- Usage attribution — All student requests are billed to the org account. Usage is viewable per session and per email in the org dashboard.
Step 1 — Request Institution Plan activation
Contact BazaarLink sales or support (sales@bazaarlink.ai / support@bazaarlink.ai) and let us know your organization wants the Institution Plan enabled, with the list of allowed email domains (e.g. nthu.edu.tw). We will activate the feature for your organization:
Step 2 — Org admin creates an Institution Key
On the org's API Keys page, choose "Education" as the key type when creating a new key (this is the internal codename for the institution key). The system generates an sk-edu-... key and shows it ONCE — save it and distribute it through your official channels to that org's members.
Step 3 — Member requests a verification code
Members can request a verification code in two ways: (a) visit /access and enter the institution key + their institutional email — the page calls the API for them; (b) call the API directly:
/api/edu/request-codeStep 4 — Member submits the code to exchange for a session token
/api/edu/verifyStep 5 — Use the session token to call the API
Use the edu-sess-... token as a Bearer token against any chat / completions / embeddings endpoint:
curl -X POST https://bazaarlink.ai/api/v1/chat/completions \
-H "Authorization: Bearer edu-sess-827d11a1ec67d175cfd4f67f929261f4" \
-H "Content-Type: application/json" \
-d '{
"model": "anthropic/claude-haiku-4.5",
"messages": [{"role": "user", "content": "Hello"}]
}'403 — Education keys cannot be used directly. Visit /access to exchange for a session.This is an intentional reverse gate — it stops the institution from leaking long-lived keys to individual members.
Org dashboard — monitoring and revocation
Education-type orgs get an Education tab in the side nav, providing:
- Settings — Adjust allowed domains, TTL, max sessions per email per key, and per-session request / token / USD quotas.
- Sessions — List all active / expired / revoked sessions; filter by email; revoke individual sessions.
- Usage stats — Per-session call count, token consumption, and accumulated cost.
Security and limits
| Item | Default | Description |
|---|---|---|
| Session TTL | 24 hours | Session token lifetime; expired sessions require re-verification. |
| Verification code TTL | 15 minutes | Lifetime of the email verification code. |
| Verification code length | 6 digits | Stored as an HMAC-SHA256 hash in Redis, never in plaintext. |
| Guess limit | 5 attempts | Beyond this the code is invalidated immediately. |
| request-code cooldown | 60 seconds | Minimum interval between repeated requests for the same (key, email). |
| Per-IP rate limit | 10 / 15 min | Anti-spam. |
| Per-key rate limit | 100 / hour | Prevents bulk email blasts. |
| Max sessions per email | 5 | Configurable in eduConfig; prevents one inbox from hoarding tokens. |
| Revocation propagation | ≤ 60 seconds | L1/L2 cache TTL; after DB revocation it takes up to 60 seconds to propagate to all nodes. |
Billing and usage attribution
All requests made via session tokens are billed 100% to the organization that owns the institution key, in line with how upstream providers (OpenAI / Anthropic / etc.) bill (per-token). The org dashboard supports drill-down by session, by email, and by key.
Report Feedback
Help us improve BazaarLink by reporting issues, bugs, or suggestions. We actively monitor all feedback channels.
How to Report
What to Include
- Request ID (from the response id field)
- Model used and parameters sent
- Expected vs actual behavior
- Timestamps and frequency of issue
- Error messages or HTTP status codes