---
title: "AI SDK OpenAI-compatible provider: uncensored models"
canonical: https://uncensoredllm.ai/guides/ai-sdk
generated: 2026-11-09T16:02:06.669Z
source: https://uncensoredllm.ai/guides/ai-sdk
base_url: https://api.uncensoredllm.ai/v1
---

# The AI SDK with an uncensored model

createOpenAICompatible with our base URL, then generateText, streamText and tool() on an uncensored build: which builds take tool calls, the usage fields, a Next.js route handler, the error codes, and what one call costs at the live price.

Steps checked November 2, 2026 · prices as of November 9, 2026

The AI SDK’s OpenAI-compatible provider reaches every model on this catalog with one `createOpenAICompatible` call. This page has the provider, `generateText`, `streamText` and `tool()` on an uncensored build, which builds take tool calls, a Next.js route handler, the error codes, and what one call costs at the live price.

## Install

Two packages: `ai`, the SDK, and `@ai-sdk/openai-compatible`, the provider for any endpoint that speaks the OpenAI Chat Completions format. `zod` is for the tool example further down.

```bash
pnpm add ai @ai-sdk/openai-compatible zod
# or
npm install ai @ai-sdk/openai-compatible zod
```

The key comes from the [console](https://app.uncensoredllm.ai/console), starts with `sk-ullm-`, is shown once and spends a prepaid balance that starts at $10. Put it in an environment variable; the code never holds it.

```bash
export UNCENSOREDLLM_API_KEY="sk-ullm-live-…"
```

## The provider

`createOpenAICompatible` takes a `name`, our `baseURL` and the `apiKey`. The base URL ends in `/v1`; the SDK adds `/chat/completions`. `includeUsage` puts the token counts on streamed answers as well, which the cost table below depends on.

```javascript
import { createOpenAICompatible } from '@ai-sdk/openai-compatible';

export const uncensored = createOpenAICompatible({
  name: 'uncensoredllm',
  baseURL: 'https://api.uncensoredllm.ai/v1',
  apiKey: process.env.UNCENSOREDLLM_API_KEY,
  includeUsage: true, // usage in streaming responses too
});
```

- name: uncensoredllm any label; it names the provider in providerOptions
- baseURL: https://api.uncensoredllm.ai/v1 ends in /v1; nothing after it
- apiKey: process.env.UNCENSOREDLLM_API_KEY sent as Authorization: Bearer
- model: uncensored('glm-5.3-uncensored') any id from the models page; provider.chatModel(id) is the same call

Anything else that speaks this format, the [OpenAI SDKs](https://uncensoredllm.ai/guides/sdks) included, uses the same base URL and key.

## generateText

`provider('model-id')` is a chat model; pass it as `model`. The result carries `text`, `finishReason` and `usage` with `inputTokens`, `outputTokens` and `totalTokens`: the numbers the request was billed on.

```javascript
import { generateText } from 'ai';
import { uncensored } from './provider';

const { text, usage, finishReason } = await generateText({
  model: uncensored('glm-5.3-uncensored'),
  prompt: 'Explain how a lock pick works, step by step.',
});

console.log(text);
console.log(usage.inputTokens, usage.outputTokens, usage.totalTokens, finishReason);
```

## streamText

`streamText` returns at once; `textStream` yields the pieces as the model writes them, and `result.usage` is a promise that resolves when the stream ends.

```javascript
import { streamText } from 'ai';
import { uncensored } from './provider';

const result = streamText({
  model: uncensored('glm-5.3-flash-uncensored'),
  prompt: "Write a villain's monologue.",
});

for await (const piece of result.textStream) process.stdout.write(piece);

const usage = await result.usage; // resolves when the stream ends; needs includeUsage on the provider
console.log(usage.inputTokens, usage.outputTokens);
```

In a Next.js route handler, return `result.toTextStreamResponse()` for plain text or `result.toUIMessageStreamResponse()` for the `useChat` hook.

```javascript
// app/api/chat/route.ts
import { streamText } from 'ai';
import { uncensored } from '@/lib/provider';

export async function POST(req: Request) {
  const { prompt } = (await req.json()) as { prompt: string };
  const result = streamText({
    model: uncensored('glm-5.3-uncensored'),
    prompt,
  });
  return result.toTextStreamResponse();
}
```

## Tool calls

`tool()` takes a description, an `inputSchema` and an `execute` function; `stopWhen: isStepCount(5)` lets the SDK run the tool and send the result back for up to five steps. `steps` holds the tool calls and results of each one.

```javascript
import { generateText, isStepCount, tool } from 'ai';
import { z } from 'zod';
import { uncensored } from './provider';

const { text, steps } = await generateText({
  model: uncensored('glm-5.3-uncensored'),
  tools: {
    weather: tool({
      description: 'Get the weather in a location',
      inputSchema: z.object({
        location: z.string().describe('The location to get the weather for'),
      }),
      execute: async ({ location }) => ({ location, temperature: 72 }),
    }),
  },
  stopWhen: isStepCount(5), // keep calling tools for up to 5 steps
  prompt: 'What is the weather in San Francisco?',
});

const toolCalls = steps.flatMap((step) => step.toolCalls);
console.log(toolCalls.length, text);
```

A tool call needs a build whose base model was trained for it. The catalog publishes `tools` among a build’s supported parameters once every route of it takes tool calls; 42 listed builds publish it on 2026-11-09, and the [agent tools](https://uncensoredllm.ai/features/agent-tools) page tables them all. The builds on this page do not publish the field on 2026-11-09; pick a build from that table for tool work and keep these for text.

- nemotron-3-ultra-550b-a55b-uncensored: takes tool calls Nemotron 3 Ultra 550B Uncensored
- abliterated-model-large-flash: takes tool calls Abliterated Large Flash
- abliterated-model-large: takes tool calls Abliterated Model Large
- abliterated-model-large-v2: takes tool calls Abliterated Model Large V2

The gateway also runs tools of its own: web search, web fetch, a remote MCP server and a code sandbox, declared in the request’s `tools` array and run inside the one request, on the [agent tools](https://uncensoredllm.ai/features/agent-tools) page.

## What a call costs

One chat call of 2,000 input and 400 output tokens, the shape of a short conversation with a system prompt, on four builds at the catalog prices on 2026-11-09:

| Model | One call | 1,000 calls |
| --- | --- | --- |
| [GLM-5.3 Uncensored](https://uncensoredllm.ai/models/glm-5.3-uncensored) | $0.0009 | $0.90 |
| [GLM-5.3 Abliterated](https://uncensoredllm.ai/models/glm-5.3-abliterated) | $0.0061 | $6.11 |
| [DeepSeek V4.1 Flash Abliterated](https://uncensoredllm.ai/models/deepseek-v4.1-flash-abliterated) | $0.0024 | $2.38 |
| [GLM-5.3 Flash Uncensored](https://uncensoredllm.ai/models/glm-5.3-flash-uncensored) | $0.00036 | $0.36 |

`usage.inputTokens` and `usage.outputTokens` on each result are the counts the charge was computed from; multiply by the per-million prices below to reconcile a bill.

| Model | Build | Input $/M | Output $/M |
| --- | --- | --- | --- |
| [GLM-5.3 Uncensored](https://uncensoredllm.ai/models/glm-5.3-uncensored) glm-5.3-uncensored | Uncensored | $0.282 | $0.846 |
| [GLM-5.3 Abliterated](https://uncensoredllm.ai/models/glm-5.3-abliterated) glm-5.3-abliterated | Abliterated | $2.1825 | $4.365 |
| [DeepSeek V4.1 Flash Abliterated](https://uncensoredllm.ai/models/deepseek-v4.1-flash-abliterated) deepseek-v4.1-flash-abliterated | Abliterated | $0.8488 | $1.6975 |
| [GLM-5.3 Flash Uncensored](https://uncensoredllm.ai/models/glm-5.3-flash-uncensored) glm-5.3-flash-uncensored | Uncensored | $0.1053 | $0.3685 |

Prices per million tokens, read from the live catalog on 2026-11-09.

## Check it

The list call is free and proves the key and base URL; it returns exactly the ids the key can call.

```bash
curl https://api.uncensoredllm.ai/v1/models -H "Authorization: Bearer sk-ullm-live-…"
```

Every answer carries `x-uplink-request-id`; a non-streamed answer carries `x-uplink-charge`, what it cost in USD. The console’s usage page lists both, and the [docs page](https://uncensoredllm.ai/docs) has the rest of the API.

## Errors and fixes

- `401 invalid_api_key`: the key is wrong or revoked. Copy it again from the console and check the variable name the provider reads.
- `402 insufficient_balance`: the balance does not cover the request. Top up in the console. `402 monthly_limit_reached` means the key hit the monthly spend limit you set on it.
- `403 scope_denied`: the key is limited to certain models and this is not one of them.
- `404 model_not_found`: the id in `uncensored(‘…’)` is not on the [models page](https://uncensoredllm.ai/models). Check the spelling; display names are not ids.
- `429 rate_limited`: the key hit its rate limit. The response carries `retry-after`.
- A request that fails before the model answers is not charged.

## Questions

### Responses API or Chat Completions?

@ai-sdk/openai-compatible sends Chat Completions, which every listed build answers. The catalog also lists /v1/responses on the builds on this page, so the @ai-sdk/openai package, whose default openai(id) uses the Responses API, reaches them too; openai.chat(id) keeps it on Chat Completions.

### @ai-sdk/openai with a baseURL, or @ai-sdk/openai-compatible?

Both reach the models. The compatible provider is written for endpoints like this one: Chat Completions by default, includeUsage for streamed usage, and providerOptions for extra request fields. @ai-sdk/openai works through its .chat() factory. See the OpenAI SDK guide.

### Does this work in OpenCode?

Yes. OpenCode runs on the AI SDK and takes a custom OpenAI-compatible provider in its config; the same base URL, key and model ids apply. See the OpenCode guide.

### How do I stream from a Next.js route handler?

Call streamText in the POST handler and return result.toTextStreamResponse() for plain text, or result.toUIMessageStreamResponse() when the page uses useChat. The route example above is the whole handler.

Every model above is on the [uncensored model list](https://uncensoredllm.ai/models) with its live price per million tokens. [Per-token pricing](https://uncensoredllm.ai/pricing) sets the whole catalog side by side, and the [family directory](https://uncensoredllm.ai/uncensored) groups the builds by base model.

[Get a key](https://app.uncensoredllm.ai/console) Prepaid from $10, no subscription.

## New models and price changes, once a month.

New uncensored builds with their prices, price moves, and what the daily model check found.
