API reference
Base URL https://api.oceantoken.ai/v1. Authenticate with Authorization: Bearer sk-.... Paths on this page are written out in full, so /v1/chat/completions is https://api.oceantoken.ai/v1/chat/completions.
Which API shape
OceanToken does not invent request formats for models. Each model endpoint follows one existing API, named in the Shape column below, so the matching official SDK works with only the base URL and key changed:
| Shape | Endpoints | Use |
|---|---|---|
| OpenAI | chat completions, responses, images, video, embeddings, speech, transcription, models | The OpenAI SDK with base_url="https://api.oceantoken.ai/v1" |
| Anthropic | /v1/messages | The Anthropic SDK with base_url="https://api.oceantoken.ai", for Claude models. That SDK adds /v1 itself |
| Cohere | /v1/rerank | Cohere's rerank request: query, documents, top_n |
| OceanToken | /v1/usage/{request_id} | Our own, and the only one: what a request cost. See What a request cost |
Within a shape, a parameter the chosen model does not support is dropped and the request runs without it. This is what lets one request body work across every model: top_k is meaningful to Claude and not to GPT, logit_bias the other way round, and a request carrying either still succeeds on both. Nothing is rewritten, and a parameter the model does support always reaches it.
The consequence is worth stating plainly: a successful response is not evidence that every parameter you sent took effect. When you depend on one, check the response for it rather than the absence of an error - tool_calls for a function call, tools echoed back for a hosted tool, response_format honoured in the content. A capability a model does not have is not reported as an error, because for most callers that would turn a portable request into a model-specific one.
Check this once at integration time, with the model you will use, rather than discovering in production that a parameter you rely on was never applied.
Endpoints
| Endpoint | What it does | Shape | Models |
|---|---|---|---|
POST /v1/chat/completionsChat completions | Send a conversation to a language model and get the next message back. Streams with stream: true. | OpenAI | Text models |
POST /v1/responsesResponses | The stateful successor to chat completions, with built-in tools and multi-turn state. | OpenAI | Text models |
POST /v1/messagesMessages | Anthropic-style messages for Claude models. Use the Anthropic SDK unchanged. | Anthropic | Claude models |
POST /v1/images/generationsImage generation | Generate images from a text prompt. | OpenAI | Image models |
POST /v1/images/editsImage edits | Edit an image with a prompt and an optional mask. | OpenAI | Image models |
POST /v1/videosCreate a video job | Start a video generation job. Videos take minutes; poll the job, then fetch the finished file. Send an Idempotency-Key so a retry cannot start a second job. | OpenAI | Video models |
GET /v1/videosList video jobs | Your recent video jobs and their states. | OpenAI | Video models |
GET /v1/videos/{video_id}Video status | The state of a video job and, once finished, where to download it. | OpenAI | Video models |
GET /v1/videos/{video_id}/contentVideo content | Download the finished video file. | OpenAI | Video models |
POST /v1/embeddingsEmbeddings | Turn text into vectors for search and retrieval. | OpenAI | Embedding models |
POST /v1/audio/speechText to speech | Turn text into spoken audio. | OpenAI | Speech models |
POST /v1/audio/transcriptionsTranscription | Turn an audio file into text. | OpenAI | Transcription models |
POST /v1/rerankRerank | Order a list of documents by relevance to a query. | Cohere | Rerank models |
GET /v1/modelsList models | Every model your key can call, in OpenAI's list format. | OpenAI | all keys |
GET /v1/usage/{request_id}Request cost | What one request cost and the tokens it used, looked up by the id of its response. For streamed responses, whose cost cannot travel in a header. | OceanToken | all keys |
The Models column opens the Models page filtered to the models that endpoint serves, with their prices; a model's type tells you which endpoint to call.
Text
/v1/chat/completions is the endpoint most clients use, and the one the first request shows. /v1/responses is the newer stateful API with the same models. Claude models answer on /v1/messages with the Anthropic SDK unchanged.
Streaming
Set "stream": true on any text endpoint to receive tokens as they are generated. The response is server-sent events (Content-Type: text/event-stream), each event a data: line carrying one JSON chunk in the same format the shape's SDK expects, and the stream ends with data: [DONE]. To receive token counts at the end of a chat-completions stream, send "stream_options": {"include_usage": true}; the last chunk before [DONE] then carries usage. Responses and Messages streams include usage in their own final events. Errors during a stream are described in Errors.
Images
# Generate an image. The response carries a URL you can download from.
curl -sS "${OCEANTOKEN_BASE_URL:-https://api.oceantoken.ai/v1}/images/generations" \
-H "Authorization: Bearer $OCEANTOKEN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "openai/gpt-image-2",
"prompt": "A small lighthouse on a calm sea at dawn, watercolor",
"size": "1024x1024",
"n": 1
}'
The response contains a URL for each image. Download it promptly; URLs are not permanent. For edits, send the source image and an optional mask to /v1/images/edits.
Video
Video is asynchronous. Submit a job, poll its status, then download the file.
# Video generation is a job: submit it, poll until it is done, then download the file.
# Videos take minutes to render, so never wait on the first request.
import os
import time
import requests
BASE = os.getenv("OCEANTOKEN_BASE_URL", "https://api.oceantoken.ai/v1")
HEADERS = {"Authorization": f"Bearer {os.environ['OCEANTOKEN_API_KEY']}"}
job = requests.post(
f"{BASE}/videos",
headers=HEADERS,
json={
"model": "bytedance/seedance-1-5-pro",
"prompt": "A paper boat drifting down a rain gutter, close up, soft light",
"seconds": "4",
},
timeout=60,
)
if not job.ok:
# The error body says why (see Errors in the docs), e.g. insufficient_credits.
raise SystemExit(f"{job.status_code}: {job.text}")
video_id = job.json()["id"]
print("submitted", video_id)
while True:
status = requests.get(f"{BASE}/videos/{video_id}", headers=HEADERS, timeout=60).json()
if status["status"] in ("completed", "failed"):
break
time.sleep(10)
if status["status"] != "completed":
raise SystemExit(f"video failed: {status.get('error')}")
content = requests.get(f"{BASE}/videos/{video_id}/content", headers=HEADERS, timeout=120)
content.raise_for_status()
with open(f"{video_id}.mp4", "wb") as f:
f.write(content.content)
print("saved", f"{video_id}.mp4", len(content.content), "bytes")
A job moves through queued → in_progress → completed or failed. POST /v1/videos returns the job with its id and initial state; GET /v1/videos/{video_id} returns the current state, and once it is completed, GET /v1/videos/{video_id}/content streams the file. Rendering takes minutes, so poll every 10 to 20 seconds rather than continuously; GET /v1/videos lists your recent jobs if you lose an id.
A job's estimated cost is held against your balance when it starts and settled to the actual cost when it finishes; a failed job is not charged. Download URLs are valid for 24 hours. Send an Idempotency-Key with the POST so a retry after a timeout cannot start a second job (see below).
Embeddings
# Embeddings for search and retrieval.
curl -sS "${OCEANTOKEN_BASE_URL:-https://api.oceantoken.ai/v1}/embeddings" \
-H "Authorization: Bearer $OCEANTOKEN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "baai/bge-m3",
"input": "The lighthouse keeper rowed out before dawn."
}'
Speech, transcription, rerank
/v1/audio/speech takes model, input text and a voice and returns audio bytes. /v1/audio/transcriptions takes a multipart upload with model and file and returns text. /v1/rerank takes a query and documents and returns them scored. Speech and transcription follow the OpenAI request shape, rerank the Cohere one; the model's page in the console shows an example for each.
Retries and idempotency
Retry on 429, 5xx and network failures with backoff; never on other 4xx. On the endpoints that cost money to run - /v1/images/generations, /v1/images/edits, /v1/videos and /v1/audio/speech - send an Idempotency-Key header so a retry replays the first answer instead of running again:
#!/usr/bin/env bash
# Retry a billable request safely: the same Idempotency-Key with the same body
# replays the first response instead of generating (and charging) again.
set -euo pipefail
BASE_URL="${OCEANTOKEN_BASE_URL:-https://api.oceantoken.ai/v1}"
KEY="demo-$(date +%s)-$RANDOM"
BODY='{"model": "openai/gpt-image-2", "prompt": "A paper boat on a pond at dawn", "size": "1024x1024", "quality": "low"}'
first=$(curl -s "$BASE_URL/images/generations" \
-H "Authorization: Bearer $OCEANTOKEN_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $KEY" \
-d "$BODY")
# An error answer is replayed too, so check the first call made an image.
grep -q '"data"' <<<"$first" || { echo "request failed: $first" >&2; exit 1; }
# The retry: identical key and body. Nothing runs; the stored answer comes back
# with the header Idempotency-Replayed: true.
second=$(curl -s -D /tmp/idem-headers "$BASE_URL/images/generations" \
-H "Authorization: Bearer $OCEANTOKEN_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $KEY" \
-d "$BODY")
grep -qi '^idempotency-replayed: true' /tmp/idem-headers
[ "$first" = "$second" ]
echo "replayed: identical response, charged once"
The rules (24-hour window, 409 on reuse with a different body, Retry-After while the first call runs) are in Errors.
What a request cost
Every successful response includes usage, and every request appears in the console under Logs with what it was charged. To read the cost from code:
- A non-streaming response carries it in the header
x-oceantoken-cost, in US dollars, rounded to four significant figures. Your balance is charged the exact amount. - A streamed chat completion cannot: headers are sent before the first token, and the cost is known only after the last. Look it up afterwards with
GET /v1/usage/{request_id}, whererequest_idis theidof the completion. Every chunk of a stream carries it.
curl https://api.oceantoken.ai/v1/usage/$REQUEST_ID \
-H "Authorization: Bearer $OCEANTOKEN_API_KEY"
The answer has id, model, cost (US dollars, four significant figures), status, usage with prompt_tokens, completion_tokens and total_tokens, started_at, ended_at and duration_ms.
- Use the key that made the request. With any other key the answer is
404, so the endpoint never confirms that someone else's request exists. - The cost is recorded a few seconds after the request finishes, and until then the answer is also
404. Retry after a few seconds. - The lookup takes the
idin the response body, not thex-oceantoken-request-idheader described in Errors.
Errors are described in Errors.