ApexApiApexApi
Sign In

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-key

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

POST/v1/chat/completions

Parameters

modelstringrequired

Model ID in provider/model format (e.g., openai/gpt-4o).

messagesarrayrequired

Array of message objects with role and content fields. Content can be a string or an array of multimodal parts. See Images & PDFs in Chat.

temperaturenumber

Sampling temperature between 0 and 2. Defaults to 1.

max_tokensinteger

Maximum number of tokens to generate.

streamboolean

If true, responses are streamed via Server-Sent Events. Defaults to false.

top_pnumber

Nucleus sampling parameter. Defaults to 1.

stopstring | array

Stop sequences. Up to 4 sequences.

ninteger

Number 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

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

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.

ParameterOpenAI, Mistral, DeepSeek, Groq, Together, Perplexity, Alibaba, BedrockGoogleAnthropic
response_formatAppliedApplied400
n above 1Applied400400
seedAppliedAppliedAnnounced
frequency_penalty, presence_penaltyAppliedAppliedAnnounced
logprobs, top_logprobs, logit_biasAppliedAnnouncedAnnounced

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

textobject

A text segment: { "type": "text", "text": "..." }.

image_urlobject

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

fileobject

A 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

Request (image)
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

Request (PDF document)
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.

POST/v1/images/generations

Parameters

modelstringrequired

Image model ID (e.g., openai/gpt-image-2, google/nano-banana-2). Full list via GET /v1/models.

promptstringrequired

Text description of the image to generate (max 4,000 characters).

ninteger

Number of images to generate, 1–10. Defaults to 1.

sizestring

Image size as WIDTHxHEIGHT, e.g. 1024x1024 (default).

qualitystring

Image quality. standard (default) or hd.

response_formatstring

url (default) or b64_json.

image_urlstring

A 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

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

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.

POST/v1/images/jobs
Submit and poll
# 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).

POST/v1/videos/generations

Parameters

modelstringrequired

Video model ID (e.g., google/veo3.1/lite, bytedance/seedance-2.0/text-to-video). Full list via GET /v1/models (filter type=video).

promptstringrequired

Text description of the video to generate.

durationinteger

Clip length in seconds. Allowed values are model-specific, e.g. Veo accepts 4, 6, or 8; Seedance accepts 415. An unsupported value returns 400 with the allowed set. Omit to use the model's default.

resolutionstring

480p, 720p (default), 1080p, 2k, or 4k (model-dependent).

aspect_ratiostring

e.g. 16:9, 9:16, 1:1.

image_urlstring

Starting-frame image URL for image-to-video models.

Submit

Request
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"
}
Response (202 Accepted)
{
  "id": "a5933807-c44f-4eec-b25a-15f27a19b9ea",
  "status": "queued"
}

Poll for the result

Poll GET /v1/videos/generations/{id}
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.

POST/v1/uploads

Parameters

content_typestringrequired

One 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_lengthintegerrequired

Exact file size in bytes. The signed PUT is bound to it, so a different payload is rejected.

1. Presign
POST https://api.apexapi.dev/v1/uploads
Authorization: Bearer ak-your-api-key
Content-Type: application/json

{ "content_type": "video/mp4", "content_length": 8388608 }
Response
{
  "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)."
}
2. Upload the bytes
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.

GET/v1/uploads
List your live uploads
GET https://api.apexapi.dev/v1/uploads
Authorization: Bearer ak-your-api-key

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

GET/v1/generations

Query parameters

kindstring

image or video. Omit for both.

limitinteger

Rows per page, default 20, max 100.

cursorstring

Opaque next_cursor from the previous page.

Request
GET https://api.apexapi.dev/v1/generations?kind=image&limit=20
Authorization: Bearer ak-your-api-key
Response
{
  "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.

POST/v1/audio/speech

Parameters

modelstringrequired

Audio model ID (e.g., elevenlabs/tts/multilingual-v2).

inputstringrequired

The text to synthesize into speech (1–10,000 characters).

voicestring

Optional ElevenLabs voice name or id. Leave empty for the model default voice.

Request

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

Response
{
  "created": 1710000000,
  "data": [
    { "url": "https://.../output.mp3" }
  ]
}

List Models

Retrieve a list of all available models and their pricing.

GET/v1/models

Response

Response
{
  "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.

POST/v1/scrape(URL → markdown)
Read a page
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, … }
POST/v1/crawl(site → markdown, async)
Read a site
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}
POST/v1/extract(structured JSON, async)
Structured extract
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