API Reference
ApexApi follows the OpenAI API format. All endpoints use the base URL https://api.apexapi.dev/v1.
Authentication
All API requests require a valid API key passed in the Authorization header.
Authorization: Bearer ak-your-api-keyAPI keys start with ak- and are 48 characters long. Create keys in your dashboard.
Chat Completions
Create a chat completion with the specified model. Supports streaming via Server-Sent Events.
/v1/chat/completionsParameters
modelstringrequiredModel ID in provider/model format (e.g., openai/gpt-4o).
messagesarrayrequiredArray of message objects with role and content fields. Content can be a string or an array of multimodal parts. See Images & PDFs in Chat.
temperaturenumberSampling temperature between 0 and 2. Defaults to 1.
max_tokensintegerMaximum number of tokens to generate.
streambooleanIf true, responses are streamed via Server-Sent Events. Defaults to false.
top_pnumberNucleus sampling parameter. Defaults to 1.
stopstring | arrayStop sequences. Up to 4 sequences.
nintegerNumber of completions to generate, 1 to 8. Each one generates up to the full output budget, so an unbounded n is an unbounded charge. Values above 8 return 400.
Request
POST https://api.apexapi.dev/v1/chat/completions
Authorization: Bearer ak-your-api-key
Content-Type: application/json
{
"model": "openai/gpt-4o",
"messages": [
{ "role": "system", "content": "You are a helpful assistant." },
{ "role": "user", "content": "What is the capital of France?" }
],
"temperature": 0.7,
"max_tokens": 1024,
"stream": false
}Response
{
"id": "chatcmpl-abc123",
"object": "chat.completion",
"created": 1710000000,
"model": "openai/gpt-4o",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "The capital of France is Paris."
},
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 25,
"completion_tokens": 8,
"total_tokens": 33
}
}Where OpenAI compatibility stops
Point an OpenAI client at our base URL and it works. These are the four places our behaviour differs, so you find them here instead of in production.
Every stream ends with an extra usage frame
Before data: [DONE] we send one chunk with an empty choices array, an OpenAI-shaped usage object, and an x_apexapi object carrying the request id, the cost in USD and your remaining balance. It is the only way a streaming caller learns what a request cost. The official SDKs ignore a chunk with no choices; a hand-rolled parser that reads choices[0] without checking the length needs a guard.
An empty balance returns 402
A status OpenAI never sends. Every billed route uses it, with error.type set to insufficient_credits_error.
Some parameters do not reach every provider
Models served through Anthropic are not OpenAI-shaped upstream, so a few fields have no equivalent to translate into.
| Parameter | OpenAI, Mistral, DeepSeek, Groq, Together, Perplexity, Alibaba, Bedrock | Anthropic | |
|---|---|---|---|
| response_format | Applied | Applied | 400 |
| n above 1 | Applied | 400 | 400 |
| seed | Applied | Applied | Announced |
| frequency_penalty, presence_penalty | Applied | Applied | Announced |
| logprobs, top_logprobs, logit_bias | Applied | Announced | Announced |
Where a parameter would change the shape of what you get back, we return 400 with the field named in error.param rather than answer you something you cannot parse. Where it only affects sampling, the request runs and the response carries an x-apexapi-unsupported-params header listing what was not applied. Nothing is dropped in silence.
Images & PDFs in Chat
Chat completions accept multimodal content. Instead of a string, a message's content can be an array of parts. It's the same format as the OpenAI API, so existing SDKs work unchanged. The playground supports both via the attach button.
Content part types
textobjectA text segment: { "type": "text", "text": "..." }.
image_urlobjectAn image, for vision-capable models. Accepts a base64 data URI or a publicly reachable HTTPS URL: { "type": "image_url", "image_url": { "url": "..." } }. Optional detail: low | high | auto.
fileobjectA PDF document, for PDF-capable models. Must be a base64 application/pdf data URI, max 2 MB per file and 4 file parts per request.
Model support
Images work on all vision-capable models. PDFs are supported on Anthropic Claude, OpenAI GPT-4o / GPT-4.1 / o-series, and Google Gemini models. Sending a PDF to a model without PDF support returns 400 with error code unsupported_content.
Attachments are billed as input tokens by the upstream provider, roughly 1,000–1,600 tokens per image and 1,500–3,000 tokens per PDF page. They are processed in transit only and never stored by ApexApi.
Vision request
POST https://api.apexapi.dev/v1/chat/completions
Authorization: Bearer ak-your-api-key
Content-Type: application/json
{
"model": "anthropic/claude-sonnet-4.6",
"messages": [
{
"role": "user",
"content": [
{ "type": "text", "text": "What is in this image?" },
{
"type": "image_url",
"image_url": { "url": "data:image/jpeg;base64,/9j/4AAQ..." }
}
]
}
]
}PDF request
POST https://api.apexapi.dev/v1/chat/completions
Authorization: Bearer ak-your-api-key
Content-Type: application/json
{
"model": "openai/gpt-4o",
"messages": [
{
"role": "user",
"content": [
{ "type": "text", "text": "Summarize this document." },
{
"type": "file",
"file": {
"filename": "report.pdf",
"file_data": "data:application/pdf;base64,JVBERi0xLjQ..."
}
}
]
}
]
}Image Generation
Generate images from text prompts using supported image models.
/v1/images/generationsParameters
modelstringrequiredImage model ID (e.g., openai/gpt-image-2, google/nano-banana-2). Full list via GET /v1/models.
promptstringrequiredText description of the image to generate (max 4,000 characters).
nintegerNumber of images to generate, 1–10. Defaults to 1.
sizestringImage size as WIDTHxHEIGHT, e.g. 1024x1024 (default).
qualitystringImage quality. standard (default) or hd.
response_formatstringurl (default) or b64_json.
image_urlstringA single reference image to edit or condition on, as a publicly reachable HTTPS URL. Models with an edit variant switch to it automatically.
image_urlsstring[]Up to 10 reference images, for models that accept more than one. Check capabilities.image_edit and capabilities.max_reference_images on GET /v1/models before sending these. A model that takes no references returns a 400 saying so.
Request
POST https://api.apexapi.dev/v1/images/generations
Authorization: Bearer ak-your-api-key
Content-Type: application/json
{
"model": "openai/gpt-image-2",
"prompt": "A serene mountain landscape at sunset",
"n": 1,
"size": "1024x1024",
"quality": "standard"
}Response
{
"created": 1710000000,
"data": [
{
"url": "https://uploads.apexapi.dev/generated/req-id-0.png"
}
]
}The url is always a hosted HTTPS link (never inline base64), whichever model serves the request. Links are temporary. Download the image promptly rather than hotlinking.
Async: submit and poll
The route above holds the connection open for the whole render, which can take a minute or two. If you call it from somewhere with a shorter request timeout, a serverless function or a Lambda on its 30 second default or most HTTP clients out of the box, the connection drops mid-render. The render does not stop: the image finishes and is billed, and you only see a timeout. Use the async rail instead. Same body, same models, same price, but you get a job id immediately and fetch the result on your own schedule.
/v1/images/jobs# Submit, get a job id back right away:
POST https://api.apexapi.dev/v1/images/jobs
Authorization: Bearer ak-your-api-key
Content-Type: application/json
{ "model": "openai/gpt-image-2", "prompt": "A serene mountain landscape at sunset" }
# 202 Accepted:
{ "id": "b71c2f40-...", "status": "queued", "predicted_usd": 0.04 }
# Poll it whenever, from anywhere:
GET https://api.apexapi.dev/v1/images/jobs/b71c2f40-...
Authorization: Bearer ak-your-api-key
{ "id": "b71c2f40-...", "status": "completed",
"urls": ["https://uploads.apexapi.dev/generated/b71c2f40-0.png"],
"predicted_usd": 0.04, "actual_usd": 0.04 }Statuses are queued, processing, completed and failed. As with video, actual_usd stays null until the job settles and stays null on a failed job, because a failed job is not charged.
Video Generation
Generate video from a text prompt (and optionally a starting image) using supported video models like Veo, Kling, Seedance, Hailuo, and Grok Imagine. Video generation is asynchronous: you submit a job and get an id, then poll until it's done (renders take minutes).
/v1/videos/generationsParameters
modelstringrequiredVideo model ID (e.g., google/veo3.1/lite, bytedance/seedance-2.0/text-to-video). Full list via GET /v1/models (filter type=video).
promptstringrequiredText description of the video to generate.
durationintegerClip length in seconds. Allowed values are model-specific, e.g. Veo accepts 4, 6, or 8; Seedance accepts 4–15. An unsupported value returns 400 with the allowed set. Omit to use the model's default.
resolutionstring480p, 720p (default), 1080p, 2k, or 4k (model-dependent).
aspect_ratiostringe.g. 16:9, 9:16, 1:1.
image_urlstringStarting-frame image URL for image-to-video models.
Submit
POST https://api.apexapi.dev/v1/videos/generations
Authorization: Bearer ak-your-api-key
Content-Type: application/json
{
"model": "google/veo3.1/lite",
"prompt": "A calm ocean wave rolling onto a sandy beach at sunset",
"duration": 4,
"resolution": "720p",
"aspect_ratio": "16:9"
}{
"id": "a5933807-c44f-4eec-b25a-15f27a19b9ea",
"status": "queued"
}Poll for the result
GET https://api.apexapi.dev/v1/videos/generations/a5933807-...
Authorization: Bearer ak-your-api-key
# while rendering (poll every few seconds, renders take minutes):
{ "id": "a5933807-...", "status": "processing",
"predicted_usd": 0.3, "actual_usd": null }
# when finished:
{ "id": "a5933807-...", "status": "completed", "url": "https://.../video.mp4",
"predicted_usd": 0.3, "actual_usd": 0.3 }
# on failure (the pre-auth hold is released, you are not charged):
{ "id": "a5933807-...", "status": "failed", "error": "...",
"predicted_usd": 0.3, "actual_usd": null }You're billed per second of generated video, matching the duration you requested. A job that fails releases its hold. You are not charged for a video you didn't get.
Every poll response carries the cost in real dollars. predicted_usd is what was held up front, actual_usd is what you were charged once the job settled. It stays null until then, and on a failed job it stays null because nothing was charged.
File Uploads
Host a local image or video so it can be used as a generation input (image_urls / video_url). Two steps: presign, then PUT the raw bytes straight to storage. Uploading is free and never touches your credit balance, but the account must have a positive balance. Files are deleted automatically after about 48 hours, so upload as part of the generation flow, not for long-term storage.
/v1/uploadsParameters
content_typestringrequiredOne of image/png, image/jpeg, image/webp, image/gif (up to 20MB), video/mp4, video/quicktime (up to 100MB) or audio/mpeg, audio/wav, audio/mp4 (up to 25MB).
content_lengthintegerrequiredExact file size in bytes. The signed PUT is bound to it, so a different payload is rejected.
POST https://api.apexapi.dev/v1/uploads
Authorization: Bearer ak-your-api-key
Content-Type: application/json
{ "content_type": "video/mp4", "content_length": 8388608 }{
"upload_url": "https://...r2.cloudflarestorage.com/...signed...",
"public_url": "https://uploads.apexapi.dev/refs/1a2b3c4d5e6f7a8b/9f...e2.mp4",
"key": "refs/1a2b3c4d5e6f7a8b/9f...e2.mp4",
"expires_in": 600,
"note": "Files are retained for 48 hours. Use the public_url promptly as a reference input (image_urls / video_url)."
}PUT {upload_url}
Content-Type: video/mp4
<raw file bytes>Then pass public_url as image_urls / video_url in a generation request. The daily upload allowance scales with your credit balance (500MB at the base, up to effectively unlimited for high-balance accounts); if you hit it, the 429 names your limit.
/v1/uploadsGET https://api.apexapi.dev/v1/uploads
Authorization: Bearer ak-your-api-keyLists your still-alive uploads (newest first: url, key, bytes, uploaded_at), so a lost URL never forces a re-upload.
List Generations
Your completed generations are reusable as inputs, no re-upload needed. Outputs live on permanent hosted URLs, so any listed url can go straight into image_urls, video_url or an edit source in a new generation request. Listing is free.
/v1/generationsQuery parameters
kindstringimage or video. Omit for both.
limitintegerRows per page, default 20, max 100.
cursorstringOpaque next_cursor from the previous page.
GET https://api.apexapi.dev/v1/generations?kind=image&limit=20
Authorization: Bearer ak-your-api-key{
"data": [
{
"id": "a5933807-c44f-4eec-b25a-15f27a19b9ea",
"kind": "image",
"model": "openai/gpt-image-2",
"prompt": "a cat in a hat",
"urls": ["https://uploads.apexapi.dev/generated/1a2b.../0.png"],
"created_at": "2026-07-15T00:00:00.000Z"
}
],
"next_cursor": "MjAyNi0wNy0xNVQwMD..."
}Only completed generations with a still-hosted output are listed, so a page can carry fewer rows than limit. Keep following next_cursor until it is null.
Audio (Text-to-Speech)
Generate natural voiceover from text using ElevenLabs models. Returns a URL to an MP3 file. Billed per 1,000 characters.
/v1/audio/speechParameters
modelstringrequiredAudio model ID (e.g., elevenlabs/tts/multilingual-v2).
inputstringrequiredThe text to synthesize into speech (1–10,000 characters).
voicestringOptional ElevenLabs voice name or id. Leave empty for the model default voice.
Request
POST https://api.apexapi.dev/v1/audio/speech
Authorization: Bearer ak-your-api-key
Content-Type: application/json
{
"model": "elevenlabs/tts/multilingual-v2",
"input": "Welcome to ApexAPI — one API for every AI model.",
"voice": "Rachel"
}Response
{
"created": 1710000000,
"data": [
{ "url": "https://.../output.mp3" }
]
}List Models
Retrieve a list of all available models and their pricing.
/v1/modelsResponse
{
"object": "list",
"data": [
{
"id": "mistralai/mistral-large-2512",
"object": "model",
"created": 1782293692,
"owned_by": "mistralai",
"type": "chat",
"display_name": "Mistral: Mistral Large 3 2512",
"context_length": 262144,
"max_output_tokens": null,
"capabilities": {
"streaming": true,
"images": true,
"vision": true,
"tools": true,
"image_edit": false,
"max_reference_images": 0
},
"pricing": {
"input": 6e-07,
"output": 1.8e-06,
"unit": "per_token",
"display": "$0.6/M input, $1.80/M output tokens"
}
}
]
}capabilities.vision means the model reads an image you send it in a chat message. capabilities.images is the older name for the same thing and still ships. capabilities.image_edit is the different question: whether an image model accepts a reference image you pass to /v1/images/generations, and capabilities.max_reference_images is how many it takes. Pricing reflects the rail that will actually serve your request, so the number you see is the number you pay.
Context for AI (web data)
Give models live web knowledge on the same key and balance. Billed per unit: crawl and extract failures are free, and scrape is billed once the fetch is dispatched to a rail (a blocked page or unreadable content is still billed; a rejected URL is not). Full guides: read a page, read a site, structured extract.
/v1/scrape(URL → markdown)curl https://api.apexapi.dev/v1/scrape \
-H "Authorization: Bearer ak-your-api-key" \
-H "Content-Type: application/json" \
-d '{ "url": "https://example.com/pricing", "stealth": "auto" }'
# → { "content": "# Pricing…", "tier": "standard", "cost": 0.002, … }/v1/crawl(site → markdown, async)curl https://api.apexapi.dev/v1/crawl \
-H "Authorization: Bearer ak-your-api-key" \
-H "Content-Type: application/json" \
-d '{ "url": "https://docs.example.com", "limit": 200 }'
# → { "id": "…", "status": "running" } then poll GET /v1/crawl/{id}/v1/extract(structured JSON, async)curl https://api.apexapi.dev/v1/extract \
-H "Authorization: Bearer ak-your-api-key" \
-H "Content-Type: application/json" \
-d '{ "scraper": "amazon-product",
"input": { "url": "https://www.amazon.com/dp/B09B8V1LZ3" } }'
# → { "id": "…", "status": "running" } then poll GET /v1/extract/{id}
# List scrapers: GET /v1/extract/scrapers