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.
- • Payment options and any channel-specific fees are shown at live checkout.
- • Eligible TWD channels: applicable VAT and Taiwan e-invoice terms are shown before payment.
- • Bank wire transfer: for larger or enterprise top-ups, contact us to arrange a wire transfer and custom invoicing.
- Invoicing: OEN (credit card) top-ups issue a Taiwan e-invoice automatically and email it to you, with an optional tax ID. Companies needing procurement or monthly billing should contact sales (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
usage.cost is the USD amount charged for the request. A response with 0 output tokens can still incur input-token charges; check usage.cost or the Activity record for the final amount.
Quick Start
Three Ways to Integrate
Get started in under 5 minutes. BazaarLink is fully compatible with the OpenAI SDK — just change the
For requests longer than 100 seconds, we recommend using stream:true. For non-streaming requests, after about 12 seconds (the default, adjustable through deployment settings), a 200 response is sent first and keepalive whitespace starts; if an error occurs afterward, it is returned as 200 + error JSON. Keepalive is whitespace before the JSON body, which standard JSON parsers ignore.
Base URL
https://api.bazaarlink.ai/v1api.bazaarlink.ai is an API-only entry point, deployed separately from the website. The previous base URL https://bazaarlink.ai/api/v1 remains fully supported — existing integrations do not need to change.
Using 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-.Bring Your Own Key (BYOK)
Attach an upstream API key to your account or organization. Eligible requests use your key first. After an upstream error, a request can try another available source; if none is available, it returns an error. Manage personal keys on the keys page and organization keys in organization 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 →
Web search (:online)
Searches the web before the model answers and provides the results as reference material. This is useful for questions that need current information, such as news, prices, recently published policies, or products.
Supported endpoints
POST /api/v1/chat/completionsPOST /api/v1/messagesPOST /api/v1/responses
Both streaming (stream: true) and non-streaming requests are supported.
Usage
Method 1: append :online to the model name
curl https://bazaarlink.ai/api/v1/chat/completions \
-H "Authorization: Bearer $BAZAARLINK_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-5.4-mini:online",
"messages": [{ "role": "user", "content": "What AI news is there in Taiwan today?" }]
}'Method 2: use the plugins parameter
Method 3: use web_search_options
{
"model": "gpt-5.4-mini",
"messages": [{ "role": "user", "content": "..." }],
"web_search_options": { "search_context_size": "high", "query_source": "conversation" }
}You can use any one of these methods, or combine them.
Search parameters
The following parameters can be placed in the web item of plugins or in web_search_options; when both specify different values, plugins takes precedence.
search_promptAny text—Directly sets the search query. When provided, it takes priority over query_source.query_sourcelast_user, conversationlast_userThe source of the search query when search_prompt is absent. last_user uses only the latest user message; conversation uses the whole conversation, excluding system messages.countryLowercase English country name (for example, taiwan, japan, united states)All regionsPrioritizes sources from the specified country. If omitted, the search is not limited by region. When country is set, the search type is fixed to general search (general); specifying topic: "news" at the same time returns 400.topicgeneral, newsgeneralThe search type. If omitted, general search (general) is used; set topic: "news" explicitly when you need news results.search_queriesString array, 1–5 items—Specifies multiple search queries at once. Each query runs once and incurs one search charge; results are merged and duplicate URLs are removed before they are provided to the model. Cannot be used with search_prompt. If some queries fail, the successful results are used and only successful queries are charged; all failures return 503 without a charge.time_rangeday, week, month, year (also accepts common forms such as d/w/m/y, past_day, 24h, 7d, 30d, 365d; the parameter name may also be recency or search_recency_filter)No limitSearches only content from the specified period.languageISO 639-1 language code (such as zh, en, ja), single value or arrayNo limitPrioritizes content in the specified language. When an array is provided, only the first language is currently used.include_domainsString array of domains, with wildcards (for example, *.gov.tw)—Searches only these domains, up to 300 domains with a maximum of 253 characters per domain.exclude_domainsString array of domains, with wildcards—Excludes these domains, up to 150 domains.max_results1–10By levelThe number of reference sources returned. Can also be written as num_results.enginebasic, fast, ultra-fast, advancedBy levelDirectly selects search depth and has the highest priority. Other values, including search engine names from other platforms, use the default depth and do not return an error. (plugins only)modeturbo, instant, fast, basic, advanced, deep—Another way to select depth: turbo/instant → fastest, fast → fast, basic → standard, and advanced/deep → advanced. Unrecognized values are ignored.search_context_sizelow, medium, high, fastmediumSearch level; see the table below. (web_search_options only)Depth priority: engine (the four values above) > mode > search_context_size > default medium.
The parameter format of plugins and web_search_options is compatible with OpenRouter, so existing requests can be used directly.
Search tool syntax also compatible with Vercel AI Gateway
Chat Completions tools also accepts the following search tool format. It is treated as one web search and handled by this platform's web search service, with the same billing and citation format:
- Accepted
typevalues:vercel:perplexity_search,vercel:exa_search,vercel:parallel_search, andvercel:tako_search. Thesetypevalues behave and cost the same. config.queryis treated assearch_prompt;config.max_results(ornum_results) andconfig.countrymap to the same-named parameters above. Unrecognized fields are ignored.- These search tools are not forwarded to the model.
- When one request contains multiple search tools, only the first is used.
`web_search_preview` tool is also accepted
In tools for Responses and other endpoints, { "type": "web_search_preview" } is likewise treated as one web search and handled by this platform's web search service, with the same billing and citation format. This tool is not forwarded to the model.
When multiple formats are used together
:online, plugins, web_search_options, and search tools (including web_search_preview) can appear together, but each request performs only one search and incurs one search charge; search_queries is charged per query. Search-query priority is search_queries > the search tool's config.query > search_prompt > query_source. Other parameters are merged according to the parameter priorities above.
A 400 invalid_web_search_options is returned only for parameter type errors, such as passing a number to engine or a non-string array to include_domains, or for values outside the allowed range.
Search levels
lowStandardUp to 5More concisemediumStandardUp to 10Default levelhighAdvancedUp to 10Deeper search, higher unit pricefastFastUp to 10Faster responsemedium is used when no level is specified.
Citations in the response
Search results are returned in annotations, using the industry-standard url_citation format:
- Each reference source has one citation entry.
- When the model marks a corresponding source in its answer (for example,
[source-1]),start_indexandend_indexidentify the marker's position in the answer text; both are0when no marker is present.
Locations by endpoint:
- Chat Completions: non-streaming responses use
choices[].message.annotations; streaming responses send an additional block containing onlydelta.annotationsbefore[DONE]. - Responses: in
annotationswithinoutput_text; streaming responses send them with completion events such asresponse.output_text.done. - Messages: attached to the text content block in
annotations; streaming responses send them in thetext_deltaevent before the end.
Billing
- Cost per request = model cost + web search cost.
- Web search is charged per search, at the platform's listed price; advanced depth (
high, or advanced depth selected withengine/mode) has a higher unit price. - Free models also incur web search charges; the model itself remains free.
- Each request performs one search by default. With
search_queries, the search count equals the number of queries and charges are multiplied accordingly. Backend route retries do not repeat the search or charge. - The following cases do not incur a web search charge:
- The search itself fails (returns 503; see below).
- Individual failures when using
search_queries(only successful queries are charged). - The search succeeds but the model ultimately fails to produce a response.
- The search cost is included when the balance is reserved. The actual deduction is reflected in response
usage.costand the billing record.
Errors and limits
web_search_unavailableThe web search service is paused. Try again later.No charge; the model is not calledinsufficient_creditsThe balance cannot cover this request, including search cost; the request is blocked before search.No chargeweb_search_rate_limitedWeb search requests are too frequent. Try again later.No chargeinvalid_web_search_optionsInvalid search parameters, such as an unsupported country, a non-string-array include_domains, or specifying search_queries and search_prompt together.No charge- Rate limits, model permissions, content review, and other checks for a normal request complete before search; requests blocked by these checks do not search.
- Search parameters and tools (
:onlinesuffix, the web item inplugins,web_search_options, the search tools above, andweb_search_preview) are handled only by this platform and are not forwarded to the model.
Migrate from OpenRouter
BazaarLink's API is OpenRouter-compatible — most integrations switch by changing two values: the base URL to https://api.bazaarlink.ai/v1 and the API key to a BazaarLink key starting with sk-bl-.
- Base URL: https://openrouter.ai/api/v1 → https://api.bazaarlink.ai/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 /v1/models
- models[] fallbacks, 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. Request Handling
When an upstream source has a problem, a request can try another available source. If none is available, the request returns an error.
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
Responses can include x-request-id; x-generation-id on non-streaming responses; x-ratelimit-*; retry-after when applicable; and x-auto-resolved-model for auto or auto:free routing.
x-request-id: 85e6f35c71a31f19-SJC
x-generation-id: 85e6f35c71a31f19-SJC
x-ratelimit-limit: 200
x-ratelimit-remaining: 198
x-ratelimit-reset: 1740000060
retry-after: 5
x-auto-resolved-model: openai/gpt-4oCredit 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.
IP Allowlist
Each API key can have its own IP allowlist, so only requests from the specified source IPs can use that key.
When to use it
This is best for production services running on servers with a fixed egress IP. If your application runs from a home network, mobile network, or another changing address, confirm that its egress IP is stable first; otherwise you may lock yourself out.
How to set it up
Go to /keys and open the key’s “…” menu. Choose “IP Allowlist”, enter the entries, and save.
You can also use PATCH /api/v1/keys/:id with a signed-in session or a Management API key:
Entry format
Enter one value per line in the UI. In the API, put the same values in the `allowedCidrs` array. Accepted values are a bare IPv4 address, an IPv4 CIDR, or an IPv6 literal (exact match only).
203.0.113.7
198.51.100.0/24
2001:db8::1Behavior
Leave it empty (send an empty [] array) to allow all source IPs. This is the default, so existing keys keep working as before.
The source IP is read from the `cf-connecting-ip` header.
If the list is non-empty but the source IP cannot be resolved, the request is rejected (fail-closed).
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.
Account Security
Beyond your password, BazaarLink also supports two-step verification for an extra layer of protection at sign-in.
Enable two-step verification (TOTP)
- Go to the Settings page and find the "Two-step verification (TOTP)" section, then click "Enable two-step verification."
- Scan the QR code with an authenticator app (such as Google Authenticator), or enter the secret shown below manually.
- Enter the 6-digit code your authenticator generates and confirm to finish enabling it.
Once enabled, you'll see 8 backup codes shown only once — each is 10 characters and works only once. Save or copy them somewhere safe other than your phone; use a backup code to sign in if you lose your phone or can't reach your authenticator.
Other ways to protect your account
Combine these with two-step verification to further limit the damage from a leaked or misused key:
- IP allowlist — restrict an API key to specific source IPs.
- Personal circuit breaker — set per-minute and per-hour spend caps that pause automatically once exceeded.
- Per-key spend limit — set a daily, weekly, or monthly spend cap when creating or updating an API key.
Image Generation
Generate images via /v1/chat/completions with modalities:["image"], or the OpenAI DALL·E-compatible /v1/images/generations.
curl -N https://api.bazaarlink.ai/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://api.bazaarlink.ai/v1/videos \
-H "Authorization: Bearer $BAZAARLINK_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model":"bytedance/seedance-2.0","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)MPEGMOVWebMOAuth PKCE
OAuth 2.0 PKCE (Proof Key for Code Exchange) lets public client apps (browser SPAs, mobile apps) make API calls on behalf of individual users without storing a long-lived server secret client-side.
When to Use OAuth PKCE
- Building a browser SPA or mobile app that needs per-user billing
- Multi-tenant SaaS — each end-user pays for their own API usage
- You cannot store a shared secret client-side (public client)
- You need a separate, revocable key per user (it does not expire by itself) instead of a single shared API key
What your users will see
Describe these steps on your own sign-in screen:
- In your app they choose "Sign in with BazaarLink" (or whatever you call the button).
- Their browser opens the BazaarLink authorization page. If they are not signed in yet, they sign in first and come back to this page automatically.
- The page shows your app's address and what happens if they allow: a new API key is created for the app, usage is billed to their BazaarLink balance, and they can revoke the key at any time. They may name the key and set a monthly spend limit (both optional).
- They click Allow and are sent back to your app. If they click Deny, no key is created, your app is not notified and they see a "denied" page.
- To revoke access later they open the API Keys page (https://bazaarlink.ai/keys) and disable or delete the key named after your app.
How it works
Step 1 — Initiate Authentication — Generate a random code_verifier (a long random string) and its SHA-256 hash as the code_challenge, then redirect the user to the BazaarLink auth page.
https://bazaarlink.ai/auth?callback_url=https://yourapp.com/callback&code_challenge=CODE_CHALLENGE&code_challenge_method=S256Authorization URL parameters
- callback_url (required for apps that can receive a redirect; omit it for headless tools, see the last item) — where the user returns after allowing. Rules below.
- code_challenge (required) — the base64url-encoded SHA-256 of your code_verifier (43 characters, no padding).
- code_challenge_method (required) — must be S256.
- state (optional) — your own opaque CSRF value, 1 to 256 printable ASCII characters. BazaarLink hands it back unchanged as &state=… right after the code. A malformed state makes the authorization page show "invalid request".
- key_label (optional) — up to 100 characters. Prefills the key name on the consent page (the user can still change it). Longer or blank labels are ignored.
- Headless tools (CLI / desktop, no callback_url) — omit callback_url. After the user allows, the page shows a one-time code (valid 5 minutes, single use) for them to enter into your tool; exchange it exactly like a redirected code. The page cannot show which app is asking, so tell users to enter the code only in a tool they started themselves.
Not supported
- workspace_id and required_workspace_id — not supported. The key always belongs to the user's own account.
- code_challenge_method=plain — not supported (S256 only).
callback_url rules
- It must be https://. http:// is accepted only for localhost, 127.0.0.1 and [::1] (any port); the consent page then shows a local-development warning.
- No fragment (#), no username or password in the URL, at most 2048 characters.
- The consent page shows your callback host name so the user can see which app is asking.
- After the user allows, the browser is sent to your callback_url with code=… appended (and state=… when you sent one; an existing query string is kept). The code is valid for 10 minutes and works once.
Step 2 — Exchange Code for API Key — After the user authorizes the request, BazaarLink redirects to your callback_url with a one-time code. The code is valid for 10 minutes and can be used once; a wrong code_verifier burns it. POST code, code_verifier and code_challenge_method (all three required) to receive the user's API key.
Step 3 — Use the User-Scoped Key — The returned key is a normal BazaarLink API key that belongs to the user who authorized — usage is billed to that user's balance, not yours.
curl https://bazaarlink.ai/api/v1/chat/completions \
-H "Authorization: Bearer sk-bl-..." \
-H "Content-Type: application/json" \
-d '{"model": "google/gemini-3.5-flash", "messages": [{"role": "user", "content": "Hello"}]}'JavaScript example (browser)
The /auth page and POST /api/v1/auth/keys are served from https://bazaarlink.ai (not api.bazaarlink.ai). The key you receive works on both https://bazaarlink.ai/api/v1 and https://api.bazaarlink.ai/v1.
If the user sets a spend limit when authorizing, it is a monthly limit and resets with the account's monthly period.
Errors
Errors use the OpenAI-style shape {"error":{"message","type","code"}}. POST /api/v1/auth/keys can return:
- 400 invalid_code_challenge_method — code_challenge_method is missing, plain, or anything other than S256.
- 400 — the JSON body is invalid, or code / code_verifier is missing.
- 403 invalid_code — the code is unknown, expired or already used, the code_verifier is wrong, or the account is no longer allowed. All of these return the same message on purpose, so a caller cannot tell them apart. A wrong code_verifier also burns the code: ask the user to authorize again.
- 429 rate_limited — more than 30 exchange attempts in 10 minutes from one IP (see the Retry-After header).
- 429 key_total_cap — the account already holds the maximum of 200 API keys.
- 405 — only POST is accepted.
Create the code from your server (Management key)
Instead of sending the user through the browser page, the owner of a personal Management key can create the authorization code directly with POST /api/v1/auth/keys/code (OpenRouter-compatible). The code is bound to the Management key's owner, so the key you exchange it for is a standard key on that owner's account, with the limit, period, expiry and label chosen here. Use it when your own tool already holds a Management key and wants to hand an app a limited key. The exchange (POST /api/v1/auth/keys) is the same as above.
- callback_url (required) and code_challenge + code_challenge_method=S256 (required) — same rules as the authorization URL.
- key_label (optional) — up to 100 characters; the key's name. Default: "App: <your callback host>".
- limit (optional) — spend limit in USD, 0 or more; 0 or missing means no limit.
- usage_limit_type (optional) — daily, weekly or monthly: how often the limit resets. Only used together with limit; the default is monthly.
- expires_at (optional) — when the key expires: ISO 8601 UTC time with seconds in the future, for example 2026-12-31T23:59:59Z. Minute-precision values are rejected. Missing means the key stays valid until it is revoked.
- workspace_id — not supported (400). spawn_agent and spawn_cloud are accepted and ignored.
Response: { "data": { "id", "app_id", "created_at" } }. id is the one-time code to exchange (valid 10 minutes, once). BazaarLink has no app registry, so app_id is a stable integer derived from your callback address: the same address always gets the same app_id.
- 401 unauthorized — no Bearer key, or the key is unknown.
- 403 management_key_required — the key is not a personal Management key (standard keys and organization Management keys are refused).
- 403 account_not_allowed — the key's owner is suspended or deleted.
- 400 — invalid_callback_url, invalid_code_challenge_method, invalid_code_challenge, invalid_expires_at, workspace_not_supported, or a malformed body.
- 429 rate_limited — more than 30 codes per hour for one owner or 120 per hour from one IP (see Retry-After).
The key you receive
- A standard BazaarLink API key (sk-bl-…), the same kind as a key created on the API Keys page. It does not expire by itself.
- Usage is billed to the user's BazaarLink balance. If the user set a spend limit on the consent page, it is a monthly limit.
- It works on both base URLs: https://bazaarlink.ai/api/v1 and https://api.bazaarlink.ai/v1.
- It counts toward the account's limit of 200 keys and shows up in the user's API Keys list, where they can disable or delete it at any time.
- Suspended or deleted accounts cannot authorize apps, and a code issued before a suspension is refused at exchange (403 invalid_code).
What you get
- Authorization Code + PKCE flow for public client applications
- Per-user billing: each user pays for their own usage from their own balance
- A separate key per user, listed on the user's API Keys page where they can revoke it
Management 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://api.bazaarlink.ai/v1/keys/:id
Authorization: Bearer sk-bl-YOUR_MGMT_KEY
# Returns 204 No Content on successQuery Balance
Query Usage
GET https://api.bazaarlink.ai/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
Error envelopes vary by endpoint. Use HTTP status and error.type when present; error.code may be a string or numeric HTTP status, and can be absent. Use error.message for display only.
{
"error": {
"message": "Insufficient credits. Please top up to continue.",
"type": "invalid_request_error",
"code": "insufficient_credits"
}
}HTTP status and error.code
Error envelopes vary by endpoint. Use HTTP status and error.type when present; error.code may be a string or numeric HTTP status, and can be absent. Use error.message for display only.
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.
Content Moderation
All requests are automatically reviewed before being sent to the model. Requests that violate our usage policy are rejected before dispatch and return a 403 error.
Categories that get blocked
Moderation is based on our Acceptable Use Policy and primarily blocks the following categories of content:
- Sexual exploitation content
- Violent threats
- Operational guidance for illegal acts
- Biohazard-related content
403 response format
A request blocked by moderation returns an error in this shape:
{
"error": {
"message": "Your prompt was blocked by content moderation.",
"type": "invalid_request_error",
"code": "content_filter"
}
}Blocked requests are not billed — you are not charged for a rejected request.
Appealing a false positive
If you believe a request was blocked incorrectly, contact support with the request time and (if convenient) the request id, and we'll review it.
Privacy
Requests are automatically reviewed for content safety; the text and images of your latest input may be sent to a third-party content-safety scoring service.
For requests judged violating, suspected, near the threshold or randomly sampled, we may keep the full text or the leading portion for compliance evidence, dispute handling and auditing, with no fixed deletion period promised.
Other ordinary successful requests do not have their prompts or responses retained; requests made with ZDR keys are not subject to the first two points.
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.
Error envelopes vary by endpoint. Use HTTP status and error.type when present; error.code may be a string or numeric HTTP status, and can be absent. Use error.message for display only.
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 a stream fails after it starts, BazaarLink may emit a final SSE event with a top-level error object followed by data: [DONE]. Treat the response as incomplete; do not use partial output as a completed result.
data: {"error":{"message":"The request could not be completed.","type":"upstream_error","code":502}}
data: [DONE]
// Branch on error.code; error.type can vary by failure path.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
The client-supplied transforms field is ignored and removed before an upstream request. It cannot enable or disable message transformations. BazaarLink may apply context handling under its own server-side rules; shorten the input or select a model with a larger context window when needed.
transforms values such as middle-out; they do not change how this request is routed or processed.Zero Data Retention
For ordinary successful requests, BazaarLink does not store your message content (exceptions: see the Privacy Policy); ZDR key mode provides stricter zero data retention. This page describes how your data is handled. Suitable for applications processing sensitive data.
Current Data Handling
- Message content: not stored for ordinary successful requests (exceptions: see the Privacy Policy); ZDR key mode is stricter
- Billing and operations usage records: token counts, time, model, cost, latency and status
- Usage logs: usage statistics only for ordinary successful requests, 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/gpt-5.5, gpt-5.6-sol, gpt-5.4 | 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) |
Reading the thinking text
A reasoning model can return its thinking text with the answer. For the models listed below, thinking is on by default: a request that sends no reasoning parameter runs with thinking enabled, returns the thinking text, and is billed for the thinking tokens actually used. Send reasoning: { exclude: true } or include_reasoning: false to turn it off. Every other model returns thinking text only when you ask: send reasoning_effort, a reasoning object, include_reasoning: true, thinking or enable_thinking.
Models that think by default: gemini-4-argon, deepseek-v4.1-flash, deepseek-v4.1-flash-1017
How to ask for it
Send any one of these in the request: reasoning_effort (low, medium or high), a reasoning object (effort, max_tokens), include_reasoning: true, thinking: { type: "enabled" } or enable_thinking: true. Send reasoning.exclude: true or include_reasoning: false to turn the thinking text off (the model still reasons).
| Field | Where | Meaning |
|---|---|---|
| reasoning | choices[].message.reasoning | The thinking text (a string) |
| reasoning_details | choices[].message.reasoning_details | The thinking text as a list of items, each with only type and text |
| reasoning | choices[].delta.reasoning | Streaming: the thinking text arrives in pieces (delta.reasoning and delta.reasoning_details), ahead of the answer text in delta.content |
Check which efforts a model supports with GET /v1/models: each reasoning model lists reasoning.supported_efforts, reasoning.default_effort and reasoning.mandatory (for example gemini-4-argon supports high, medium and low).
Example: gemini-4-argon
On /v1/responses, ask with reasoning: { effort, summary: "auto" }; the thinking text is returned in the reasoning item of the output array. On /v1/messages, send thinking: { type: "enabled", budget_tokens } to receive thinking blocks.
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
- Choose smaller models (flash/mini/haiku) for latency-sensitive scenarios
- Enable prompt caching to reduce latency for repeated requests
Uptime Optimization
After an upstream error, a request can try another available source; if none can handle it, the request returns an error. Circuit breakers limit repeated attempts, and provider status is monitored.
Availability Mechanisms
- Circuit breaker: auto-detects and isolates failing providers
- When an upstream source has a problem, a request can try another available source. If none is available, the request returns an error.
- 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://api.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 /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 the upstream doesn't reject it 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 provider-specific fields when the upstream does not support them — parallel_tool_calls, logprobs, top_logprobs, logit_bias, service_tier, user are removed before forwarding (the upstream 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 the provider of your selected model, 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-v4.1-flashRouting 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://api.bazaarlink.ai/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
Independent Model IDs
Catalog variants such as :free, :extended, and :thinking can identify entries with distinct capabilities or catalog pricing. BazaarLink's catalog and server-side routing rules determine the upstream and customer price.
:free
:extended
:thinking:nitro
:floor
:exacto:onlineFree 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 /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.
qwen/qwen3.7-flash
reflection/beam-free
stealth/glyph-cluster
nvidia/nemotron-3.5-lightning-free
inclusionai/ling-3.1-flash-1019
deepseek/deepseek-v4-flash-0731freeFree 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. Free quota is measured in units, not raw request counts: the units consumed by a single request grow with the prompt's context length. Under the same daily quota, long-context requests therefore yield noticeably fewer usable requests than short prompts.
After the free quota runs out
After the free quota is used, requests can continue at the listed paid model rate if the account has balance. To receive 429 instead, send `X-Free-Fallback: false` or disable the paid-tier transition in key settings. Without balance, over-quota requests return 429.
When a request is actually degraded this way, the response carries an x-bl-free-fallback: daily or x-bl-free-fallback: rpm response header naming which cap fired, so you can detect the switch at request time instead of only from your bill. The header is absent from any request that was not degraded.
# 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
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. `/v1/orgs` handles organization and member management; cost reports use the separate `/api/orgs/:orgId/reports/*` paths below.
Organizations
/v1/orgsList all organizations the caller belongs to, with role and joinedAt.
/v1/orgs/:orgIdGet org detail including team and member counts.
curl https://api.bazaarlink.ai/v1/orgs \
-H "Authorization: Bearer sk-bl-YOUR_MANAGEMENT_KEY"Teams
/v1/orgs/:orgId/teamsList teams with member counts, ordered by name.
/v1/orgs/:orgId/teams/v1/orgs/:orgId/teams/:teamIdPartial update — include only the fields to change.
/v1/orgs/:orgId/teams/:teamId# Create a team
curl https://api.bazaarlink.ai/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
/v1/orgs/:orgId/membersList all members with nested user (id/name/email) and team info.
/v1/orgs/:orgId/members404 if the email address has no BazaarLink account. 409 if already a member. Default role: member.
/v1/orgs/:orgId/members/:memberIdPartial update of role, teamId, or monthlyBudget.
/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. Disabled by default — opt in to turn it on.
- 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
Rotating API keys is a security practice. Create a replacement key, update and verify your integration, then revoke the old key. We do not guarantee zero downtime during this process.
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 | Fixed BazaarLink value: bazaarlink |
| 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 | USD amount charged to the customer for this request |
| 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 | BazaarLink response status code |
| app_name | string | null | Application name (X-Title header) |
| key_name | string | API key name used for the request |
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
Social data (
:social)Append
:socialto a model name when your question is about a social platform. Before the model answers, BazaarLink fetches matching posts, profiles or comments from that platform and provides them as reference material. If the platform data cannot be fetched in time, the request falls back to the normal web search, so you still get an answer.Example
curl https://bazaarlink.ai/api/v1/chat/completions \ -H "Authorization: Bearer $BAZAARLINK_API_KEY" \ -H "Content-Type: application/json" \ --max-time 60 \ -d '{ "model": "gpt-5.4-mini:social", "messages": [{ "role": "user", "content": "Recent posts on X about GPT-6" }] }'What to know
:socialrequest can take 20 to 40 seconds before the answer starts. After 40 seconds it falls back to the web search. Set your client timeout to at least 60 seconds.:onlinestays fast because it is the normal web search.:online(5 per day without a balance, 10 with a balance or a subscription, reset at UTC midnight). Beyond the allowance, each query is billed from the Data API price table (seeGET /v1/data/catalog), on top of the model's own token charge. A query that falls back to the web search is billed like:online.:socialreads only some social platforms, and the list depends on what is enabled; the current list is on/solutions/data-api#social-suffix. A platform that is in the Data API catalog but not on that list is not read by:social, so a query about it, or about no platform, is answered by the web search.POST /api/v1/chat/completions,/api/v1/messagesand/api/v1/responses, streaming and non-streaming, with the same citations as:online.:onlineand:socialcannot be combined in one model name (400invalid_web_search_options).