BazaarLinkBazaarLink
Sign in
DocsAPI ReferenceSDK ReferenceAgentic UsageAI Skills

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.

Pricing

BazaarLink charges zero markup on model usage (same as upstream provider list price), plus a 10% transaction fee and 5% business tax. Accounted in USD with TWD quotes and Taiwan e-invoices. Pay-as-you-go credits for self-serve users; enterprises can arrange monthly billing (Net-30, negotiable).

How it works

  • Top-up: New Taiwan Dollars are converted to USD at the Bank of Taiwan's live TWD→USD selling rate at the moment of the transaction, and credited to your BazaarLink balance in USD.
  • Usage: Every API call is billed by actual token usage; the per-token price matches the upstream provider's official USD pricing (zero markup), plus a 10% transaction fee.
  • Invoice: Taiwan e-invoices are supported for local accounting workflows. Companies that need procurement or monthly billing can arrange enterprise billing terms.
Example
For US$1.00 of usage at the official list price: model cost stays US$1.00 (zero markup), plus a 10% transaction fee = US$1.10, then 5% business tax = US$1.155; the TWD amount is converted at the Bank of Taiwan rate at billing time and a uniform invoice is issued.

About the exchange rate

Foreign-exchange conversion uses the Bank of Taiwan published 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.

Quick Start

AI Agent Skill File
Load our skill file into your AI assistant (Claude, Cursor, Copilot…) to give it full knowledge of the BazaarLink API: bazaarlink.ai/skill.md

Get started in under 5 minutes. BazaarLink is fully compatible with the OpenAI SDK — just change the

Base URL

bash
https://bazaarlink.ai/api/v1

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.

python
from openai import OpenAI

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

completion = client.chat.completions.create(
    model="openai/gpt-4.1",
    messages=[
        {"role": "user", "content": "What is the meaning of life?"}
    ],
)

print(completion.choices[0].message.content)
Need an API key?
Get your API key from the API Keys page. All keys start with sk-bl-.
Model ID Format — provider/model-name required
Model IDs must include the provider prefix (e.g. openai/gpt-4.1). Using a bare model name without the prefix will return a 400 error.
✓ openai/gpt-4.1   anthropic/claude-sonnet-4-6   google/gemini-2.5-flash
✗ gpt-4.1   claude-sonnet-4-6   gemini-2.5-flash

Authentication

All API requests require an Authorization header with your API key.

bash
Authorization: Bearer sk-bl-YOUR_API_KEY

Get your API key from the dashboard. Keep your key secure — do not expose it in client-side code.

Security Note
Never expose API keys in client-side JavaScript. Always proxy requests through your backend server.

Optional Headers

HTTP-Referer
string
Your site URL, for rankings and analytics (optional)
X-Title
string
Your app name, shown in dashboards (optional)

Principles

BazaarLink is designed around three core principles:

1. Unified Interface

One API, one SDK, hundreds of models. Switch between OpenAI, Anthropic, Google Gemini, Meta Llama, and others without changing your code — just change the model ID.

2. Price Optimization

BazaarLink automatically routes to the most cost-effective provider for your chosen model. You pay only for what you use, billed in USD with full invoicing support.

3. High Availability

Automatic failover means if a provider goes down, your requests are seamlessly rerouted. No code changes, no downtime.

Multimodal

BazaarLink supports multimodal inputs — send images, audio, and files alongside text to models that support them. Content is passed through to the upstream provider.

Supported Modalities

Input
Description
Example Models
TextStandard text messagesAll models
ImagesURL or base64 data URI — PNG, JPEG, WebP, GIFopenai/gpt-4.1, anthropic/claude-sonnet-4.6, google/gemini-2.5-flash
Files / PDFsDocument via base64 data URI (`data:application/pdf;base64,...`)google/gemini-2.5-flash, anthropic/claude-sonnet-4.6
AudioRaw base64 — no URL support. Requires `format` fieldopenai/gpt-4o-audio-preview, google/gemini-2.0-flash
VideoURL (CDN) or base64 data URIgoogle/gemini-2.5-flash, qwen/qwen3.5-flash-02-23

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:

python
response = client.chat.completions.create(
    model="openai/gpt-4.1",
    messages=[
        {
            "role": "user",
            "content": [
                {"type": "text", "text": "What's in this image?"},
                {
                    "type": "image_url",
                    "image_url": {
                        "url": "https://example.com/photo.jpg",
                        "detail": "auto"  # "low", "high", or "auto"
                    }
                }
            ]
        }
    ],
)
print(response.choices[0].message.content)
Sending Images
Always include a text part alongside images. Text-first ordering (text part before image parts) is recommended for best compatibility across all providers.
Sending Images
Check the Models page for each model's supported input modalities. The modality column shows which inputs each model accepts.

Image Generation

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.

Supported Models/images/generations

Use the `/images/generations` endpoint (OpenAI-compatible format). Returns image URLs in `data[].url`.

ModelModality
bytedance-seed/seedream-4.5image+text->image
black-forest-labs/flux.2-maxtext+image->image

Supported Models/chat/completions + modalities

Use the `/chat/completions` endpoint with `modalities: ["image"]` or `["image", "text"]`. Generated images are returned in `choices[0].message.images[]`, not in `content`.

ModelModality
google/gemini-2.5-flash-imagetext+image->text+image
google/gemini-3.1-flash-image-previewtext+image->text+image
bytedance-seed/seedream-4.5image+text->image
openai/gpt-5.4-image-2text+image+file->text+image
google/gemini-3-pro-image-previewtext+image->text+image
black-forest-labs/flux.2-maxtext+image->image

Using the modalities Parameter

To request image output, pass `modalities: ["image"]` or `["image", "text"]`. Generated images are returned in `choices[0].message.images[]` as base64-encoded data URIs.

python
response = client.chat.completions.create(
    model="google/gemini-2.5-flash-image",
    messages=[{"role": "user", "content": "A serene mountain lake at sunrise"}],
    modalities=["image", "text"],  # ["image"] only for Flux / DALL-E
    image_config={
        "aspect_ratio": "16:9",
        "image_size": "2K",
    },
)

# Generated images are in message.images[], NOT in message.content
for img in response.choices[0].message.images:
    data_uri = img["image_url"]["url"]  # "data:image/png;base64,..."

# Text caption (when modalities includes "text")
print(response.choices[0].message.content)

image_config Options

Fine-tune generation with the optional image_config parameter:

Field
Type
Values
aspect_ratiostring"1:1" (default), "2:3", "3:2", "3:4", "4:3", "4:5", "5:4", "9:16", "16:9", "21:9"
image_sizestring"1K" (default), "2K", "4K" — resolution tier
font_inputsobject[]Max 2. Each: { font_url, text }. Sourceful/Riverflow models only.
super_resolution_referencesstring[]Max 4 reference image URLs. Image-to-image mode (Sourceful/Riverflow) only.

Video Generation

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.

1. Submit job (returns vjob_xxx immediately)

POST/api/v1/videos
modelrequired
string
Model id, e.g. alibaba/wan-2.7
promptrequired
string
Text prompt
duration
integer
Duration in seconds (model-dependent)
resolution
string
Resolution — 480p / 720p / 1080p etc.
generate_audio
boolean
Generate audio track (true/false)
bash
curl https://bazaarlink.ai/api/v1/videos \
  -H "Authorization: Bearer $BL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "alibaba/wan-2.7",
    "prompt": "a bird flying over mountains",
    "duration": 3,
    "resolution": "720p",
    "generate_audio": false
  }'
# → 202 { "id": "vjob_xxx", "status": "pending" }
Note
Submit reserves worst-case × duration × multiplier; on completion the real upstream `usage.cost` settles the delta.

2. Poll status

GET/api/v1/videos/{id}
bash
curl -H "Authorization: Bearer $BL_API_KEY" \
  https://bazaarlink.ai/api/v1/videos/vjob_xxx
Note
Each GET must be ≥ 8 s apart to actually hit the upstream (avoids rate limits).
Note
When status=failed, the full reserve amount is refunded to the user.

3. Fetch video content (MP4)

GET/api/v1/videos/{id}/content
bash
curl -H "Authorization: Bearer $BL_API_KEY" \
  -o output.mp4 \
  https://bazaarlink.ai/api/v1/videos/vjob_xxx/content

Supported video models

Model IDModality
bytedance/seedance-2.0text+image->video
bytedance/seedance-2.0-fasttext+image->video

PDF Inputs

Send PDF documents directly in messages for models to analyze, summarize, or answer questions about. BazaarLink passes file content through to upstream providers that support it.

Supported Formats

  • PDF documents (text, images, tables, scanned)
  • Base64-encoded data URL (`data:application/pdf;base64,...`)
  • Multi-page documents
  • Password-free PDFs only
python
import base64

with open("document.pdf", "rb") as f:
    pdf_data = base64.b64encode(f.read()).decode()

response = client.chat.completions.create(
    model="anthropic/claude-sonnet-4.6",
    messages=[{
        "role": "user",
        "content": [
            {
                "type": "file",
                "file": {
                    "filename": "document.pdf",
                    "file_data": f"data:application/pdf;base64,{pdf_data}",
                },
            },
            {"type": "text", "text": "Summarize this document."},
        ],
    }],
)

Processing Engines

Engine
Pricing
Description
nativeModel costForwards PDF bytes directly. Requires model-native PDF support (Claude, Gemini).
pdf-textFreeExtracts embedded text. Best for text-only PDFs with embedded fonts. Fast, zero extra cost.
mistral-ocrPaid / pageOCR extraction — works on scanned PDFs and image-heavy documents. Higher accuracy.

Selecting a Processing Engine

Pass a `plugins` array to select the PDF parsing engine. The parsed content is automatically injected into the model context — works with any model, not just PDF-native ones.

python
import base64

with open("document.pdf", "rb") as f:
    pdf_data = base64.b64encode(f.read()).decode()

response = client.chat.completions.create(
    model="openai/gpt-4o",        # any model — parser injects text into context
    messages=[{
        "role": "user",
        "content": [
            {
                "type": "file",
                "file": {
                    "filename": "report.pdf",
                    "file_data": f"data:application/pdf;base64,{pdf_data}",
                },
            },
            {"type": "text", "text": "Summarize this document."},
        ],
    }],
    plugins=[{
        "id": "file-parser",
        "pdf": {"engine": "mistral-ocr"},  # or "pdf-text" (free), "native"
    }],
)

Audio Inputs

BazaarLink supports two audio paths: inline audio in chat messages for multimodal models, and a dedicated transcription endpoint for speech-to-text.

Path 1 — Chat completions (multimodal input)

Base64 only
Audio data must be **raw base64** — do NOT include a data URI prefix (`data:audio/...;base64,`). That prefix is only used for `image_url`. Pass the bare base64 string directly in the `data` field.

Supported Formats

WAVMP3AIFFAACOGG (Opus / Vorbis)FLACM4APCM16 (raw)PCM24 (raw)
python
import base64

with open("audio.wav", "rb") as f:
    audio_b64 = base64.b64encode(f.read()).decode()
    # audio_b64 is a raw base64 string — do NOT add data: prefix

response = client.chat.completions.create(
    model="openai/gpt-4o-audio-preview",
    messages=[{
        "role": "user",
        "content": [
            {"type": "text", "text": "Transcribe this audio:"},
            {
                "type": "input_audio",
                "input_audio": {
                    "data": audio_b64,   # raw base64, no "data:audio/...;base64," prefix
                    "format": "wav",     # wav, mp3, flac, ogg, m4a, aac
                },
            },
        ],
    }],
)
print(response.choices[0].message.content)
Compatibility
Not all models support all audio formats — check the model page for supported modalities. Billing is based on token usage proportional to audio duration.

Path 2 — Transcription endpoint

POST /v1/audio/transcriptions is a dedicated speech-to-text endpoint compatible with the OpenAI Whisper API. Drop-in replacement: point your existing OpenAI SDK client at BazaarLink and it just works.

Model
Billing
Description
openai/whisper-1Duration (seconds)Classic Whisper — reliable, low cost, broad format support
openai/gpt-4o-transcribeTokensGPT-4o powered — higher accuracy for noisy audio and accents
python
from openai import OpenAI

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

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

print(transcript.text)
Billing
whisper-1 bills by audio duration (seconds). gpt-4o-transcribe bills by tokens. Both return the exact upstream cost in the usage.cost field.

Text-to-Speech (TTS)

Synthesize natural speech from text via POST /v1/audio/speech. Returns binary audio (mp3/opus/aac/flac/wav/pcm depending on model and response_format).

Binary response, not JSON
The response body is raw audio bytes — save with --output in curl, or call .arrayBuffer() in fetch. Usage is recorded server-side; query /v1/usage for spend.

TTS Models

ModelBillingNotes
openai/gpt-4o-mini-tts-2025-12-15$0.60 / 1M charsCost-efficient, 11 voices, accepts natural-language instructions for tone/pace/style.
openai/tts-1$15 / 1M charsOriginal OpenAI TTS — faster, lower fidelity.
openai/tts-1-hd$30 / 1M charsHigher-fidelity OpenAI TTS.
mistralai/voxtral-mini-tts-2603$16 / 1M charsMistral's voice model — different voice character.

Voices (gpt-4o-mini-tts)

alloyashballadcoralechofablenovaonyxsageshimmerverse

Voice availability varies by model — check the model card. For natural Mandarin female voice, pick coral, nova, sage, or shimmer.

Response Formats

mp3 (default)opusaacflacwavpcm

Each model accepts a subset of formats. mp3 and pcm are the safest defaults.

python
from openai import OpenAI

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

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

Steering with instructions

openai/gpt-4o-mini-tts accepts an optional instructions string in natural language to direct voice tone, pace, and style.

Less is more
For natural-sounding voice, leave instructions empty and pick a suitable voice. Heavy direction (e.g. "cute, melodic, singing-style") can cause the model to over-emote.

Limits

Input text is limited to 4096 characters per request. For longer text, split into multiple calls.

Video Inputs

Send video content for models to analyze visuals, generate descriptions, or answer questions about scenes and events. Supported via Gemini models today.

Supported Formats

MP4 (H.264)MPEGMOVWebM

Provider Variations

  • Google AI (gemini-*): Supports YouTube URLs and Google Cloud Storage (gs://) URIs. File size limit: ~1 GB / up to 1 hour (Gemini 1.5 Pro); ~50 MB / 5 min (Gemini 1.5 Flash, 2.0 Flash).
  • Vertex AI (gemini-* via vertex): Supports base64-encoded video data and GCS URIs. Suited for private or enterprise storage.
  • Other providers: MP4, MPEG, MOV, WebM via URL or base64 (model-dependent).
python
# Video analysis via Gemini (Google AI — direct video URL)
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?"},
        ],
    }],
)

# Via base64 — local file upload
import base64
with open("video.mp4", "rb") as f:
    vid_b64 = base64.b64encode(f.read()).decode()

response = client.chat.completions.create(
    model="google/gemini-2.5-flash",
    messages=[{
        "role": "user",
        "content": [
            {
                "type": "video_url",
                "video_url": {"url": f"data:video/mp4;base64,{vid_b64}"},
            },
            {"type": "text", "text": "Describe the key scenes."},
        ],
    }],
)

Best Practices

  • Trim to only the necessary segments — shorter videos reduce cost and latency.
  • Use 720p resolution or lower — higher resolution rarely improves model understanding.
  • Compress with H.264 codec for widest compatibility.
  • For long videos, provide a text description of the relevant time range.

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.

Note
Management keys cannot call AI models (chat/completions/messages/embeddings). Use a standard API key for model access.

Creating a Management Key

On the API Keys page, create a key and select type "Management".

List Keys

bash
GET https://bazaarlink.ai/api/v1/keys
Authorization: Bearer sk-bl-YOUR_MGMT_KEY

# Response
{
  "keys": [
    {
      "id": "clxyz123...",
      "name": "Production Key",
      "keyType": "standard",
      "keyPrefix": "sk-bl-abc1",
      "keySuffix": "XyZ9",
      "enabled": true,
      "spendLimitUsd": 10.00,
      "spendLimitPeriod": "month",
      "expiresAt": null,
      "createdAt": "2026-01-01T00:00:00.000Z",
      "lastUsed": "2026-03-01T12:34:56.000Z",
      "requestCount": 1234,
      "totalTokens": 5678901
    }
  ]
}

Create Sub-Key

bash
POST https://bazaarlink.ai/api/v1/keys
Authorization: Bearer sk-bl-YOUR_MGMT_KEY
Content-Type: application/json

{
  "name": "Agent Key",
  "limit": 10.00,
  "limit_reset": "monthly",
  "expires_at": "2026-12-31T23:59:59Z"
}

# limit_reset: daily | weekly | monthly
# expires_at:  ISO 8601 datetime (optional)

# Response — save the key value, it won't be shown again
{
  "id": "clxyz789...",
  "name": "Agent Key",
  "key": "sk-bl-xyz789abcdef...",
  "keyType": "standard",
  "spendLimitUsd": 10.00,
  "spendLimitPeriod": "month",
  "expiresAt": "2026-12-31T23:59:59.000Z",
  "enabled": true,
  "createdAt": "2026-03-01T00:00:00.000Z"
}

Update Key

bash
PATCH https://bazaarlink.ai/api/v1/keys/:id
Authorization: Bearer sk-bl-YOUR_MGMT_KEY
Content-Type: application/json

{"enabled": false}              # disable key
{"spendLimitUsd": 5, "spendLimitPeriod": "week"}  # set spend limit
{"spendLimitUsd": null}         # remove spend limit
# Response: {"updated": true}

Revoke Key

bash
DELETE https://bazaarlink.ai/api/v1/keys/:id
Authorization: Bearer sk-bl-YOUR_MGMT_KEY

# Returns 204 No Content on success

Query Balance

bash
GET https://bazaarlink.ai/api/v1/credits
Authorization: Bearer sk-bl-YOUR_MGMT_KEY

# Response
{
  "data": {
    "total_credits": 12.345,
    "total_usage": 3.210
  }
}

Query Usage

bash
GET https://bazaarlink.ai/api/v1/usage?period=month
Authorization: Bearer sk-bl-YOUR_MGMT_KEY

# period: day | week | month | year

App Attribution

Identify your application in request headers to enable usage tracking, dashboard visibility, and fine-grained analytics.

Note
These headers are entirely optional and do not affect API functionality. However, setting them is recommended for debugging and usage attribution.

Available Headers

HeaderDescription
HTTP-RefererYour site URL, for rankings and analytics (optional)
X-TitleYour app name, shown in dashboards (optional)
python
from openai import OpenAI

client = OpenAI(
    base_url="https://bazaarlink.ai/api/v1",
    api_key="sk-bl-YOUR_KEY",
    default_headers={
        "HTTP-Referer": "https://yourapp.com",  # Optional: your site URL
        "X-Title": "My Application",             # Optional: your app name
    },
)

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

Report Feedback

Help us improve BazaarLink by reporting issues, bugs, or suggestions. We actively monitor all feedback channels.

How to Report

Channel
Best For
Response Time
Contact PageGeneral feedback, feature requests1-2 business days
EmailBug reports, technical issuesWithin 24 hours
API Response HeadersAuto-reported errors and metricsAutomatic

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

Visit our Contact page to submit feedback.

Error Codes

BazaarLink uses standard HTTP status codes. Error responses follow the OpenAI format:

json
{
  "error": {
    "message": "Invalid or disabled API key.",
    "type": "invalid_request_error",
    "code": 401
  }
}
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
413Payload Too LargeRequest body exceeds 10 MB; reduce content size or split the request
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

Handling Errors

python
from openai import OpenAI, APIError, RateLimitError

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

try:
    response = client.chat.completions.create(
        model="openai/gpt-4.1",
        messages=[{"role": "user", "content": "Hello!"}],
    )
except RateLimitError:
    print("Rate limited — waiting before retry...")
except APIError as e:
    print(f"API error {e.status_code}: {e.message}")

Streaming Error Formats

Errors that occur before any tokens are streamed return a standard HTTP error response with a JSON body.

Errors that occur mid-stream are sent as SSE events with finish_reason: "error". Parse the error field in the delta.

typescript
// Error chunk sent mid-stream (finish_reason: "error")
type MidStreamError = {
  choices: [
    {
      index: 0;
      finish_reason: "error";
      delta: { content: "" };
      native_finish_reason: null;
      error: {
        code: number;
        message: string;
        metadata?: {
          provider_name?: string;
          raw?: unknown;
        };
      };
    }
  ];
};

Debugging

Use debug.echo_upstream_body: true to inspect the exact request body sent to the upstream provider. The transformed request is returned as the first SSE chunk. For development / debugging only — do not use in production.

json
// Request with debug enabled (streaming only)
{
  "model": "openai/gpt-4.1",
  "messages": [{ "role": "user", "content": "Hello" }],
  "stream": true,
  "debug": { "echo_upstream_body": true }
}

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.

Tier
RPM
Daily Usage
Notes
Free (< $10 credits)20 RPMUnlimitedDevelopment & testing
Paid (≥ $10 credits)200 RPMUnlimitedProduction workloads

When a rate limit is exceeded you receive a 429 response with a Retry-After header. Implement exponential backoff when retrying requests.

Response Headers

Every successful response includes rate limit headers for client-side tracking:

text
X-RateLimit-Limit: 200        # Max requests per minute for your tier
X-RateLimit-Remaining: 198    # Remaining requests in current window
X-RateLimit-Reset: 1740000060 # Unix timestamp when the window resets
X-Provider: anthropic          # Which upstream provider served this request
X-Request-Id: chatcmpl-abc123 # Unique request ID for debugging
402 Insufficient Credits
When your balance reaches $0, the API returns HTTP 402 with message "Insufficient credits. Please top up to continue." — monitor usage.cost in responses to track spending in real time.

FAQ

How is BazaarLink different from calling OpenAI directly?
BazaarLink provides USD billing with NTD-quoted pricing, unified invoices, Chinese support, and a single API across all major models. You can access OpenAI, Anthropic, Google, and more with the same code.
Do I need to change my existing code?
Just change the base URL and API key. All other settings (except model IDs) remain unchanged.
Does BazaarLink store my messages?
By default, we do not store message content. We only log token counts and timestamps for billing purposes.
How do I get a unified invoice (統一發票)?
Enterprise monthly billing customers receive a Taiwan unified e-invoice (統一發票, including business tax ID) at each billing cycle. Pay-as-you-go top-ups via Stripe currently generate a Stripe receipt only — contact sales to switch to monthly billing if you need a Taiwan e-invoice.
What payment methods are supported?
All major credit cards are accepted (Visa, Mastercard, American Express).
Which OpenAI SDK features are supported?
Chat completions, streaming, tool calling, structured output (response_format), and assistant prefill all work. Features are passed through to the upstream provider.
Can I use BazaarLink with agent frameworks like LangChain or CrewAI?
Yes! Any framework that supports the OpenAI API works with BazaarLink. Just set the base URL and use your BazaarLink API key. See the Agentic Usage section for examples.

API Key Rotation

Regularly rotating API keys is a security best practice. BazaarLink supports zero-downtime key rotation — create a new key first, then migrate, then revoke the old one.

Note
API keys can be revoked at any time from the dashboard or via the management API. Revocation is immediate — all requests using that key will fail instantly.

Rotation Steps

  1. Create a new API key
  2. Update your application or environment variables to use the new key
  3. Verify the new key is working correctly
  4. Disable or delete the old key
bash
# Step 1: Create new key
POST https://bazaarlink.ai/api/v1/keys
Authorization: Bearer sk-bl-OLD_KEY
{"name": "Production v2"}
# → saves new key: sk-bl-NEW_KEY_VALUE

# Step 2: Update your application
# export BAZAARLINK_API_KEY=sk-bl-NEW_KEY_VALUE

# Step 3: Verify new key works
curl https://bazaarlink.ai/api/v1/models \
  -H "Authorization: Bearer sk-bl-NEW_KEY_VALUE"

# Step 4: Revoke old key
DELETE https://bazaarlink.ai/api/v1/keys/:old_key_id
Authorization: Bearer sk-bl-NEW_KEY_VALUE

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

Column
Description
dateISO 8601 timestamp (UTC)
modelModel ID (e.g. openai/gpt-4.1)
providerUpstream provider name
prompt_tokensInput token count
completion_tokensOutput token count
total_tokensTotal tokens (prompt + completion)
reasoning_tokensReasoning tokens (o-series / thinking models)
cached_tokensPrompt cache hit tokens
cost_ntdCost in NTD (台幣)
duration_msEnd-to-end latency in milliseconds
finish_reasonstop / length / content_filter / error
statusHTTP status code from upstream
app_nameX-Title header value (app attribution)

JSON Usage API

For programmatic access, query aggregated stats grouped by period, model, or key:

bash
# Query usage data (grouped / aggregated)
GET https://bazaarlink.ai/api/v1/usage
Authorization: Bearer sk-bl-YOUR_KEY

# With period filtering (day | week | month | year)
GET https://bazaarlink.ai/api/v1/usage?period=month

# Response
{
  "period": "month",
  "since": "2025-01-01T00:00:00.000Z",
  "credits": 10.5000,
  "totals": {
    "spend": 0.1812,
    "requests": 309,
    "tokens": 161200,
    "promptTokens": 95000,
    "completionTokens": 66200
  },
  "byModel": [{ "model": "openai/gpt-4.1", "spend": 0.0028, "tokens": 1200, "requests": 5 }],
  "byKey":   [{ "keyName": "My Agent", "spend": 0.0028, "tokens": 1200, "requests": 5 }],
  "byApp":   [{ "appName": "MyApp", "spend": 0.0015, "tokens": 600, "requests": 3 }],
  "timeSeries": [{ "date": "2025-01-15", "model": "openai/gpt-4.1", "cost": 0.0012, "tokens": 500, "requests": 2 }]
}

Usage Accounting

Query detailed usage statistics via API, including token consumption, cost analysis, and request history.

Note
Usage data is billed in USD. Individual request records are available on the Logs page or via CSV export. Aggregated stats (by period, model, or key) are available via the `/api/v1/usage` endpoint with Bearer token auth.

Response Field Reference

FieldTypeDescription
modelstringModel ID used (e.g., openai/gpt-4.1)
providerstringUpstream provider name
prompt_tokensnumberInput tokens consumed
completion_tokensnumberOutput tokens generated
total_tokensnumberTotal tokens (prompt + completion)
reasoning_tokensnumberReasoning tokens (for thinking models)
cached_tokensnumberPrompt tokens served from cache
costnumberTotal cost in NTD
duration_msnumberEnd-to-end latency in milliseconds
throughputnumberGeneration speed in tokens/sec
finish_reasonstringstop | length | content_filter | error
statusnumberHTTP status code from upstream
app_namestring | nullApplication name (X-Title header)
key_namestringAPI key name used for the request
python
import httpx

# Aggregated stats (Bearer token — period: day | week | month | year)
response = httpx.get(
    "https://bazaarlink.ai/api/v1/usage",
    headers={"Authorization": "Bearer sk-bl-YOUR_KEY"},
    params={"period": "month"},
)

data = response.json()
totals = data["totals"]
print("This month: NT$%.4f  (%d requests)" % (totals["spend"], totals["requests"]))

# Cost breakdown by model
for m in data["byModel"]:
    print("  %s: NT$%.4f  (%d reqs, %d tokens)" % (m["model"], m["spend"], m["requests"], m["tokens"]))

Institution Plan

The Institution Plan lets any institution (school, enterprise, conference, government, etc.) issue short-lived session tokens to its members from a single org-level key. Members don't need to create a platform account. The org controls which members can request tokens by email domain (e.g. nthu.edu.tw); all usage is billed to the org's account. This page uses the education scenario as an example — the same mechanism works for any institution that needs short-term, multi-user temporary access.

Who is this for
Institutions (schools, enterprises, conferences, government agencies, etc.) that want to give a group of members access to the AI API without creating individual accounts and without handing out long-lived API keys.

Architecture overview

  • Institution KeyStarts with sk-edu-. Created by an org_admin on the org keys page. Cannot be used directly as a Bearer token to call the API — direct calls return 403.
  • Member Session TokenStarts with edu-sess-. Members obtain it after email verification. Default lifetime is 24 hours; revocable by an org admin.
  • Allowed DomainsThe org configures which email domains (exact match, no suffix bypass) may request a session.
  • Usage attributionAll student requests are billed to the org account. Usage is viewable per session and per email in the org dashboard.

Step 1 — Request Institution Plan activation

Contact BazaarLink sales or support ([email protected] / [email protected]) and let us know your organization wants the Institution Plan enabled, with the list of allowed email domains (e.g. nthu.edu.tw). We will activate the feature for your organization:

json
{
  "orgType": "education",
  "eduConfig": {
    "allowedDomains": ["nthu.edu.tw", "student.nthu.edu.tw"],
    "sessionTtlSeconds": 86400,
    "verificationTtlSeconds": 900,
    "maxSessionsPerEmailPerKey": 5
  }
}
Domain matching is exact
nthu.edu.tw matches @nthu.edu.tw only — it will not match @nthu.edu.attacker.com. Subdomains must be listed explicitly (e.g. student.nthu.edu.tw).

Step 2 — Org admin creates an Institution Key

On the org's API Keys page, choose "Education" as the key type when creating a new key (this is the internal codename for the institution key). The system generates an sk-edu-... key and shows it ONCE — save it and distribute it through your official channels to that org's members.

Step 3 — Member requests a verification code

Members can request a verification code in two ways: (a) visit /access and enter the institution key + their institutional email — the page calls the API for them; (b) call the API directly:

POST/api/edu/request-code
bash
curl -X POST https://bazaarlink.ai/api/edu/request-code \
  -H "Content-Type: application/json" \
  -d '{
    "key": "sk-edu-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
    "email": "[email protected]"
  }'

# 200 / 202 always returns {"ok":true,"sent":true} (key-enumeration defence)
# Sends a 6-digit verification code to the email; default 15-minute lifetime
Anti-enumeration
request-code always returns 202 regardless of whether the key exists or the email domain is allowed, preventing attackers from probing which institution keys exist. Failed attempts are recorded in the org audit log.

Step 4 — Member submits the code to exchange for a session token

POST/api/edu/verify
bash
curl -X POST https://bazaarlink.ai/api/edu/verify \
  -H "Content-Type: application/json" \
  -d '{
    "key":   "sk-edu-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
    "email": "[email protected]",
    "code":  "646291"
  }'

# Success → 200
{
  "token":     "edu-sess-827d11a1ec67d175cfd4f67f929261f4",
  "expiresAt": "2026-05-04T11:16:00.163Z",
  "organization": { "id": "...", "name": "NTHU AI Lab" }
}

# Wrong code → 400 {"error":"invalid"}
# 5 wrong attempts → 400 {"error":"too_many_attempts"} (code invalidated; re-request)

Step 5 — Use the session token to call the API

Use the edu-sess-... token as a Bearer token against any chat / completions / embeddings endpoint:

bash
curl -X POST https://bazaarlink.ai/api/v1/chat/completions \
  -H "Authorization: Bearer edu-sess-827d11a1ec67d175cfd4f67f929261f4" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "anthropic/claude-haiku-4-5",
    "messages": [{"role": "user", "content": "Hello"}]
  }'
sk-edu- keys cannot be used directly
Sending sk-edu-... directly as a Bearer token to a chat endpoint returns:
403 — Education keys cannot be used directly. Visit /access to exchange for a session.
This is an intentional reverse gate — it stops the institution from leaking long-lived keys to individual members.

Org dashboard — monitoring and revocation

Education-type orgs get an Education tab in the side nav, providing:

  • SettingsAdjust allowed domains, TTL, max sessions per email per key, and per-session request / token / USD quotas.
  • SessionsList all active / expired / revoked sessions; filter by email; revoke individual sessions.
  • Usage statsPer-session call count, token consumption, and accumulated cost.

Security and limits

ItemDefaultDescription
Session TTL24 hoursSession token lifetime; expired sessions require re-verification.
Verification code TTL15 minutesLifetime of the email verification code.
Verification code length6 digitsStored as an HMAC-SHA256 hash in Redis, never in plaintext.
Guess limit5 attemptsBeyond this the code is invalidated immediately.
request-code cooldown60 secondsMinimum interval between repeated requests for the same (key, email).
Per-IP rate limit10 / 15 minAnti-spam.
Per-key rate limit100 / hourPrevents bulk email blasts.
Max sessions per email5Configurable in eduConfig; prevents one inbox from hoarding tokens.
Revocation propagation≤ 60 secondsL1/L2 cache TTL; after DB revocation it takes up to 60 seconds to propagate to all nodes.

Billing and usage attribution

All requests made via session tokens are billed 100% to the organization that owns the institution key, in line with how upstream providers (OpenAI / Anthropic / etc.) bill (per-token). The org dashboard supports drill-down by session, by email, and by key.

Organization Management

BazaarLink organizations use a three-tier architecture: Organization → Team → Member. Credits are stored at the org level; each Team and member can have a monthly spend cap. API requests check member → team → org credits in sequence.

Create & Manage Organizations

  1. Go to Settings → Organizations → Create New Organization
  2. Create Teams in the org portal (optional: cost center code and monthly budget)
  3. Invite members by email, assign a role and Team
  4. Issue API keys for members — usage is automatically tagged to the correct Team / member
  5. View the Reports page for monthly spend broken down by Team, Model, or Member

Member Roles

org_adminFull control: members, teams, billing, settings
billing_viewerRead-only access to financial reports (cannot see per-member detail)
team_adminManage members and budget within their own team
memberUse the API, subject to team and org budget limits

Three-Tier Budget System

On every API request, three budget layers are checked in order. Exceeding any layer returns HTTP 429:

  1. Member monthly budget (OrgMember.monthlyBudget)
  2. Team monthly budget (Team.monthlyBudget)
  3. Org credits balance (Organization.credits)

Usage Reports

The Reports page in the org portal provides monthly spend analytics across four dimensions:

  • 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.

Go to Org Portal

Management API (v1)

The /api/v1/orgs/ endpoints accept both Bearer management key (sk-bl-...) and session cookie, enabling server-to-server org management without a browser session.

Authentication
All /api/v1/orgs/ endpoints require org_admin role. Pass Authorization: Bearer sk-bl-<key> or a session cookie. Management keys can be created from Settings → API Keys.

Organizations

GET/api/v1/orgs

List all organizations the caller belongs to, with role and joinedAt.

GET/api/v1/orgs/:orgId

Get org detail including team and member counts.

bash
curl https://bazaarlink.ai/api/v1/orgs \
  -H "Authorization: Bearer sk-bl-YOUR_MANAGEMENT_KEY"

Teams

GET/api/v1/orgs/:orgId/teams

List teams with member counts, ordered by name.

POST/api/v1/orgs/:orgId/teams
namerequired
string
Team display name (must be unique within the org)
costCenterCode
string
Accounting cost center code
monthlyBudget
number | null
Team monthly spend cap in USD
PATCH/api/v1/orgs/:orgId/teams/:teamId

Partial update — include only the fields to change.

DELETE/api/v1/orgs/:orgId/teams/:teamId
bash
# Create a team
curl https://bazaarlink.ai/api/v1/orgs/{orgId}/teams \
  -X POST \
  -H "Authorization: Bearer sk-bl-YOUR_MANAGEMENT_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name": "Engineering", "costCenterCode": "ENG-001", "monthlyBudget": 500}'

Members

GET/api/v1/orgs/:orgId/members

List all members with nested user (id/name/email) and team info.

POST/api/v1/orgs/:orgId/members
emailrequired
string
Email of an existing BazaarLink user
role
string
org_admin | billing_viewer | team_admin | member (default: member)
teamId
string
Assign to a team (required if role is team_admin)
monthlyBudget
number | null
Per-member monthly spend cap in USD

404 if the email address has no BazaarLink account. 409 if already a member. Default role: member.

PATCH/api/v1/orgs/:orgId/members/:memberId

Partial update of role, teamId, or monthlyBudget.

DELETE/api/v1/orgs/:orgId/members/:memberId

Returns 400 if the target is the last org_admin.

bash
# Add a member
curl https://bazaarlink.ai/api/v1/orgs/{orgId}/members \
  -X POST \
  -H "Authorization: Bearer sk-bl-YOUR_MANAGEMENT_KEY" \
  -H "Content-Type: application/json" \
  -d '{"email": "[email protected]", "role": "member", "monthlyBudget": 50}'

# Remove a member
curl https://bazaarlink.ai/api/v1/orgs/{orgId}/members/{memberId} \
  -X DELETE \
  -H "Authorization: Bearer sk-bl-YOUR_MANAGEMENT_KEY"

Reports API

Query monthly spend data programmatically. Accessible to org_admin and billing_viewer. Accepts both web session and Bearer management key.

Query params: year (default current), month (default current, 1–12).

Endpoint
Description
GET /api/orgs/:orgId/reports/overviewTotal spend, margin rate, daily trend
GET /api/orgs/:orgId/reports/by-teamPer-team spend, share %, model breakdown, budget utilization
GET /api/orgs/:orgId/reports/by-modelPer-model spend, avg price ($/1M tokens)
GET /api/orgs/:orgId/reports/by-memberPer-member spend — org_admin only
GET /api/orgs/:orgId/reports/exportCSV download; add ?view=overview|by-team|by-model|by-member
bash
# Monthly overview via management key
curl "https://bazaarlink.ai/api/orgs/{orgId}/reports/overview?year=2026&month=3" \
  -H "Authorization: Bearer sk-bl-YOUR_MANAGEMENT_KEY"

# By-team breakdown
curl "https://bazaarlink.ai/api/orgs/{orgId}/reports/by-team?month=3" \
  -H "Authorization: Bearer sk-bl-YOUR_MANAGEMENT_KEY"

# Export CSV (downloads file)
curl "https://bazaarlink.ai/api/orgs/{orgId}/reports/export?month=3&view=by-team" \
  -H "Authorization: Bearer sk-bl-YOUR_MANAGEMENT_KEY" \
  -o report.csv

Technical architecture

RBAC nav visibility

ROLE_NAV_VISIBILITY (lib/auth/org-scope.ts) defines which navs each role sees in Org Portal. The backend enforces the same rules at the route layer via requireOrgRole() — UI hiding is only the first line of defense.

Role
Visible nav keys
org_adminoverview, teams, members, keys, allowed-models, reports, settings, circuit-breaker
billing_vieweroverview, keys, reports
team_adminoverview, teams, members, keys
memberkeys

Spend Circuit Breaker

To stop a single prompt or batch loop from running away with cost, every chat completion request passes through the scoped circuit breaker (lib/scoped-circuit-breaker.ts).

  • Dual-window rolling counter: 1-min + 1-hr sliding windows backed by Redis sorted sets
  • Default thresholds: minute = $5, hourly = $20 (CB_DEFAULTS in lib/spend-circuit-breaker.ts)
  • Settings resolution: member → team → org → default; each layer can override; 30s in-memory cache
  • Trip behavior: HTTP 503 with Retry-After header; recovers automatically once the rolling window rolls over
  • Cost path: every chat completion calls recordScopedUpstreamCost() in lib/usage-logger.ts

Allowed Models resolution chain

When a request hits /api/v1/chat/completions, the API key resolves to memberId → teamId → orgId, then Org.allowedModels is checked against the request's model id; mismatches return HTTP 403. Adding / removing models is instant — no redeploy needed.

Budget deduction architecture

  • Org credits: PostgreSQL UPDATE ... RETURNING — atomic deduct prevents race conditions
  • Team / Member layers: Redis INCRBY for real-time counting; reset by scheduled job at month start
  • Check order: member.monthlyBudget → team.monthlyBudget → org.credits; any layer over budget returns HTTP 429
  • User-facing balance is cached (5s TTL); the source of truth is DB / Redis

Error response reference

401API key invalid or revoked
403RBAC denial (insufficient role) or model not in Allowed Models whitelist
429Any of the three budget layers exceeded; response body contains scope (member / team / org) and reset time
503Spend Circuit Breaker tripped; Retry-After header indicates recovery time in seconds

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:

json
HTTP/1.1 403 Forbidden
Content-Type: application/json

{
  "error": {
    "message": "Model is not allowed for this account",
    "code": "model_not_allowed"
  }
}

Management API

All endpoints accept Web Session or Bearer Management Key (sk-bl-...). PATCH replaces the entire list; pass [] to clear.

bash
# Org-level list
GET    /api/orgs/:orgId/allowed-models
PATCH  /api/orgs/:orgId/allowed-models

# Team-level list
GET    /api/orgs/:orgId/teams/:teamId/allowed-models
PATCH  /api/orgs/:orgId/teams/:teamId/allowed-models

# Member-level list
GET    /api/orgs/:orgId/members/:memberId/allowed-models
PATCH  /api/orgs/:orgId/members/:memberId/allowed-models

# Example: restrict an org to OpenAI + a specific Anthropic model
curl -X PATCH https://bazaarlink.ai/api/orgs/$ORG_ID/allowed-models \
  -H "Authorization: Bearer sk-bl-..." \
  -H "Content-Type: application/json" \
  -d '{"allowedModels": ["openai/*", "anthropic/claude-sonnet-4.6"]}'

Circuit Breaker (Spend Kill Switch)

A dual-window rolling 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 rolling 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 rolls forward.
  • Defaults: $5 / minute, $20 / hour, enabled by default.
  • Counters live in Redis with TTL — recovery is automatic, no manual reset needed for org/team/member trips.

Scopes (member overrides team overrides org)

Each layer can set its own thresholds. Resolution order is member → team → org → platform default — the first non-null value wins per field (cbEnabled, cbMinuteUsd, cbHourlyUsd).

  • Org level — applies to all keys under the organization. Set in Org Portal → Circuit Breaker.
  • Team level — applies to all keys tagged to that team. Overrides org for those keys.
  • Member level — applies only to keys tagged to that member. Overrides team and org.

Trip behavior

When tripped, requests fail fast (no upstream call is made). The response is HTTP 429 with this body:

json
HTTP/1.1 429 Too Many Requests
Content-Type: application/json

{
  "error": {
    "message": "Spend circuit breaker tripped at member scope (minute window: $5.2341 ≥ $5.00). Try again later or contact your organization owner."
  }
}
Global vs scoped
A separate global platform-wide circuit breaker (operator-controlled, not visible in the org portal) returns HTTP 503 with a Retry-After header. Operators set it to defend the platform against multi-tenant abuse — it cannot be overridden from your org settings.

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.

bash
# Org-level config
GET    /api/orgs/:orgId/circuit-breaker
PATCH  /api/orgs/:orgId/circuit-breaker

# Team-level config
GET    /api/orgs/:orgId/teams/:teamId/circuit-breaker
PATCH  /api/orgs/:orgId/teams/:teamId/circuit-breaker

# Member-level config
GET    /api/orgs/:orgId/members/:memberId/circuit-breaker
PATCH  /api/orgs/:orgId/members/:memberId/circuit-breaker

# Example: tighten the org-level cap to $2/min, $10/hr
curl -X PATCH https://bazaarlink.ai/api/orgs/$ORG_ID/circuit-breaker \
  -H "Authorization: Bearer sk-bl-..." \
  -H "Content-Type: application/json" \
  -d '{"cbMinuteUsd": 2, "cbHourlyUsd": 10, "cbEnabled": true}'

# GET response (org scope)
{
  "settings":         { "cbEnabled": true, "cbMinuteUsd": 2,  "cbHourlyUsd": 10  },
  "resolvedSettings": { "cbEnabled": true, "cbMinuteUsd": 2,  "cbHourlyUsd": 10  },
  "liveSpend":        { "minuteSpend": 0.4123, "hourSpend": 3.8721 }
}
Support
Support
Hi! How can we help you?
Send a message and we'll get back to you soon.