EN ▾
Get API key

Chat OpenAI in LangChain with an Uncensored API

LangChain's ChatOpenAI class works with any OpenAI-compatible endpoint, so you only change the base URL, the key and the model name. This page shows the setup in Python and Node, then builds up to streaming, tools and a small agent.

Updated

Isometric chain of linked rings feeding chat bubbles into a server cube, representing a LangChain connection to an API

Key points

  1. ChatOpenAI needs three settings: base_url, api_key and model set to uncensored.
  2. Streaming, tool calling and JSON mode all pass through the standard LangChain interfaces.
  3. Cap concurrency at 8 and handle HTTP 429 with retries to stay inside the limits.
  4. A ten-step agent run costs well under a cent at $0.15 and $0.50 per 1M tokens.

The short answer: three settings on ChatOpenAI

If you searched for chat open ai langchain, you probably have a LangChain project that already uses ChatOpenAI and you want it to talk to a different backend. That is a configuration change, not a rewrite. The API on this site speaks the OpenAI chat completions protocol, so LangChain treats it like any other OpenAI-style server.

You need the base URL, your API key and the model ID uncensored. Install the packages first:

pip install -U langchain-openai langgraph

Then create the model object:

import os
from langchain_openai import ChatOpenAI

llm = ChatOpenAI(
    model="uncensored",
    base_url="https://api.nofilteraichat.com/v1",
    api_key=os.environ["NOFILTER_API_KEY"],
    temperature=0.8,
)

reply = llm.invoke("Write a two-sentence villain monologue for a heist novel.")
print(reply.content)

That is the whole integration. Every LangChain feature built on ChatOpenAI now runs against the uncensored model: chains, retrievers that call the chat model, output parsers and agents. If you do not have a key yet, the API key page explains sign-up and the one-click trial credit.

Keep the key out of your code

Put the key in an environment variable and read it at start-up. On Linux or macOS:

export NOFILTER_API_KEY="paste-your-key-here"

Create one shared client module for the project, for example llm.py that builds the model once and exports it. Every chain then imports the same object, and a later change of key, timeout or temperature happens in a single place. This also makes it easy to keep a second, colder instance for tool steps and a warmer one for creative writing, both pointing at the same endpoint with different settings.

One pitfall catches many people: ChatOpenAI also reads OPENAI_API_KEY and OPENAI_API_BASE from the environment. If those variables are set for another project, a half-configured object may send your request to the wrong server. Passing base_url and api_key explicitly, as above, removes the ambiguity.

You can confirm the connection without LangChain at all. The models endpoint should return a single entry:

curl https://api.nofilteraichat.com/v1/models \
  -H "Authorization: Bearer $NOFILTER_API_KEY"

If that works but LangChain fails, the problem is in your Python environment, not the key. If both fail with 401, check for a stray space or newline in the key. The quickstart lists the exact request format for comparison.

Streaming and token usage

Chat UIs feel better when tokens appear as they are generated. LangChain exposes that through .stream(), and the server sends standard SSE chunks when stream: true is set under the hood.

from langchain_openai import ChatOpenAI
import os

llm = ChatOpenAI(
    model="uncensored",
    base_url="https://api.nofilteraichat.com/v1",
    api_key=os.environ["NOFILTER_API_KEY"],
    stream_usage=True,
)

final = None
for chunk in llm.stream("Describe a rainy harbour town in four sentences."):
    print(chunk.content, end="", flush=True)
    final = chunk if final is None else final + chunk

print()
print(final.usage_metadata)

With stream_usage=True the last chunk carries input and output token counts, which the API sends at the end of the stream. Keep those numbers. They are the cheapest way to see what an agent really costs, and we use them in the cost section below.

One thing to remember: a streamed request holds its concurrency slot until the stream finishes. If you abandon a stream without closing it, that slot stays busy for longer than you expect.

Node and TypeScript

The JavaScript package uses a slightly different option name. The base URL goes inside configuration:

npm install @langchain/openai @langchain/core
import { ChatOpenAI } from "@langchain/openai";

const llm = new ChatOpenAI({
  model: "uncensored",
  apiKey: process.env.NOFILTER_API_KEY,
  configuration: { baseURL: "https://api.nofilteraichat.com/v1" },
  temperature: 0.8,
});

const res = await llm.invoke("Give me three names for a smuggler's ship.");
console.log(res.content);

The TypeScript types for the response are the usual LangChain message classes, so existing code that reads res.content or res.usage_metadata keeps working. When you run this inside a server route, build the model once at module scope rather than per request. That avoids creating a new HTTP client for every call and keeps your concurrency easy to reason about.

Everything else, including .stream(), .bindTools() and .withStructuredOutput(), behaves the same way as in Python. Temperature, top_p, stop sequences, seed and the penalty options are passed through to the model, so you can set them on the constructor like you would for any other OpenAI-style backend.

Tool calling and a small agent

The API supports tool calling: the tools and tool_choice fields, streamed tool_calls, and tool results returned as role: "tool" messages. LangGraph's prebuilt agent uses exactly that loop. Here is an agent with one tool:

import os
from langchain_core.tools import tool
from langchain_openai import ChatOpenAI
from langgraph.prebuilt import create_react_agent

@tool
def roll_dice(sides: int) -> int:
    """Roll a die with the given number of sides."""
    import random
    return random.randint(1, sides)

llm = ChatOpenAI(
    model="uncensored",
    base_url="https://api.nofilteraichat.com/v1",
    api_key=os.environ["NOFILTER_API_KEY"],
)

agent = create_react_agent(llm, [roll_dice])
out = agent.invoke(
    {"messages": [("user", "Roll a twenty-sided die twice and tell me the total.")]}
)
print(out["messages"][-1].content)

Tool use with open-weight models is less predictable than with a hosted flagship, so treat it as something to test. Write tool descriptions in plain words, keep argument schemas small, and set temperature low (0.2 to 0.4) for agent steps that must produce valid arguments. If a call comes back malformed, LangChain surfaces it as an invalid tool call, and a retry with a clearer description usually fixes it.

Keep the agent loop bounded. LangGraph has a recursion limit, and you should set it to a number you can afford. If an agent can call tools forever, a bad prompt can burn through credit while you are away from the keyboard. A limit of 10 to 15 steps is a sensible default for most tasks, and you can pass it in the config when you invoke the agent.

Structured output

JSON mode is available through response_format. In LangChain, ask for it with the JSON mode method and name the keys in your prompt:

from pydantic import BaseModel
from langchain_openai import ChatOpenAI
import os

class Character(BaseModel):
    name: str
    role: str
    secret: str

llm = ChatOpenAI(
    model="uncensored",
    base_url="https://api.nofilteraichat.com/v1",
    api_key=os.environ["NOFILTER_API_KEY"],
)
structured = llm.with_structured_output(Character, method="json_mode")
print(structured.invoke(
    "Return JSON with keys name, role and secret for a noir detective."
))

Rate limits, retries and cost

A full key allows 8 concurrent requests and 300 requests per minute. A trial key allows 2 and 60. When you exceed either, the API answers with HTTP 429 and an error code: rate_limited for requests per minute, concurrency for too many at once. Temporary overload also returns 429, with a Retry-After: 5 header.

LangChain gives you two knobs for this. max_retries on the model retries failed calls with backoff, and max_concurrency in the run config caps parallel batch work:

llm = ChatOpenAI(
    model="uncensored",
    base_url="https://api.nofilteraichat.com/v1",
    api_key=os.environ["NOFILTER_API_KEY"],
    max_retries=4,
    timeout=120,
)

answers = llm.batch(
    ["Prompt one", "Prompt two", "Prompt three", "Prompt four"],
    config={"max_concurrency": 6},
)

Setting max_concurrency to 6 on a full key leaves headroom for a second process. Remember that each agent step is a separate request, and a streamed step occupies its slot until the stream ends.

What an agent run costs

Pricing is $0.15 per 1M input tokens and $0.50 per 1M output tokens. Say an agent step sends 3,000 tokens of history, tools and instructions, and the model writes 500 tokens. The input costs 3,000 × $0.15 / 1,000,000 = $0.00045. The output costs 500 × $0.50 / 1,000,000 = $0.00025. One step is $0.0007. A ten-step run is $0.007, and 1,000 such runs cost $7.00.

Agents resend their growing history on every step, so input tokens dominate. Trimming old tool output is the cheapest optimisation you have. Failed or refused requests are not charged. Details of top-ups and bonuses are on the pricing page.

Common problems

  • 404 on every call. The base URL must end in /v1 and must not include /chat/completions. LangChain appends the path itself.
  • Model not found. The only model ID is uncensored. Names like gpt-4o will not work.
  • Context errors. The window is 100,000 tokens for prompt plus completion. Long retrieval chains can exceed it. Summarise or trim before you send.
  • Request too large. The body limit is 8 MB, which matters if you stuff whole documents into a prompt.
  • Embeddings fail. This API is text chat only. Keep your embedding model on another provider and use this endpoint for generation.

The same pattern works for other frameworks and chat front ends; the integration guide covers more clients. For anything unusual, the support page is the place to start.

Questions and answers

Can I use ChatOpenAI with a custom base URL?

Yes. Pass base_url (baseURL inside configuration in Node), your key and model uncensored. LangChain does not care which server answers as long as it follows the OpenAI chat protocol.

Does LangChain streaming work with this API?

Yes. The API streams over SSE and sends usage at the end, so .stream() and astream() work, and stream_usage=True gives you token counts.

Can I use LangChain embeddings or vector stores with it?

Not for embeddings, because the API has no embeddings endpoint. Use another embedding source for retrieval and send the retrieved text to this endpoint for generation.

What happens when I hit the rate limit?

You get HTTP 429 with the code rate_limited or concurrency. Set max_retries on ChatOpenAI and cap max_concurrency at or below 8 on a full key.

Your key is one form away

Create an account, copy the key, change the base URL. That is the whole setup.

Get API key