Text
Generate text through the AudioLab API and choose a compatible client protocol.
Before You Begin
Text, embeddings, images, video, and audio use the same AudioLab API URL and workspace key. For text requests, choose an enabled model and a key with llm.write access in a funded workspace.
Set the production API URL without /v1. For staging, use the staging API URL and credentials together:
export AUDIOLAB_API_KEY="YOUR_WORKSPACE_KEY"
export AUDIOLAB_API_URL="https://api.tryaudiolab.ai"
export AUDIOLAB_BASE_URL="$AUDIOLAB_API_URL/v1"Use AUDIOLAB_API_URL for raw HTTP examples that append /v1/.... Use AUDIOLAB_BASE_URL for compatible SDKs that append /chat/completions themselves. Keep these conventions consistent to avoid a duplicated /v1 path.
Send a Chat Completion
This example uses openai/gpt-5.5; use it only if that model is enabled on your gateway, or replace the ID with an enabled text model. Generate a new idempotency key for each new logical request:
export AUDIOLAB_REQUEST_ID="$(uuidgen)"
curl --fail-with-body --show-error --silent \
"$AUDIOLAB_API_URL/v1/chat/completions" \
-H "Authorization: Bearer $AUDIOLAB_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $AUDIOLAB_REQUEST_ID" \
--data '{
"model": "openai/gpt-5.5",
"messages": [
{"role": "user", "content": "Explain API routing in one sentence."}
],
"max_tokens": 256
}'The generated text is in choices[0].message.content. Keep the response identifier for troubleshooting. Output varies, so check the response structure rather than expecting a particular sentence. The command requires uuidgen and a version of curl supporting --fail-with-body.
max_tokens is optional: without it the model may write up to its output cap, listed in List Models, and you pay for the tokens it writes. Adding stream: true changes the response to SSE; follow the streaming guide instead of parsing the entire response as one JSON document.
While a request runs, part of your credits is held for it and the rest is released when it finishes. With max_tokens set you are never charged more than that hold, even when an xAI Grok model on Responses reasons past the cap; without max_tokens, a long answer can be charged past the hold, up to the model's maximum output.
Send Images and PDFs
A model reads images when architecture.input_modalities in List Models includes image, and PDFs when it includes file. Add them to a user message as content parts, by https URL or as a base64 data URI:
curl --fail-with-body --show-error --silent \
"$AUDIOLAB_API_URL/v1/chat/completions" \
-H "Authorization: Bearer $AUDIOLAB_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
--data '{
"model": "openai/gpt-5.4-nano",
"messages": [{
"role": "user",
"content": [
{"type": "text", "text": "Name the logo, then quote the PDF title."},
{"type": "image_url", "image_url": {"url": "https://raw.githubusercontent.com/github/explore/main/topics/javascript/javascript.png"}},
{"type": "file", "file": {"filename": "dummy.pdf", "file_data": "https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf"}}
]
}],
"max_tokens": 200
}'| API | Image | |
|---|---|---|
| Chat Completions | {"type": "image_url", "image_url": {"url": "..."}} | {"type": "file", "file": {"filename": "...", "file_data": "..."}} |
| Responses | {"type": "input_image", "image_url": "..."} | {"type": "input_file", "filename": "...", "file_data": "..."} or file_url |
| Messages | {"type": "image", "source": {...}} | {"type": "document", "source": {"type": "base64" or "url", ...}} |
Images and PDF pages are billed as input tokens at the model’s input rate, as the provider counts them in the usage’s prompt tokens. Your workspace’s credit hold includes an estimate for each image and page.
| Limit | Value |
|---|---|
| Image formats | PNG, JPEG, WebP and GIF; Gemini reads no GIF, and Grok only PNG and JPEG |
| Each image | 5 MB |
| Each PDF | 10 MB |
| Per request | 32 images, 8 PDFs, 20 MB of media |
| PDF pages read by the model | 100 per request; 600 on Claude models with a 1M context, 1,000 on Gemini |
| URLs | Public https addresses only, fetched once within 15 seconds; at most 16 per request |
| Request body | 20 MB, base64 included; send large files by URL |
Uploaded file IDs are not accepted. A model that cannot read an image or PDF is skipped as a fallback; requested alone, it returns 400 unsupported_parameter.
Read PDFs on Any Model
A PDF goes to the model itself when the requested model lists file input. Otherwise its text is read first and sent as text. Choose how with the file-parser plugin:
"plugins": [{"id": "file-parser", "pdf": {"engine": "mistral-ocr"}}]| Engine | Reads | Price |
|---|---|---|
native | The model reads each page | Input tokens at the model’s rate |
pdf-text | The PDF’s own text layer, up to 500 pages per request; scanned PDFs have none | Free |
mistral-ocr | Every page, with Mistral OCR 3; up to 32 pages and 4 MB per PDF | $0.0017 per page |
Without an engine, a model that reads PDFs gets native and any other model gets pdf-text. cloudflare-ai is accepted as pdf-text. max_files caps the PDFs a request may carry.
mistral-ocr runs as its own OCR request on your key, so the key needs ocr.write. If the model's answer then fails or comes back empty, the OCR charge is refunded. On Chat Completions the text read comes back as a file annotation on the assistant message; send that message back with its annotations and the same PDF is not read, or charged, again.
Search the Web
Add the web plugin and the model searches the web with its provider’s own search, then cites what it used:
curl --fail-with-body --show-error --silent \
"$AUDIOLAB_API_URL/v1/chat/completions" \
-H "Authorization: Bearer $AUDIOLAB_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
--data '{
"model": "openai/gpt-5.4-nano",
"messages": [{"role": "user", "content": "Who is the current UN Secretary-General?"}],
"plugins": [{"id": "web"}],
"max_tokens": 300
}'These turn on the same search: the :online suffix (openai/gpt-5.4-nano:online, which combines with the other variants in any order), web_search_options, or a search tool, {"type": "web_search"} on Responses, {"type": "web_search_20250305", "name": "web_search"} on Messages and {"type": "openrouter:web_search"} on Chat Completions.
| Models | Search | Price |
|---|---|---|
| OpenAI GPT-4o, GPT-4.1, the GPT-5 and GPT-6 chat models, o3 | OpenAI web search | $0.0085 per call |
| Gemini 3 and later | Grounding with Google Search | $0.0119 per search query |
| Gemini 2.5 | Grounding with Google Search | $0.02975 per grounded request |
| Grok 4.3, Grok 4.20 Non-Reasoning | xAI web search | $0.00425 per call |
pricing.web_search in List Models lists each model’s price; usage.server_tool_use.web_search_requests counts the searches you paid for. Search results OpenAI and Grok models read are billed as input tokens. An OpenAI request makes at most 5 calls, or max_uses up to 16; Gemini and Grok decide for themselves, and the hold covers 16 Gemini queries or 32 Grok calls.
| API | Citations |
|---|---|
| Chat Completions | url_citation items in message.annotations, with start_index and end_index where the provider places them |
| Responses | url_citation annotations on the output_text part |
| Messages | web_search_result_location items in the text block’s citations |
A model without its own search is skipped as a fallback; requested alone, it returns 400 unsupported_parameter. Gemini searches without function tools, and Gemini and Grok without a response_format. OpenAI does not search at minimal reasoning effort. Search does not combine with costream/fusion. Options a provider does not apply, such as max_results, are named in x-audiolab-dropped-params.
Repair JSON and Fit Long Conversations
| Plugin | What It Does |
|---|---|
{"id": "response-healing"} | Repairs a JSON answer that does not parse (code fences, text around it, trailing commas, missing brackets) when response_format asks for JSON. Non-streaming only; no extra model call and no charge. An answer cut off by max_tokens is left as it is. |
"transforms": ["middle-out"] or {"id": "context-compression"} | Removes messages from the middle of a conversation that is too long, until it fits the smallest context among the requested model and its fallbacks. System messages, the first message and the latest one stay. |
Each plugin reports what it did in the plugins field of the routing explanation.
Choose a Client Protocol
| Endpoint | Best Fit | Compatibility Boundary |
|---|---|---|
| Chat Completions | Message-based applications and supported tool loops | Options and multimodal inputs depend on the enabled model profile |
| Responses | Supported stateless reasoning and client-tool workflows | Native GPT-5.3 Codex path; other admitted models use limited translation |
| Messages | Clients using the Anthropic request shape | Translated subset; signed thinking and server tools other than web search are not supported |
All three route alike: costream/auto, fallback models, costream/fusion and named routers work on each, streamed or not. See Model Routing.
Protocol compatibility does not mean every feature of the provider API is implemented. In particular, stored response state, hosted tools other than web search, and compaction are not enabled on the Responses path. Read the endpoint’s limitations before connecting a client that requires those features.
Cache Prompts and Answers
Prompt caching makes a long, repeated prompt prefix cheaper. Anthropic models cache where you mark them: add "cache_control": {"type": "ephemeral"} to a text part, system block or tool (at most four), or at the top level to cache up to the last message. OpenAI and Gemini models cache long prefixes automatically. Cache writes and reads are charged at the model’s cache rates and reported in usage.prompt_tokens_details as cache_write_tokens and cached_tokens.
Response caching answers an identical repeated request without calling a model. Send x-audiolab-cache: true (and optionally x-audiolab-cache-ttl in seconds, up to 86400; default 300). A repeat of the same request body from your workspace within that time is free: usage.cost is 0, usage.cache_hit is true, and x-audiolab-cache-status reads HIT. Answers are never shared between workspaces, and are never stored under zero data retention.
Choose a Service Tier
service_tier: "flex" runs on cheaper, slower capacity and "priority" on faster capacity, on OpenAI and Gemini models with a reviewed price for that tier. The request is charged at that tier’s rate, and the response’s service_tier names the tier that served it. Where a model has no such tier, or the request uses web search on an OpenAI or xAI model, the tier is dropped and named in x-audiolab-dropped-params, or refused when provider.require_parameters is true.
Request Several Completions
n (up to 8) on Chat Completions returns that many choices for one model. Each is charged, and the hold scales with n. Streaming n above 1 needs a model that returns several completions in one call, such as OpenAI’s; with web search, each completion is a separate call with its own searches. For many unrelated prompts, use Batches.
Continue a Conversation or Call a Tool
For Chat Completions, your application keeps message history and includes it in each request. A tool call asks your application to do work; the gateway does not execute your functions. Validate arguments, run only permitted tools, and return each result with its matching call ID.
Follow Build Your Own Agent for a bounded example. For a coding client, use the integration guides and verify the client’s required features before using it on a real project.
A timed-out request may already have reached a provider. Keep the same idempotency key when retrying the same logical request, and inspect conflicts or unresolved usage before creating a replacement request. See Errors and Debugging.