BazaarLink provides one OpenAI-compatible request and response schema across models and providers, so you can integrate once and switch models without rewriting your application.
OpenAPI Specification
The complete BazaarLink API is documented with the OpenAPI specification. You can access it in YAML or JSON format:
Force the model to return valid JSON matching a schema. This is essential for building reliable applications that parse model outputs programmatically.
json_object — basic JSON mode; the model returns valid JSON.
json_schema — strict schema mode; the model output must match the supplied JSON Schema.
Plugins
BazaarLink forwards the plugins array to the selected upstream route. Plugin availability depends on the chosen model and provider; the web plugin is also enabled by the :online model variant.
Add a partial assistant message as the last item to request continuation on compatible model routes.
How it works
BazaarLink preserves and forwards the final assistant message. Continuation behavior is implemented by the selected upstream model and provider, so it is not guaranteed on every route.
TypeScript
1
2
3
4
5
6
7
8
const response = await client.chat.completions.create({
model: "anthropic/claude-sonnet-4.6",
messages: [
{ role: "user", content: "What is the meaning of life?" },
// Intentional partial response; compatible routes continue from here.
{ role: "assistant", content: "My best answer is" }
]
});
Responses
BazaarLink normalizes completion responses across models and providers into one OpenAI-compatible shape.
CompletionsResponse Format
The choices field is always an array. Streaming responses use delta; non-streaming responses use message. Usage and cost details are returned when available.
finish_reason uses normalized values such as stop, length, tool_calls, content_filter, and error. native_finish_reason preserves the provider’s original value.
Penalize tokens based on presence. Range: [-2, 2]. Default: 0
repetition_penalty
number
Reduce token repetition from input. Range: (0, 2]. Default: 1
min_p
number
Minimum probability relative to the top token. Range: [0, 1]. Default: 0
top_a
number
Dynamic top-P based on highest-probability token. Range: [0, 1]. Default: 0
seed
integer
Integer seed for deterministic sampling. Not guaranteed for all models
n
integer
Number of completions to generate. Default: 1
user
string
End-user identifier for monitoring and abuse detection. Has no effect on billing. OpenAI-family models only — see note below.
stop
string | string[]
Stop sequences — generation halts when encountered
logit_bias
object
Map token IDs to bias values [-100, 100] added before sampling. OpenAI-family models only — see note below.
logprobs
boolean
Return log probabilities of each output token. OpenAI-family models only — see note below.
top_logprobs
integer
Number of most-likely tokens to return per position (requires logprobs: true). Range: 0–20. OpenAI-family models only — see note below.
tools
Tool[]
List of tools (functions) the model may call
tool_choice
string | object
Controls tool use: "auto", "none", or specific tool
parallel_tool_calls
boolean
Enable parallel function calling when tools are provided. Default: true. OpenAI-family models only — see note below.
response_format
object
Force structured JSON output. See Structured Output section
structured_outputs
boolean
Request strict JSON-schema-conformant output on providers that support it. Passed through as-is
reasoning
object
Provider-specific reasoning/thinking configuration. Passed through as-is
reasoning_effort
string
OpenAI o-series style reasoning effort: "low", "medium", or "high". Passed through as-is
transforms
string[]
Message transforms to apply, e.g. ["middle-out"]. Omit to auto-apply on ≤8k-context models
models
string[]
Fallback model list — BazaarLink tries each in order if primary fails
route
string
Advanced routing compatibility field — most users don't need this. Use "models" for fallback.
provider
object
Advanced routing preferences — most users don't need this.
user, logprobs, top_logprobs, logit_bias, and parallel_tool_calls only reach OpenAI-family models
These five parameters are stripped from the upstream request whenever the resolved model isn't recognized as OpenAI's own — sending them to anthropic/claude-*, google/gemini-*, or any other non-OpenAI target returns 200 with the parameter silently ignored, not an error. If you're setting one of these and the effect isn't showing up, check whether the target model is OpenAI's.
BazaarLink normalizes exactly two fields — model and the removal of a provider field — and forwards the rest of the upstream response as-is. Fields like native_finish_reason, system_fingerprint, and reasoning are only present when the specific upstream provider populates them; don't rely on their presence across every model. usage.cost is the exception — it's always BazaarLink's own settled/billed amount, not a value relayed from upstream.
BazaarLink offers two image-generation paths: (A) /v1/chat/completions with modalities: ["image"] — the native path, supports SSE streaming and mixed text+image output; recommended for new integrations. (B) /v1/images/generations — OpenAI DALL·E-compatible request shape, but responses are SSE event streams (required for slow models to dodge the 100 s upstream timeout). Both paths emit the same SSE event protocol — endpoint choice is purely a request-shape preference. Image EDITING (modifying an existing image) uses POST /v1/images/edits — OpenAI images.edit compatible, multipart/form-data with your source image(s). Edit-capable models have modality text+image->image (e.g. qwen/qwen-image-edit); pure-generation models are text->image — check each model's modality in GET /v1/models.
Response format
/api/v1/images/generations returns OpenAI-compatible sync JSON by default (since 2026-07-25) — client.images.generate() works with no wrapper. Pass stream: true to opt into the SSE event stream instead, which delivers progress for models with long generation times.
A. /v1/chat/completions (native, recommended)
POST/api/v1/chat/completions
The canonical streaming path. Recommended for any new integration.
1
2
3
4
5
6
7
8
9
curl -N https://bazaarlink.ai/api/v1/chat/completions \
-H "Authorization: Bearer $BL_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
}'
Image-to-image: include an image_url part in the content array. Supports data URIs or https image URLs (http:// is rejected), up to 8 images, ~10MB per data URI. The message carrying images must also include a text part (the edit instruction). Some models additionally support image_config (e.g. {"strength": 0.7}, 0–1 — lower stays closer to the source image), passed through to the upstream as-is.
1
2
3
4
5
6
7
8
9
10
11
12
curl -N https://bazaarlink.ai/api/v1/chat/completions \
-H "Authorization: Bearer $BL_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "openai/gpt-5.4-image-2",
"messages": [{"role":"user","content":[
{"type":"image_url","image_url":{"url":"data:image/png;base64,..."}},
{"type":"text","text":"change the background to a night city"}
]}],
"modalities": ["image"],
"stream": true
}'
Image Edits (OpenAI-compatible)
POST/api/v1/images/edits
The OpenAI SDK's client.images.edit() works directly (multipart upload, sync JSON response returning data: [{ url }]). Same limits as image-to-image: up to 8 images, 10MB each; mask and response_format=b64_json are not supported yet.
1
2
3
4
5
curl https://bazaarlink.ai/api/v1/images/edits \
-H "Authorization: Bearer $BL_API_KEY" \
-F model="openai/gpt-5.4-image-2" \
-F image=@cat.png \
-F prompt="change the background to a night city"
B. /v1/images/generations (DALL·E-compatible)
POST/api/v1/images/generations
OpenAI DALL-E request shape. Sync JSON ({ created, data: [{ url }] }) is the default and works with client.images.generate() out of the box; pass stream: true to get the SSE event stream documented below instead.
1
2
3
4
5
6
7
8
curl https://bazaarlink.ai/api/v1/images/generations \
-H "Authorization: Bearer $BL_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "openai/gpt-image-2",
"prompt": "a red cat on a sofa",
"size": "1024x1024"
}'
modelrequired
string
Model id, e.g. google/gemini-2.5-flash-image
promptrequired
string
Text prompt
size
string
Output size (auto-mapped)
n
integer
Number of images (default 1)
1
2
3
4
5
6
# Streaming variant — progressive delivery for long generations
curl -N https://bazaarlink.ai/api/v1/images/generations \
-H "Authorization: Bearer $BL_API_KEY" \
-H "Accept: text/event-stream" \
-H "Content-Type: application/json" \
-d '{"model":"openai/gpt-5.4-image-2","prompt":"a red cat on a sofa","stream":true}'
Asynchronous three-step flow (submit → poll → content). Video generation takes 30 s–5 min, which doesn't fit the synchronous request/response shape of chat-completions — so BazaarLink exposes it as a dedicated /api/v1/videos endpoint using a job-id pattern: submit returns a vjob_* ID → poll status → fetch bytes on completion. Calling a video model via /chat/completions or /images/generations returns 400 (code: wrong_endpoint_for_video). Billing settles against real usage.cost when the job reaches completed.
Video tasks
One endpoint covers several tasks. Which task runs is decided by which fields you send — a single model can do image-to-video, keyframes, and continuation. Not every model supports every task; an unsupported request returns 400.
Text-to-video
prompt
Generate from a text prompt alone — no input media.
Image-to-video
frame_images: [ first frame ]
Your image IS the picture: it becomes the first frame and gets animated, staying faithful to the original. E.g. a photo of a cat → that same cat turns its head in the same scene.
Keyframes (first + last)
frame_images: [ first, last ]
Give a start and an end image; the model interpolates the motion in between.
Continuation
input_video
Extend an existing clip. The requested duration must be greater than the source video's length.
Reference-to-video
input_references: [ images ]
Your images are REFERENCES, not frames: the model keeps the subject/style and generates a brand-new scene. E.g. a cat photo + "dancing in a forest" → a new video where the cat's look is preserved but the scene and motion are new (1–9 references). Difference from image-to-video: i2v stays faithful to the exact picture; r2v re-casts the subject into new footage.
Video editing
input_video + prompt
Edit an existing video — change the scene, style, or motion. Billed on input-video seconds + output seconds.
Reference images for reference-to-video — subject/style guidance, not exact frames.
input_video
string|object
Source video URL for continuation and video editing. Must be publicly fetchable by the upstream.
aspect_ratio
string
Aspect ratio, e.g. 16:9 or 9:16. Ignored when an input image dictates the ratio.
watermark
boolean
Add a watermark. Default false.
callback_url
string
Webhook URL called once the job reaches a terminal state — HTTPS only, SSRF-checked. No signature in v1; treat it as a hint and confirm via GET /videos/{id}.
Allowed resolutions differ per model — an unsupported value returns 400 listing the supported ones.
Input images/videos must be reachable from a public URL. Hotlink-protected hosts (e.g. some wikis) fail.
Input media passes upstream content moderation and can occasionally be rejected.
For continuation, the requested duration must exceed the source video length.
Output aspect ratio follows the input image — a square image yields a square video.
Video editing is billed on the input video's seconds plus the generated output's seconds.
input_video (editing / continuation) must be a PUBLIC URL. Videos you generate here are served behind your API key, so the upstream can't fetch them — host your source video on a publicly-reachable URL.
Webhook payloads are unsigned — verify status/amount via GET /videos/{id} before acting, and note unsigned_urls there are absolute (unlike the relative paths in the poll response).
Supported video models
Model ID
Modality
Tasks
bytedance/seedance-2.0
text+image+audio+video->video
t2v, i2v
bytedance/seedance-2.0-fast
text+image+audio+video->video
t2v, i2v
google/veo-3.1
text+image->video
t2v, i2v
openai/sora-2-pro
text+image->video
t2v, i2v
bytedance/seedance-1-5-pro
text+image->video
t2v, i2v
alibaba/wan2.6-r2v-flash
text+image+video->video
r2v
alibaba/wan2.5-i2v-preview
text+image->video
i2v
alibaba/wan2.7-i2v
text+image+video->video
i2v, kf2v, continuation
alibaba/wan2.6-t2v
text->video
t2v
alibaba/wan2.5-t2v-preview
text->video
t2v
alibaba/wan2.2-t2v-plus
text->video
t2v
alibaba/wan2.7-r2v
text+image+video->video
r2v
alibaba/wan2.7-t2v
text->video
t2v
alibaba/happyhorse-1.0
text+image->video
t2v, i2v
alibaba/happyhorse-1.1
text->video
t2v
alibaba/wan2.2-i2v-plus
text+image->video
i2v
alibaba/wan2.1-t2v-plus
text->video
t2v
alibaba/wan2.2-i2v-flash
text+image->video
i2v
alibaba/wan2.6-r2v
text+image+video->video
r2v
alibaba/wan2.7-videoedit
text+image+video->video
videoedit
alibaba/wan2.1-t2v-turbo
text->video
t2v
alibaba/wan2.6-i2v-flash
text+image->video
i2v
alibaba/wan2.6-i2v
text+image->video
i2v
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)MPEGMOVWebM
1
2
3
4
5
6
7
8
9
10
11
12
13
response = client.chat.completions.create(
model="google/gemini-2.5-flash",
messages=[{
"role": "user",
"content": [
{
"type": "video_url",
"video_url": {"url": "https://example.com/video.mp4"},
},
{"type": "text", "text": "What is happening in this video?"},
],
}],
)
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,...`)
An OpenAI Responses API-compatible endpoint for stateless multi-turn conversations, tool calling, and multimodal inputs. Ideal for agents and frameworks that use the OpenAI Python SDK ≥ 1.x with client.responses.create().
POST/api/v1/responses
Note
Accepts the same authentication and model routing as Chat Completions.
Request Body
modelrequired
string
Model ID, e.g. "openai/gpt-4o" or "anthropic/claude-3.5-sonnet"
inputrequired
string | Item[]
User input — a plain string (single message) or an array of input items for multi-turn / multimodal conversations.
instructions
string
System-level instructions, equivalent to a system message. Must be re-sent on every request.
stream
boolean
If true, returns Responses API SSE stream events. Event types: response.created, response.output_text.delta, response.completed.
max_output_tokens
integer
Maximum number of output tokens to generate (includes reasoning tokens for o-series models).
temperature
number
Sampling temperature 0–2. Higher = more random. Default: 1
top_p
number
Nucleus sampling probability mass. Default: 1
tools
Tool[]
Tool (function) definitions — same JSON Schema format as Chat Completions (accepts both the flat Responses shape and the nested shape). OpenAI's own built-in hosted tools (web_search_preview, file_search, computer_use_preview) are not supported; web search is available via plugins: [{id:"web"}] — supported only on some model routes.
tool_choice
string | object
Controls tool use: "auto", "none", or specific tool
parallel_tool_calls
boolean
Enable parallel function calling when tools are provided. Default: true. OpenAI-family models only — see note below.
response_format
object
Force structured JSON output. See Structured Output section
models
string[]
Fallback model list — BazaarLink tries each in order if primary fails
transforms
string[]
Message transforms to apply, e.g. ["middle-out"]. Omit to auto-apply on ≤8k-context models
previous_response_id
string
This endpoint is stateless — passing a non-null value returns 400 (invalid_prompt) immediately, it is never accepted and ignored. Use stateless mode instead: pass the full conversation history in the input array.
provider
object
Advanced routing preferences — most users don't need this.
curl https://bazaarlink.ai/api/v1/responses \
-H "Authorization: Bearer $BAZAARLINK_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "openai/gpt-4o-mini",
"instructions": "You are a helpful assistant.",
"input": "What is the capital of Taiwan?"
}'
Replace messages with input (string or array), use instructions instead of a system-role message, and read output[0].content[0].text instead of choices[0].message.content.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# Chat Completions (before)
response = client.chat.completions.create(
model="openai/gpt-4o-mini",
messages=[
{"role": "system", "content": "You are helpful."},
{"role": "user", "content": "Hello"},
]
)
text = response.choices[0].message.content
# Responses API (after)
response = client.responses.create(
model="openai/gpt-4o-mini",
instructions="You are helpful.",
input="Hello"
)
text = response.output[0].content[0].text
Limitations
previous_response_id or store: true is rejected with 400 (error code invalid_prompt) — not accepted and ignored. Always use stateless mode: pass the full conversation history in the input array.
OpenAI's own built-in hosted tools (web_search_preview, file_search, computer_use_preview) are not supported. Web search is available via plugins: [{id:"web"}] — supported only on some model routes.
background: true is accepted but ignored — every request runs synchronously to completion.
Messages (Anthropic)
Anthropic-compatible Messages API for Claude SDK. Use exactly as you would with Anthropic's API — just change base URL and auth header.
POST/api/v1/messages
Note
Accepts Bearer token or x-api-key header (Anthropic SDK compatibility). Max body size: 10 MB.
Request Body
modelrequired
string
Model ID, e.g. "openai/gpt-4o" or "anthropic/claude-3.5-sonnet"
max_tokensrequired
integer
Maximum number of tokens to generate (positive integer).
messagesrequired
Message[]
Array of conversation messages (non-empty).
system
string
Optional system prompt.
stream
boolean
If true, returns a Server-Sent Events stream. Default: false
temperature
number
Sampling temperature 0–2. Higher = more random. Default: 1
{"id":"msg_...","type":"message","role":"assistant","model":"anthropic/claude-opus-4","content":[{"type":"text","text":"Hello! How can I help you today?"}],"stop_reason":"end_turn","usage":{"input_tokens":10,"output_tokens":12,"cache_read_input_tokens":0,"cache_creation_input_tokens":0,"bz_cost":0.00042}}
// /v1/models — Response SchematypeModelsResponse = {
data: Model[];
};
typeModel = {
id: string; // Model ID (e.g. "openai/gpt-4.1")name: string; // Human-readable namecontext_length: number | null; // Max context window in tokensmodality: string | null; // e.g. "text->text", "text+image->text"architecture?: {
input_modalities?: string[];
output_modalities?: Array<
"text" | "image" | "embeddings" | "audio" |
"video" | "rerank" | "speech" | "transcription"
>;
};
pricing: {
prompt: string; // Input price per 1M tokens (USD)completion: string; // Output price per 1M tokens (USD)
};
description?: string | null; // Model descriptiontop_provider?: {
max_completion_tokens?: number;
};
supported_parameters?: string[]; // e.g. ["tools", "response_format", "reasoning"]pricing_tiers?: { // Present only for models with input-length tiersabove_prompt_tokens: number; // Ascending; strict "greater than" thresholdprompt: string; // USD per token, override tiercompletion: string; // USD per token, override tier
}[];
};
Input-Length Tiered Pricing
Some models switch to a different price table once the prompt exceeds a token threshold — the whole table changes, not just the tokens above the threshold. Thresholds are strict: a prompt with exactly N tokens still bills at the tier below N; the higher tier applies only when input tokens are greater than N.
pricing_tiers is a sibling of pricing, present only when a model has override tiers beyond its base price. Entries are sorted by above_prompt_tokens ascending; prompt/completion are USD per token (same unit as pricing.prompt/pricing.completion). pricing.prompt and pricing.completion always remain the base (lowest) tier.
Most models have no tiers — for those, the pricing_tiers key is simply absent from the response.
When streaming, usage data is returned in the final chunk before the [DONE] message, alongside a choices array with an empty delta and finish_reason: "stop".
Keep-alive & final chunk
Streams may contain SSE comment lines (starting with a colon) or heartbeat events as keep-alives — skip non-data: lines instead of JSON.parsing the raw stream. The final data chunk carries usage (token counts and cost) before data: [DONE]. Successful responses include an X-Request-Id header — include it when reporting issues.
1
2
3
4
5
6
7
: keepalive <- SSE comment line — ignore, do NOT JSON.parse
data: {"id":"chatcmpl-abc","choices":[{"delta":{"content":"Hi"},"index":0}]}
data: {"id":"chatcmpl-abc","choices":[{"delta":{},"finish_reason":"stop","index":0}],"usage":{...}}
data: [DONE]
Stream Cancellation
Streaming requests can be cancelled by closing the client connection — e.g. calling AbortController.abort() or closing the stream object. BazaarLink stops relaying further chunks and cancels the outbound provider request as soon as the cancellation is received.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
importOpenAIfrom"openai";
const client = newOpenAI({
baseURL: "https://bazaarlink.ai/api/v1",
apiKey: "sk-bl-YOUR_API_KEY",
});
const controller = newAbortController();
const stream = await client.chat.completions.create(
{
model: "anthropic/claude-sonnet-4.6",
messages: [{ role: "user", content: "Write a long story." }],
stream: true,
},
{ signal: controller.signal }
);
forawait (const chunk of stream) {
const content = chunk.choices[0]?.delta?.content;
if (content) process.stdout.write(content);
}
// e.g. on a "Stop" button click:
controller.abort();
No charge for an interrupted stream
If a stream ends before the final usage chunk arrives — including a client-side cancellation — there is no token-count ground truth for that request, so BazaarLink refunds the full reservation. You are not billed for content already delivered to your client before the cancellation.
Not guaranteed to stop the provider instantly
Closing the connection stops BazaarLink from relaying and paying for further tokens right away, but whether the upstream provider halts generation on its own servers the instant the connection drops depends on that provider — some may keep computing briefly after disconnect.
Mid-Stream Errors
No choices field on error frames
If a failure happens after streaming has already started (e.g. the upstream connection drops), you get an SSE data frame shaped {error:{message,type,code}} instead of the usual {choices:[...]} — with no HTTP status change, since headers were already sent. Check for an error key before reading choices[0].delta, and bill only for tokens already streamed (partial billing applies).
1
2
3
4
5
data: {"id":"chatcmpl-abc","choices":[{"delta":{"content":"The capital of "},"index":0}]}
data: {"error":{"message":"Upstream connection lost.","type":"upstream_error","code":502}}
data: [DONE]
Embeddings
Embeddings are numerical representations of text that capture semantic meaning — they convert text into vectors (arrays of numbers) usable for a range of machine learning tasks. BazaarLink provides a unified, OpenAI Embeddings API-compatible endpoint so you can call embedding models from multiple providers through one interface.
What are Embeddings?
Embeddings transform text into high-dimensional vectors where semantically similar texts sit closer together in vector space — for example, "cat" and "kitten" would have similar embeddings, while "cat" and "airplane" would be far apart. These vector representations let machines understand relationships between pieces of text, making them foundational to many AI applications.
Common Use Cases
Use Case
Description
RAG (Retrieval-Augmented Generation)Build systems that retrieve relevant context from a knowledge base before generating an answer — embeddings find the most relevant documents to include in the LLM's context.
Semantic SearchConvert documents and queries into embeddings, then find the most relevant documents by vector similarity — this understands meaning rather than just matching keywords, giving better results than keyword search alone.
Recommendation SystemsGenerate embeddings for items (products, articles, videos) and user preferences to recommend similar items — vector comparison surfaces semantically related items even without shared keywords.
Clustering & ClassificationGroup similar documents or classify text by analyzing embedding patterns — documents with similar embeddings usually belong to the same topic or category.
Duplicate DetectionFind duplicate or near-duplicate content by comparing embedding similarity — this works even when the content has been paraphrased or reworded.
Anomaly DetectionSpot unusual or outlier content by identifying embeddings that deviate significantly from the dataset's typical pattern.
POST/api/v1/embeddings
Note
Not all upstream providers support embeddings. If your configured provider does not support the requested model, BazaarLink will automatically failover to the next available provider.
Parameters
modelrequired
string
The embedding model to use, e.g. "openai/text-embedding-3-small".
inputrequired
string | string[] | ContentItem[]
Text to embed — a single string, an array of strings for one batch call, or (for models that support it) an array of {content:[...]} items mixing text and image_url parts.
dimensions
integer
Requested output vector size. Only honored by models that support variable dimensions (e.g. the OpenAI text-embedding-3 family); forwarded to the upstream provider as-is and ignored by models that don't support it.
encoding_format
string
Requested embedding encoding, e.g. "float" or "base64". Forwarded to the upstream provider as-is — support depends on the model.
provider
object
Provider routing preferences — order, allow_fallbacks, data_collection, and the other fields described in Provider Selection.
Basic Request
1
2
3
4
5
6
7
8
9
10
11
12
13
from openai import OpenAI
client = OpenAI(
base_url="https://bazaarlink.ai/api/v1",
api_key="sk-bl-YOUR_API_KEY",
)
response = client.embeddings.create(
model="openai/text-embedding-3-small",
input="The quick brown fox jumps over the lazy dog",
)
print(response.data[0].embedding) # 1536-dimensional vector
Batch Processing
Send an array of strings to embed multiple texts in a single request — cheaper and faster than one call per text.
1
2
3
4
5
6
7
8
9
10
11
response = client.embeddings.create(
model="openai/text-embedding-3-small",
input=[
"Machine learning is a subset of artificial intelligence",
"Deep learning uses neural networks with multiple layers",
"Natural language processing enables computers to understand text",
],
)
for i, item inenumerate(response.data):
print(f"Embedding {i}: {len(item.embedding)} dimensions")
Multimodal Input (Image + Text)
Models with image input support (output_modalities includes "embeddings" and inputModalities includes "image") accept input items shaped as {content:[{type:"text",...}, {type:"image_url",...}]}, letting you embed an image alone or jointly with text.
Model-dependent
Only some embedding models accept image input — check a model's supported modalities on the Models page before sending image_url content. Text-only models will reject this shape.
Control which upstream serves an embedding request the same way as chat completions — see Provider Selection for the full field reference.
1
2
3
4
5
6
7
8
9
{"model":"openai/text-embedding-3-small","input":"Your text here","provider":{"order":["openai"],"allow_fallbacks":true,"data_collection":"deny"}}
Finding Embedding Models
There's no dedicated embeddings model-listing endpoint — call GET /api/v1/models and filter client-side for entries whose output_modalities includes "embeddings", or browse the Models page.
Limitations
No streaming — embeddings are always returned as a complete response, unlike chat completions.
Each model has a maximum input length; text beyond that limit is truncated or rejected upstream.
Embeddings for identical input are deterministic — no temperature or randomness involved.
Best Practices
Choose a model for your speed/quality/cost tradeoff — smaller models (e.g. qwen/qwen3-embedding-4b) are cheaper and faster; larger ones (e.g. openai/text-embedding-3-large) generally embed with higher fidelity.
Batch multiple texts into one request instead of one call per text — fewer round trips, lower overhead.
Cache results — embeddings for the same input never change, so store them rather than regenerating.
Compare with cosine similarity, not Euclidean distance — it's scale-invariant and works better for high-dimensional vectors.
Watch each model's context length — long documents may need chunking before embedding.
Parameters
Sampling parameters shape the token generation process. BazaarLink passes supported parameters to the upstream provider; unsupported parameters are silently ignored.
Sampling Parameters
Pure passthrough — no local defaults
These are forwarded to the upstream provider exactly as sent — BazaarLink never injects or enforces a default. "Default" below describes the provider's own behavior when a field is omitted, not a guarantee from BazaarLink.
temperature
number
Sampling temperature 0–2. Higher = more random. Default: 1
is_free_tier = true when credit balance < $10. Windows are in UTC: daily = current day, weekly = Mon–Sun, monthly = 1st–EOM. When the key has a per-key spend limit set (via the limit param on key creation/update), limit / limit_remaining / limit_reset reflect that limit and the matching period's usage; otherwise limit is null and limit_remaining falls back to your account credit balance. expires_at is the key's expiration time (null if none). is_management_key and is_provisioning_key are aliases for the same concept — both true for a management key.
BYOK
BazaarLink does not offer a bring-your-own-key (BYOK) program, so this endpoint does not return any byok_usage fields.
Errors: 401 (auth), 404 (user missing — rare).
Agent Registration
Self-service registration for AI agents (bots, autonomous systems). Returns an API key with trial credits and a claim token for account upgrade.
POST/api/v1/agents/register
Rate Limit
No authentication required, but IP-limited to 1 registration per 24 hours.
Request Body
namerequired
string
Agent name (non-empty, trimmed, max 100 chars).
description
string
Optional agent description.
referral_code
string
Optional referral code.
Example Request
1
2
3
4
5
6
curl -X POST https://bazaarlink.ai/api/v1/agents/register \
-H "content-type: application/json" \
-d '{
"name": "My Agent",
"description": "Autonomous research bot"
}'
Response
1
2
3
4
5
6
7
8
9
10
11
12
13
14
{"api_key":"sk-bl-xxxxx...","credits":0.10,"credits_usd":"$0.1000","claim_token":"abc...xyz","claim_expires":"2026-04-27T10:00:00.000Z","upgrade_url":"https://bazaarlink.ai/claim?token=...","referral_code":"aBcDeFgH","free_model":"auto:free","message":"Welcome to BazaarLink!...","referral_message":"Share referral link:...","base_url":"https://bazaarlink.ai/api/v1","docs":"https://bazaarlink.ai/llms.txt"}
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.
1
2
3
4
5
6
7
{"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.
Code
Name
Description
400Bad RequestMalformed request, empty messages array, or missing required fields
401UnauthorizedAPI key is missing, invalid, or disabled
402Payment RequiredInsufficient account credits, per-key spend limit reached, or monthly/weekly budget cap exceeded
403ForbiddenAccount is suspended or does not have permission
404Not FoundThe requested model, generation, key, or other resource does not exist
409ConflictThe resource is not in the required state, such as a video job that is not complete
410GoneThe requested model has been retired and must be replaced
413Payload Too LargeRequest body exceeds 10 MB; reduce content size or split the request
416Range Not SatisfiableThe requested byte range is invalid for generated video content
429Too Many RequestsRate limit exceeded; check Retry-After header before retrying
500Server ErrorInternal BazaarLink error
502Bad GatewayAll upstream providers failed; failover was attempted
503Service UnavailableNo upstream provider is configured for this model; contact admin
504Gateway TimeoutThe upstream connection or stream stalled and timed out
Machine-readable billing codes
A 402 response can represent different controls. These stable codes let clients show the correct next action.
Code
Description
budget_cap_reachedA weekly or monthly advisory budget cap was reached; raise or reset the cap.
credit_limit_exceededA monthly-billing organization's hard credit line was exhausted; contact billing.
insufficient_creditsA prepaid user or organization cannot reserve enough balance; add credits.
spend_limit_exceededThe API key reached its configured daily, weekly, or monthly spend limit.
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.
Model and endpoint
Model lookup, lifecycle, pricing, modality, and endpoint compatibility errors.
Code
HTTP status
unknown_model400
invalid_model_id400
model_not_found404
model_retired410
model_endpoint_mismatch400
embedding_on_chat_endpoint400
model_not_priced400
invalid_modality_for_model400
Request and safety
Invalid parameters, context, tools, schemas, and content-safety refusals.
Code
HTTP status
missing_required_field400
unsupported_param400
max_tokens_invalid400
context_too_long400
tool_use_unsupported400
malformed_tool_messages400
invalid_response_format_schema400
invalid_tools_definition400
content_moderation403
content_filter403
unknown_4xx400
Image generation and editing
Image input, multipart editing, output, and image-pipeline errors.
Code
HTTP status
invalid_image_url400
input_images_not_supported400
invalid_content_type400
mask_not_supported400
unsupported_response_format400
missing_prompt400
missing_image400
too_many_images400
invalid_image_type400
image_too_large400
invalid_n400
pipeline_error502
no_images502
Upstream routing
Sanitized provider connectivity, authentication, throttling, and availability errors.
Code
HTTP status
upstream_unreachable502
upstream_auth_failed502
upstream_rate_limited429
upstream_unavailable502/503
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.
Control
HTTP status
How to identify it
Request rate limit429Numeric code 429; use Retry-After and X-RateLimit-* headers.
Rate-limit penalty block429Numeric code 429 and a temporary restriction message; use Retry-After.
Global spend emergency brake503Numeric code 503, global spend-limit message, and Retry-After of 30 or 300 seconds.
Org/team/member/user spend brake429Numeric code 429 and a spend circuit-breaker message naming the affected scope.
Billing and budget controls402Use the stable billing string codes listed above.
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.
Retry with backoff
429, 502, 503, and 504. Respect Retry-After when provided. For generation requests, do not create a second job after an ambiguous network failure until you have checked the original job.
Fix before retrying
400, 401, 402, 403, 404, 409, 410, 413, and 416. Change the request, credentials, balance, permissions, resource state, or Range header first.
Handling Errors
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import random
import time
from openai import OpenAI, APIStatusError
client = OpenAI(
base_url="https://bazaarlink.ai/api/v1",
api_key="sk-bl-YOUR_API_KEY",
max_retries=0, # Avoid double retries; this example handles them.
)
RETRYABLE = {429, 502, 503, 504}
for attempt inrange(5):
try:
response = client.chat.completions.create(
model="openai/gpt-4.1",
messages=[{"role": "user", "content": "Hello!"}],
)
breakexcept APIStatusError as error:
if error.status_code notin RETRYABLE or attempt == 4:
raise
retry_after = error.response.headers.get("Retry-After")
delay = (
float(retry_after)
if retry_after
elsemin(8, 0.5 * (2 ** attempt)) + random.uniform(0, 0.25)
)
time.sleep(delay)
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.
1
2
3
4
5
6
7
8
9
10
// If the stream fails mid-flight, BazaarLink emits a final SSE event
// with a top-level "error" object, followed by data: [DONE]
data: {"error":{"message":"Upstream stream interrupted. The response is incomplete.","type":"upstream_error","code":502}}
data: [DONE]
// Chunks relayed verbatim from some upstreams may instead carry the error
// inline on the choice: choices[0].finish_reason === "error" with an
// "error" object ({ code, message }) on the choice — handle both shapes.
// Branch on error.code; error.type can vary by failure path.
Versioning
BazaarLink exposes a single stable API path, /api/v1 — there are no date-pinned versions or version headers to manage. The API evolves continuously rather than through numbered releases.
Non-Breaking Changes
These ship without prior notice:
New endpoints
New models added to the catalog
New optional request parameters
New response fields
New schemas with optional properties
Additional response status/error codes
Write clients defensively
Ignore response fields you don't recognize, and don't fail on unknown values in enum-like fields — new ones are added as the catalog and feature set grow.
Breaking Changes
These are rare, and cover:
Removing or renaming an endpoint, parameter, or response field
Changing a field's type
Making an optional parameter required
When they do happen, a breaking change applies to a specific endpoint rather than the whole /api/v1 surface — there's no single version bump that could break every integration at once. We don't yet publish a formal changelog with a Breaking-change tag (see Staying Current below); for anything integration-critical, reach out via Support before you rely on undocumented behavior.
Deprecation Policy
The one routine "breaking" event you should expect: individual models get retired as upstream providers deprecate them. Query a model's current status via GET /api/v1/models.
1
2
3
4
5
6
7
GET https://bazaarlink.ai/api/v1/models
Authorization: Bearer sk-bl-YOUR_API_KEY
# A model within 30 days of its deprecation date shows in the catalog# with an "EOL" badge on the Models page. After the effective date it's# dropped from the catalog and calls return:# 410 { "error": { "type": "model_not_available", "code": "model_retired" } }
Staying Current
We don't yet publish a dedicated API changelog or RSS feed. For now, check this page directly, watch a model's status via GET /api/v1/models, or contact Support if you need advance notice for a critical integration.
Support
Support
Hi! How can we help you? Send a message and we'll get back to you soon.