Use OceanToken with the Vercel AI SDK
OceanToken's API is OpenAI-compatible, so the AI SDK's OpenAI Compatible provider, @ai-sdk/openai-compatible, works with a base URL and your key. There is no OceanToken package to install. Claude models also work with @ai-sdk/anthropic, the AI SDK's Anthropic provider (below).
Every sample on this page is a file we run against the service before publishing. They were tested on 2026-10-19 with ai 7.0.142, @ai-sdk/openai-compatible 3.0.70, @ai-sdk/anthropic 4.0.81 and zod 4.6.7.
Install
npm install ai @ai-sdk/openai-compatible zod
export OCEANTOKEN_API_KEY=sk-...
Create an API key in the console and give it a Credit limit if you want a cap on what this app can spend. Use the key on the server only: in a route handler, a server action or a backend job, never in code that runs in the browser.
Create the provider
Create the provider once and import it wherever you call a model:
// npm install ai @ai-sdk/openai-compatible
import { createOpenAICompatible } from "@ai-sdk/openai-compatible";
export const oceantoken = createOpenAICompatible({
name: "oceantoken",
baseURL: process.env.OCEANTOKEN_BASE_URL ?? "https://api.oceantoken.ai/v1",
apiKey: process.env.OCEANTOKEN_API_KEY,
// Token counts in streamed responses, as in non-streamed ones.
includeUsage: true,
// Send your schema with structured-output requests (Output.object).
supportsStructuredOutputs: true,
});
baseURLishttps://api.oceantoken.ai/v1. The samples readOCEANTOKEN_BASE_URLfirst only so that we can run them against a test environment; you can leave it out.apiKeyis sent asAuthorization: Bearer <key>.includeUsage: trueadds token counts to streamed responses.supportsStructuredOutputs: truesends your schema with structured output requests. Without it the SDK asks only for JSON, with no schema.
Pass a model ID exactly as the Models page writes it, with the publisher in front: openai/gpt-5.5, anthropic/claude-sonnet-5.5, deepseek/deepseek-v4.1-flash, google/gemini-3.5-flash. GET https://api.oceantoken.ai/v1/models with your key lists every model the key can use, and the full catalog with prices is also published as JSON at https://api.oceantoken.ai/public/model_hub.
Generate text
import { generateText } from "ai";
import { oceantoken } from "./oceantoken";
const { text, usage } = await generateText({
model: oceantoken("openai/gpt-5.5"),
prompt: "Explain what an API gateway does in one sentence.",
});
console.log(text);
console.log(usage.inputTokens, "input tokens,", usage.outputTokens, "output tokens");
usage has the token counts the request was billed for. The same request appears in the console under Logs with its cost.
Stream text
import { streamText } from "ai";
import { oceantoken } from "./oceantoken";
const result = streamText({
model: oceantoken("deepseek/deepseek-v4.1-flash"),
prompt: "Write a four-line poem about a lighthouse.",
});
for await (const chunk of result.textStream) {
process.stdout.write(chunk);
}
const usage = await result.usage; // needs includeUsage: true
console.log("\n", usage.inputTokens, "input tokens,", usage.outputTokens, "output tokens");
Stream anything a person is waiting on: tokens arrive as they are generated, and long answers do not run into the request timeout described in Limits.
Call tools
// npm install zod
import { generateText, stepCountIs, tool } from "ai";
import { z } from "zod";
import { oceantoken } from "./oceantoken";
const { text, steps } = await generateText({
model: oceantoken("google/gemini-3.5-flash"),
tools: {
weather: tool({
description: "Get the current weather in a city",
inputSchema: z.object({ city: z.string() }),
execute: async ({ city }) => ({ city, temperatureC: 18, sky: "clear" }),
}),
},
// Let the model call the tool, read the result, then answer.
stopWhen: stepCountIs(5),
prompt: "What is the weather in Lisbon right now?",
});
console.log(text);
console.log(steps.flatMap((step) => step.toolCalls.map((call) => call.toolName)));
The model asks for weather, the SDK runs execute and sends the result back, and the model answers with it. stopWhen: stepCountIs(5) allows up to five such steps; without it, generation stops after the first tool call. Tool calling also works with streamText. Use a model whose page on Models lists tool calling; the four models named above were tested with both generateText and streamText.
Structured output
import { generateText, Output } from "ai";
import { z } from "zod";
import { oceantoken } from "./oceantoken";
const { output } = await generateText({
model: oceantoken("openai/gpt-5.5"),
output: Output.object({
schema: z.object({
name: z.string(),
ingredients: z.array(z.object({ item: z.string(), amount: z.string() })),
minutes: z.number(),
}),
}),
prompt: "A simple recipe for pancakes.",
});
console.log(output.name, output.minutes, output.ingredients.length);
The SDK validates the answer against your schema, and output is typed from it. This needs supportsStructuredOutputs: true on the provider. In AI SDK 7, generateObject is deprecated in favour of generateText with output; it still works with the same provider.
Embeddings
import { cosineSimilarity, embedMany } from "ai";
import { oceantoken } from "./oceantoken";
const { embeddings, usage } = await embedMany({
model: oceantoken.embeddingModel("openai/text-embedding-3-small"),
values: [
"The lighthouse keeper rowed out before dawn.",
"At first light the keeper took the boat out.",
"Quarterly revenue rose by four percent.",
],
});
console.log(embeddings[0].length, usage);
console.log(cosineSimilarity(embeddings[0], embeddings[1])); // close in meaning
console.log(cosineSimilarity(embeddings[0], embeddings[2])); // unrelated
Use oceantoken.embeddingModel() with any embedding model from the Models page, for example openai/text-embedding-3-small or baai/bge-m3. embed takes one value and embedMany several.
Generate images
import { randomUUID } from "node:crypto";
import { writeFile } from "node:fs/promises";
import { generateImage } from "ai";
import { oceantoken } from "./oceantoken";
const { image } = await generateImage({
model: oceantoken.imageModel("openai/gpt-image-2"),
prompt: "A small lighthouse on a calm sea at dawn, watercolor",
size: "1024x1024",
// A retry with the same key, including the SDK's own retries, gets the
// first result back instead of running (and charging) a second time.
headers: { "Idempotency-Key": randomUUID() },
});
await writeFile("lighthouse.png", image.uint8Array);
console.log(image.mediaType, image.uint8Array.length, "bytes");
Use oceantoken.imageModel() with any image model from the Models page. The SDK downloads the result, so image holds the file itself. Image requests are charged when they run, and the SDK retries failed requests twice by default; the Idempotency-Key header makes a retry return the first result instead of running again (Errors).
Claude with @ai-sdk/anthropic
Claude models work through the OpenAI-compatible provider above like any other model. To use the AI SDK's Anthropic provider instead, give it the same base URL:
npm install ai @ai-sdk/anthropic
// npm install ai @ai-sdk/anthropic
// Claude models also answer on the Anthropic Messages API. Unlike the
// Anthropic SDKs, this package's baseURL includes /v1.
import { createAnthropic } from "@ai-sdk/anthropic";
import { generateText } from "ai";
const anthropic = createAnthropic({
baseURL: process.env.OCEANTOKEN_BASE_URL ?? "https://api.oceantoken.ai/v1",
apiKey: process.env.OCEANTOKEN_API_KEY,
});
const { text, usage } = await generateText({
model: anthropic("anthropic/claude-sonnet-5.5"),
prompt: "Explain what an API gateway does in one sentence.",
});
console.log(text);
console.log(usage.inputTokens, "input tokens,", usage.outputTokens, "output tokens");
@ai-sdk/anthropic takes https://api.oceantoken.ai/v1, with /v1. The Anthropic SDKs for Python and TypeScript take https://api.oceantoken.ai and add /v1 themselves (Quickstart).
Text, streaming and tool calling work as above. For structured output, set the provider's JSON-tool mode:
const { output } = await generateText({
model: anthropic("anthropic/claude-sonnet-5.5"),
output: Output.object({ schema }),
providerOptions: { anthropic: { structuredOutputMode: "jsonTool" } },
prompt: "...",
});
Without it, @ai-sdk/anthropic sends the schema as output_config.format, which OceanToken does not apply yet: the model answers in prose and the SDK reports that no object was generated. The OpenAI-compatible provider needs no such setting for Claude.
Troubleshooting
| What you see | What to do |
|---|---|
Invalid API key. Check the key, or create one in the OceanToken console. | The key is wrong, revoked or has a stray space. Check OCEANTOKEN_API_KEY, or create a new key. |
Unknown model '…'. GET /v1/models lists the models your key can use. | Use the model ID exactly as the Models page writes it, with the publisher in front (deepseek/deepseek-v4.1-flash, not deepseek-v4.1-flash). |
Insufficient balance - please add credit to continue. | Add credits. |
No object generated with Output.object | On createOpenAICompatible, set supportsStructuredOutputs: true. With @ai-sdk/anthropic, set structuredOutputMode: "jsonTool" (above). |
usage is empty after streamText | Set includeUsage: true on the provider. |
The SDK raises an APICallError whose statusCode and responseBody carry OceanToken's error; Errors lists every code and what to do about it.
Resources
- Models: every model ID with its context window and capabilities, and a link to its price
- API reference: every endpoint
- Create an API key
- Errors and Limits
- AI SDK documentation and its OpenAI Compatible provider