Vercel AI SDK integration

Vercel AI SDK with Plan2API: createOpenAICompatible in TypeScript

The Vercel AI SDK 7 reaches Plan2API through @ai-sdk/openai-compatible: 3 settings in createOpenAICompatible (name, baseURL, apiKey) give generateText, streamText and tools access to Claude, Gemini, GPT and Kimi models through one /v1/chat/completions endpoint.

Package
@ai-sdk/openai-compatible
Factory
createOpenAICompatible
baseURL
https://api.plan2api.com/v1
Endpoint used
/v1/chat/completions

Updated

AI SDK settings for Plan2API

One createOpenAICompatible provider instance covers every Plan2API model you have connected. The table below lists the exact settings to pass when creating a Vercel AI SDK custom provider.

createOpenAICompatible settings for Plan2API
SettingValue
Package@ai-sdk/openai-compatible
name'plan2api' (also the providerOptions key)
baseURLhttps://api.plan2api.com/v1
apiKeyprocess.env.PLAN2API_KEY
Modelplan2api('claude/claude-sonnet-5')
Endpoint/v1/chat/completions

Set up the provider

  1. Sign up with a Google account, connect your Claude, ChatGPT, Google AI or Kimi subscription, and copy the p2a_ API key from the dashboard.
  2. Install the core SDK, the OpenAI-compatible provider package and Zod with npm install ai @ai-sdk/openai-compatible zod (ai 7.0.157 and @ai-sdk/openai-compatible 3.0.81 as of October 2026).
  3. Export your key in the environment as PLAN2API_KEY.
  4. Create the provider with createOpenAICompatible, setting name to 'plan2api', baseURL to 'https://api.plan2api.com/v1' and apiKey to process.env.PLAN2API_KEY.
  5. Run the script as an ES module (for example, by setting "type": "module" in package.json), since the code uses top-level await.

The following example calls Claude with generateText, streams Gemini with streamText, and runs a Kimi tool call.

TypeScript
import { createOpenAICompatible } from '@ai-sdk/openai-compatible';
import { generateText, isStepCount, streamText, tool } from 'ai';
import { z } from 'zod';

const plan2api = createOpenAICompatible({
  name: 'plan2api',
  baseURL: 'https://api.plan2api.com/v1',
  apiKey: process.env.PLAN2API_KEY,
});

const { text } = await generateText({
  model: plan2api('claude/claude-sonnet-5'),
  prompt: 'Explain HTTP 429 in one sentence.',
  providerOptions: { plan2api: { reasoningEffort: 'high' } },
});
console.log(text);

const result = streamText({
  model: plan2api('gemini/gemini-3.8-flash-high'),
  prompt: 'Write a haiku about deploy previews.',
});
for await (const part of result.textStream) {
  process.stdout.write(part);
}
console.log();

const { text: answer } = await generateText({
  model: plan2api('kimi/k3'),
  tools: {
    getWeather: tool({
      description: 'Get the current weather for a city',
      inputSchema: z.object({ city: z.string() }),
      execute: async ({ city }) => ({ city, forecast: 'sunny' }),
    }),
  },
  stopWhen: isStepCount(3),
  prompt: 'What is the weather in Berlin?',
});
console.log(answer);

Pick a model id

Pass the full model id straight into plan2api('model-id'), slashes included, and the SDK sends that exact string to /v1/chat/completions. The prefix before the slash picks the upstream provider: claude/ (or anthropic/) for Claude models like Claude Sonnet 5 (claude/claude-sonnet-5), openai/ (or codex/) for ChatGPT Codex models such as openai/gpt-6.1-sol, gemini/ (or google/ and antigravity/) for Gemini models like Gemini 3.8 Flash (gemini/gemini-3.8-flash-high), and kimi/ (or moonshotai/) for kimi/k3. GET /v1/models lists every id Plan2API serves, including providers you haven't connected, and Codex ids run on your own ChatGPT plan, so pick one your plan includes. The table below lists the main ids with a note on each.

Plan2API model ids for the AI SDK
Model idSubscriptionNote
claude/claude-sonnet-5Claude Pro or Max1M context, no JSON response_format
claude/claude-opus-5-5Claude Pro or Max1M context, up to 128K output
gemini/gemini-3.8-flash-highGoogle AI Pro or Ultra1,048,576-token context
openai/gpt-6.1-solChatGPT (Codex)Runs on your ChatGPT plan's Codex allowance
kimi/k3Kimi CodeReasoning effort low, high or max

Streaming, tools and reasoning effort

  • streamText({ model, prompt }) returns right away, and result.textStream yields text parts as an async iterable.
  • Define tools with tool({ description, inputSchema, execute }) using a zod schema for inputSchema (such as z.object({ city: z.string() })), which works across all four providers.
  • Pass stopWhen: isStepCount(n) so the model can keep going and answer after receiving tool results (stepCountIs is the deprecated name).
  • Set providerOptions: { plan2api: { reasoningEffort: 'high' } } to send reasoning_effort in the request body; the key under providerOptions matches the camelCase provider name. On kimi/k3 the accepted values are low, high and max, and high is the default.
  • Pass includeUsage: true in createOpenAICompatible to add stream_options include_usage to streaming requests (the default is false).
  • temperature and topP call settings are passed through on Gemini, fixed on Kimi (temperature 1, top_p 0.95) and ignored on Claude and Codex models.

Use @ai-sdk/openai-compatible, not @ai-sdk/openai

In the @ai-sdk/openai package, the default openai('id') call from createOpenAI targets the OpenAI Responses API (/v1/responses), which Plan2API doesn't serve, even when you configure a custom baseURL. Calling openai.chat('id') switches @ai-sdk/openai back to Chat Completions.

The @ai-sdk/openai-compatible package only calls /chat/completions for chat models (provider('id'), provider.languageModel('id') and provider.chatModel('id')), so it's the simpler fit for an OpenAI-compatible setup in the Vercel AI SDK.

openai('id') from @ai-sdk/openai posts to /v1/responses, which Plan2API doesn't serve, so use createOpenAICompatible or openai.chat('id') instead.

What works and what doesn't

These AI SDK features work through the /v1/chat/completions endpoint:

  • Text generation and streaming with generateText and streamText.
  • Tool and function calling on Claude, Codex, Gemini and Kimi models.
  • Image input on Claude, Gemini and Kimi models, plus Codex models that accept images (up to 32 image URLs per request, up to 20 MB and 15 seconds each).
  • Structured JSON output on Gemini, Codex and Kimi models; setting supportsStructuredOutputs: true on the provider sends a response_format of json_schema instead of json_object.
  • Direct calls from browser code because the API sends Access-Control-Allow-Origin: * CORS headers, though any p2a_ key included in client-side JavaScript is visible to anyone inspecting the page (read the API key security notes before shipping a browser app).

These features aren't supported:

  • Legacy text completions through provider.completionModel(), which posts to /completions.
  • Embeddings through provider.embeddingModel(), which posts to /embeddings.
  • Structured output on Claude models, which rejects any non-text response_format with HTTP 400 unsupported_parameter.

Troubleshooting the AI SDK with Plan2API

  • 401 invalid_api_key: PLAN2API_KEY isn't set in the environment where the code runs, the key doesn't start with p2a_, or it was sent in an x-api-key header instead of Authorization: Bearer.
  • 403 no_credentials: your vault has no connected subscription for that model's provider, so connect an account in the dashboard first.
  • 400 model_not_found: the model id isn't recognized; pass a full prefixed id listed by GET /v1/models.
  • 400 unsupported_parameter: you requested structured JSON output (a non-text response_format) on a Claude model; use a Gemini, Codex or Kimi model for JSON schemas, or ask Claude for JSON in the prompt.
  • Requests failing at /v1/responses: the code is calling openai('id') from @ai-sdk/openai; switch to createOpenAICompatible from @ai-sdk/openai-compatible or call openai.chat('id').
  • 429 daily_requests: you hit the 51st chat request of the day on the Free plan (resets at 00:00 UTC), and each step of a stopWhen tool loop is a separate request; wait for the Retry-After window or check Plan2API plan limits and pricing to upgrade to Pro or Scale, which have no daily request cap.

Once streaming has started, any error arrives as a final server-sent event instead of an HTTP status code, so make sure your code handles stream errors too.

Use the AI subscriptions you already pay for as one OpenAI-compatible API key.

  • Free: 1 subscription, 50 requests a day, no card
  • Pro: $12 a month

FAQ

How do I use an OpenAI-compatible API with the Vercel AI SDK?

Install @ai-sdk/openai-compatible, create a provider with createOpenAICompatible({ name, baseURL, apiKey }), and pass provider('model-id') to generateText or streamText. The provider sends your apiKey in the Authorization: Bearer header and posts chat requests to {baseURL}/chat/completions.

Why does @ai-sdk/openai fail with Plan2API?

Calling openai('id') in @ai-sdk/openai uses the OpenAI Responses API (/v1/responses), and Plan2API serves Chat Completions (/v1/chat/completions) only. Use createOpenAICompatible from @ai-sdk/openai-compatible instead, or call openai.chat('id').

Can I use my Claude Pro or Max plan in the AI SDK?

Yes, you can connect a Claude Pro or Max subscription to Plan2API and pass claude/ model ids such as claude/claude-sonnet-5 or claude/claude-opus-5-5 to the compatible provider. That lets you run Claude in the AI SDK through /v1/chat/completions without paying per token.

Does structured output work through Plan2API?

Yes, on Gemini, Codex and Kimi models, which accept json_object and json_schema (when supportsStructuredOutputs: true is set on the provider). Claude models don't support it and return HTTP 400 unsupported_parameter for any non-text response_format.

Do embeddings work with the AI SDK and Plan2API?

No. Calling provider.embeddingModel() sends requests to /embeddings, and Plan2API doesn't serve an embeddings endpoint. Use a separate embeddings provider for vectors and keep the Plan2API provider for chat models.

Sources

  1. AI SDK OpenAI-compatible providersai-sdk.dev
  2. AI SDK generating textai-sdk.dev
  3. AI SDK tool callingai-sdk.dev
  4. openai-compatible chat model (GitHub)github.com
  5. @ai-sdk/openai provider (GitHub)github.com
  6. AI SDK stop condition exports, stepCountIs deprecated (GitHub)github.com
  7. ai on npmnpmjs.com
  8. @ai-sdk/openai-compatible on npmnpmjs.com