API reference
Base URL https://api.oceantoken.ai. Authenticate with Authorization: Bearer sk-....
Which API shape
OceanToken does not invent request formats. Each 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 |
| Cohere | /v1/rerank | Cohere's rerank request: query, documents, top_n |
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.
To know in advance, ask the model: GET /model_group/info?model_group=<model> returns that model's supported_openai_params, the exact list the gateway will forward. Checking it once at integration time is cheaper than discovering at runtime that a parameter you rely on was never sent.
curl https://api.oceantoken.ai/model_group/info?model_group=anthropic/claude-sonnet-5 \
-H "Authorization: Bearer $OCEANTOKEN_API_KEY" \
| jq '.data[0].supported_openai_params'
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 | 344 |
POST /v1/responsesResponses | The stateful successor to chat completions, with built-in tools and multi-turn state. | OpenAI | 344 |
POST /v1/messagesMessages | Anthropic-style messages for Claude models. Use the Anthropic SDK unchanged. | Anthropic | 344 |
POST /v1/images/generationsImage generation | Generate images from a text prompt. | OpenAI | 51 |
POST /v1/images/editsImage edits | Edit an image with a prompt and an optional mask. | OpenAI | 51 |
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 | 29 |
GET /v1/videosList video jobs | Your recent video jobs and their states. | OpenAI | 29 |
GET /v1/videos/{video_id}Video status | The state of a video job and, once finished, where to download it. | OpenAI | 29 |
GET /v1/videos/{video_id}/contentVideo content | Download the finished video file. | OpenAI | 29 |
POST /v1/embeddingsEmbeddings | Turn text into vectors for search and retrieval. | OpenAI | 33 |
POST /v1/audio/speechText to speech | Turn text into spoken audio. | OpenAI | 17 |
POST /v1/audio/transcriptionsTranscription | Turn an audio file into text. | OpenAI | 21 |
POST /v1/rerankRerank | Order a list of documents by relevance to a query. | Cohere | 7 |
GET /v1/modelsList models | Every model your key can call, in OpenAI's list format. | OpenAI | all keys |
The Models column counts the models currently deployed behind that endpoint. Use the Models page to choose one; its 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")
HEADERS = {"Authorization": f"Bearer {os.environ['OCEANTOKEN_API_KEY']}"}
job = requests.post(
f"{BASE}/v1/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,
)
job.raise_for_status()
video_id = job.json()["id"]
print("submitted", video_id)
while True:
status = requests.get(f"{BASE}/v1/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}/v1/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
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 "$OCEANTOKEN_BASE_URL/v1/images/generations" \
-H "Authorization: Bearer $OCEANTOKEN_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $KEY" \
-d "$BODY")
# 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 "$OCEANTOKEN_BASE_URL/v1/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.
Responses
Every successful response includes usage, and the amount you were charged is visible in the console under Logs by request ID (the x-oceantoken-request-id header). Errors are described in Errors.