Skip to main content

Text generation

Every text model answers on three endpoints. Pick the one your code or SDK already speaks; the models, prices and answers are the same.

EndpointRequest shapeUse it when
/v1/chat/completionsOpenAI Chat CompletionsMost SDKs, frameworks and tools. The default.
/v1/responsesOpenAI ResponsesYour code already uses the Responses API.
/v1/messagesAnthropic MessagesYour code uses the Anthropic SDK, or a tool such as Claude Code. Works for every chat model, not only Claude.

The complete programs on this page are files we run against the service before publishing. Set OCEANTOKEN_API_KEY and they run as-is.

Chat completions​

# pip install openai
import os
from openai import OpenAI

client = OpenAI(
base_url=os.getenv("OCEANTOKEN_BASE_URL", "https://api.oceantoken.ai/v1"),
api_key=os.environ["OCEANTOKEN_API_KEY"],
)

response = client.chat.completions.create(
model="deepseek/deepseek-v4.1-flash-1017",
messages=[{"role": "user", "content": "Say hello in one short sentence."}],
)
print(response.choices[0].message.content)

A conversation is the list in messages, oldest first: an optional system message with your instructions, then user and assistant turns. The API keeps nothing between requests, so send the whole conversation every time, with the model's last answer added as an assistant message.

messages = [
{"role": "system", "content": "You are a concise travel assistant."},
{"role": "user", "content": "Two days in Porto: what should I see?"},
{"role": "assistant", "content": "Ribeira, the Livraria Lello, a port cellar in Gaia..."},
{"role": "user", "content": "Which of those is best on a rainy day?"},
]

The answer is in choices[0].message.content. finish_reason says why it stopped: stop for a complete answer, length when it reached max_tokens, tool_calls when the model wants to call a tool.

Stream the answer​

Set stream and read the answer as it is written:

# Streaming: tokens arrive as they are generated. Use this for anything a person
# is waiting on, and for long answers, which would otherwise hit the request timeout.
import os
from openai import OpenAI

client = OpenAI(
base_url=os.getenv("OCEANTOKEN_BASE_URL", "https://api.oceantoken.ai/v1"),
api_key=os.environ["OCEANTOKEN_API_KEY"],
)

with client.chat.completions.create(
model="deepseek/deepseek-v4.1-flash-1017",
messages=[{"role": "user", "content": "Count from one to five, one number per line."}],
stream=True,
) as stream:
for chunk in stream:
delta = chunk.choices[0].delta.content if chunk.choices else None
if delta:
print(delta, end="", flush=True)
print()

Stream anything a person is waiting on, and every long answer: a request that is not streamed is closed after 240 seconds. To get the token counts at the end of a chat-completions stream, send "stream_options": {"include_usage": true}. The event format and errors during a stream are in API reference → Streaming.

Responses API​

# pip install openai
# The Responses API takes the same models as chat completions.
import os
from openai import OpenAI

client = OpenAI(
base_url=os.getenv("OCEANTOKEN_BASE_URL", "https://api.oceantoken.ai/v1"),
api_key=os.environ["OCEANTOKEN_API_KEY"],
)

response = client.responses.create(
model="openai/gpt-5-mini",
instructions="Answer in one short sentence.",
input="Why is the sea salty?",
)
print(response.output_text)

output_text is the answer. Stream with stream=True; the stream ends with a response.completed event.

Anthropic Messages API​

# pip install anthropic
# Claude models are served on /v1/messages. The Anthropic SDK works unchanged:
# give it the OceanToken host and your OceanToken key. It adds /v1 itself, so it
# takes the base URL without the /v1 that OpenAI SDKs need.
import os
import anthropic

client = anthropic.Anthropic(
base_url=os.getenv("OCEANTOKEN_BASE_URL", "https://api.oceantoken.ai/v1").removesuffix("/v1"),
api_key=os.environ["OCEANTOKEN_API_KEY"],
)

message = client.messages.create(
model="anthropic/claude-haiku-4.5",
max_tokens=64,
messages=[{"role": "user", "content": "Say hello in one short sentence."}],
)
print(message.content[0].text)

The Anthropic SDK takes https://api.oceantoken.ai, without /v1, and adds it itself. Change model to use GPT, Gemini, DeepSeek or any other chat model with the same code.

Parameters​

The usual parameters of each shape work: max_tokens, temperature, top_p, stop, n, seed, tools, tool_choice, response_format and the rest. A few things to know:

  • A parameter the model does not support is dropped, and the request runs without it. A successful answer therefore does not prove a parameter took effect; see Which API shape.
  • max_tokens above the model's own output limit is lowered to that limit, rather than refused. The limit is on the model's page under Max output.
  • The model's page on Models lists what it takes under Capabilities: tool calling, image input, reasoning, web search.

Reasoning​

Models whose page lists reasoning think before they answer. On chat completions, reasoning_effort sets how much: low, medium or high. More effort means more output tokens, so a higher cost and a slower answer.

response = client.chat.completions.create(
model="openai/gpt-5-mini",
messages=[{"role": "user", "content": "Is 1,001 a prime number? Answer yes or no."}],
reasoning_effort="low",
)

Reasoning tokens are billed as output. Models that return their reasoning put it in choices[0].message.reasoning_content; the answer is still in content. Reserve room for both: a max_tokens that is too small can be used up by the reasoning before any answer is written.

Structured output​

To get JSON that matches a schema, send it in response_format:

# pip install openai
# Structured output: the answer is JSON that matches your schema.
import json
import os
from openai import OpenAI

client = OpenAI(
base_url=os.getenv("OCEANTOKEN_BASE_URL", "https://api.oceantoken.ai/v1"),
api_key=os.environ["OCEANTOKEN_API_KEY"],
)

schema = {
"type": "object",
"properties": {
"name": {"type": "string"},
"date": {"type": "string", "description": "YYYY-MM-DD"},
"attendees": {"type": "array", "items": {"type": "string"}},
},
"required": ["name", "date", "attendees"],
"additionalProperties": False,
}

response = client.chat.completions.create(
model="openai/gpt-5-mini",
messages=[{"role": "user", "content": "Alice and Bob are going to a science fair on 2026-11-14."}],
response_format={"type": "json_schema", "json_schema": {"name": "event", "schema": schema, "strict": True}},
)
event = json.loads(response.choices[0].message.content)
print(event["name"], event["date"], event["attendees"])

{"type": "json_object"} asks for any valid JSON without a schema. Not every model honours a schema: a model that does not drops response_format and answers in prose, so parse the answer and handle a failure, and test the model you will use.

Let OceanToken pick the model​

smart-router is a model name that picks a text model for each request. The request is billed at the price of the model that answered, with nothing added. The answer's model field and the x-oceantoken-model response header name that model.

What it cost​

A non-streamed response carries its cost in the x-oceantoken-cost header. For a stream, look it up afterwards by the response id: see What a request cost. Long prompts that repeat, such as instructions or a document, cost less with prompt caching.

Next​