EnglishAPI PlatformOpenAI-compatible

Nulu API documentation

Integrate your applications with OpenAI-compatible endpoints using custom public model IDs while real provider credentials and model names stay private.

Start
Base URL and core concepts.
Base URL
https://api.nuluai.com/v1
Auth
Bearer sk_wat_...
Model
nulu-5-ultra
OpenAI compatibility
Endpoints designed for OpenAI-compatible clients.
The gateway exposes OpenAI-compatible endpoints for model listing and chat completions. You can use it as a custom provider in SDKs and tools like opencode with `baseURL: https://api.nuluai.com/v1`.
Public model IDs are lowercase and case-sensitive. Use exactly `nulu-5-pro`, `nulu-5-ultra`, `nulu-image-1.5`, `nulu-image-3` in OpenAI-compatible clients, opencode, and external apps.
EndpointStatusNotes
GET /v1/modelsSupportedReturns public IDs, not real provider model names.
POST /v1/chat/completionsSupportedOpenAI chat completions compatible format.
POST /v1/images/generationsSupportedImage generation with `nulu-image-3` (JSON).
POST /v1/images/editsSupportedEditing/variations via `multipart/form-data` with image + prompt.
stream: trueSupportedForwards provider SSE and stores the sanitized response.
Authentication
Every request requires a Nulu API key.
Authorization: Bearer sk_wat_your_key
Create keys in the `API Key` dashboard section. Full secrets are shown once and then stored hashed.
Models
List of public models available to your users.
curl https://api.nuluai.com/v1/models \
  -H "Authorization: Bearer sk_wat_your_key"
Copy IDs exactly as shown here.
Public IDNameCapabilitiesThinking
nulu-5-proNulu 5 ProChat, toolsOptional
nulu-5-ultraNulu 5 UltraChat, tools, vision, advanced reasoningOptional
nulu-image-1.5Nulu Image 1.5 (Legacy)Image generation and editingUnavailable
nulu-image-3Nulu Image 3Image generation and editingUnavailable
Chat completions
Send messages and receive model responses.
cURL
curl https://api.nuluai.com/v1/chat/completions \
  -H "Authorization: Bearer sk_wat_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "nulu-5-ultra",
    "messages": [
      {"role": "system", "content": "Answer helpfully and concisely."},
      {"role": "user", "content": "Explain what a managed AI API is."}
    ],
    "temperature": 0.7,
    "max_tokens": 500
  }'
OpenAI SDK
import OpenAI from "openai";

const client = new OpenAI({
  apiKey: "sk_wat_your_key",
  baseURL: "https://api.nuluai.com/v1"
});

const response = await client.chat.completions.create({
  model: "nulu-5-ultra",
  messages: [{ role: "user", content: "Say hello in English" }]
});
The `model` field must be one of your public model IDs configured in Admin > Models. Nulu API privately maps it to the real upstream model.
Vision: Nulu 5 Ultra accepts images (JPEG, PNG, GIF, WebP; billed as input tokens). It also supports tools and configurable reasoning with `low`, `medium`, `high`, and `max`.
Images
Generate and edit images with Nulu Image 3.
Use the public ID `nulu-image-3`. The platform routes internally to the image engine — do not send third-party model names.
Generation: `POST /v1/images/generations` with JSON (`model`, `prompt`, `size`, `quality`, `n`). Editing: `POST /v1/images/edits` with `multipart/form-data` (`image`, `prompt`, optional `mask`). Responses follow the OpenAI format: `data[].url` or `data[].b64_json`.
Nulu Image 3 per-image price (generate or edit): $0.027294–$0.051177. A flat per-image price, independent of size and quality.
Nulu Image 1.5 (legacy) is billed from measured tokens, roughly $0.009505–$0.045356 per image depending on size and quality.
Nulu Image 1.5 token rates
SizeQualityGenerateEdit
1024×1024Medium$0.009567$0.009567
1024×1024Low$0.031368$0.031368
1024×1024High$0.031436$0.031436
1536×1024Low$0.045268$0.045268
1536×1024Medium$0.045356$0.045356
1536×1024High$0.009505$0.009505
2048×2048Low$0.031436$0.031436
2048×2048Medium$0.031436$0.031436
2048×2048High$0.031436$0.031436
Generate image
curl https://api.nuluai.com/v1/images/generations \
  -H "Authorization: Bearer sk_wat_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "nulu-image-3",
    "prompt": "Modern wooden chair in a bright studio",
    "size": "1024x1024",
    "quality": "medium",
    "n": 1
  }'
Edit image
curl https://api.nuluai.com/v1/images/edits \
  -H "Authorization: Bearer sk_wat_your_key" \
  -F "model=nulu-image-3" \
  -F "prompt=Same chair in vivid red" \
  -F "[email protected]" \
  -F "size=1024x1024"
OpenAI SDK
import OpenAI from "openai";
import fs from "node:fs";

const client = new OpenAI({
  apiKey: "sk_wat_your_key",
  baseURL: "https://api.nuluai.com/v1"
});

// Generate
const generated = await client.images.generate({
  model: "nulu-image-3",
  prompt: "Minimal kitchen product on a white background",
  size: "1024x1024"
});

// Edit (multipart)
const edited = await client.images.edit({
  model: "nulu-image-3",
  image: fs.createReadStream("product-photo.png"),
  prompt: "Same scene with a red finish"
});
FieldGenerationEditing
modelYes — `nulu-image-3`Yes — `nulu-image-3`
promptYesYes
imageNoYes — PNG/JPEG/WebP file
maskNoOptional — transparent PNG
sizeOptionalOptional
qualityOptional (`low`, `medium`, `high`)Optional
nOptional (1–10)Optional (1–10)
Thinking
Thinking is an option the user enables per request.
By default Nulu API sends `thinking: { type: "disabled" }` so users control when they pay for reasoning. To enable it, send `reasoning_effort: "low"`, `"medium"`, `"high"`, or `"max"`; the level also turns thinking on automatically. You can also send `thinking: { type: "enabled" }` without a level to use the provider default.
Thinking uses the same per-token rates as the model. There is no separate multiplier, but it can cost more because `reasoning_content` increases billed output tokens.
Enable thinking
const response = await client.chat.completions.create({
  model: "nulu-5-ultra",
  messages: [{ role: "user", content: "Solve this problem step by step" }],
  reasoning_effort: "high", // "low" | "medium" | "high" | "max"
  extra_body: {
    thinking: { type: "enabled" }
  }
});
Disable thinking
const response = await client.chat.completions.create({
  model: "nulu-5-ultra",
  messages: [{ role: "user", content: "Answer directly" }],
  extra_body: {
    thinking: { type: "disabled" }
  }
});
JSON mode
Structured output as valid JSON.
Send `response_format: { type: "json_object" }`, include the word JSON in the prompt, and provide an example of the expected format. Set enough `max_tokens` to avoid truncation.
const response = await client.chat.completions.create({
  model: "nulu-5-ultra",
  response_format: { type: "json_object" },
  messages: [
    { role: "system", content: "Return valid JSON only." },
    { role: "user", content: "Return a json object with question and answer." }
  ]
});
Tool calls
Allows the model to request external functions defined by your app.
The model does not execute functions. It returns `tool_calls`; your app runs the function, appends a `role: "tool"` message with `tool_call_id`, then calls the model again.
In thinking mode with tool calls, preserve and send back the assistant message `reasoning_content` in later turns to avoid provider errors.
const response = await client.chat.completions.create({
  model: "nulu-5-ultra",
  messages: [{ role: "user", content: "What is the weather in Caracas?" }],
  tools: [{
    type: "function",
    function: {
      name: "get_weather",
      description: "Get weather for a location",
      parameters: {
        type: "object",
        properties: { location: { type: "string" } },
        required: ["location"]
      }
    }
  }]
});
Multi-round chat
The API is stateless: send the full conversation history each time.
Append the previous assistant response to `messages`, then append the new user message and make another call.
const messages = [
  { role: "user", content: "What is the highest mountain?" }
];

const first = await client.chat.completions.create({ model: "nulu-5-ultra", messages });
messages.push(first.choices[0].message);
messages.push({ role: "user", content: "What is the second?" });

const second = await client.chat.completions.create({ model: "nulu-5-ultra", messages });
Context cache
Provider context caching is enabled by default.
No code changes are required. If a request reuses a persisted prefix, part of the input can count as a cache hit and is charged at the cached-input rate.
The `usage` response can include `prompt_cache_hit_tokens` or `prompt_tokens_details.cached_tokens`; Nulu API uses those tokens to apply cached-input pricing.
Supported parameters
Fields compatible with the chat completions API.
FieldRequiredDescription
modelYesPublic model ID.
messagesYesMessages with `role` and `content`.
temperatureNoControls randomness. Typical range: 0 to 2.
top_pNoNucleus sampling. Use this or temperature for simple control.
max_tokensNoMaximum generated token limit.
streamNoIf `true`, returns OpenAI-compatible SSE events.
thinkingNoUse `{ type: "enabled" }` to enable reasoning per request; disabled by default.
reasoning_effortNoAccepts `low`, `medium`, `high`, or `max`. Sending it turns thinking on automatically.
response_formatNoUse `{ type: "json_object" }` for JSON mode.
tools/tool_choiceNoTool calling when supported by the provider/model.
Usage, tokens, and storage
How usage is measured and what is stored.
Nulu API reads token usage when the provider returns it. If usage is missing, it estimates tokens as a fallback.
Billing
Prepaid balance, pricing, and deductions.
Chat models deduct balance by tokens (input, cached input, and output). Nulu Image 1.5 deducts per image based on size and quality; generation and editing use the same rate.
Nulu 5 Ultra pricing (per 1M tokens)
TypeOff-peakPeak
Input — cache miss$0.348$0.696
Input — cache hit$0.00696$0.01392
Output$1.392$2.784
Nulu 5 Pro pricing (per 1M tokens)
TypeOff-peakPeak
Input — cache miss$1.5312$3.0624
Input — cache hit$0.05104$0.10208
Output$4.5936$9.1872
Peak hours are Monday–Friday, 01:00–04:00 and 06:00–10:00 UTC. Weekends, Chinese public holidays and all other hours are off-peak. During peak hours the per-token rate doubles (2×). Applies to Nulu 5 Ultra and Nulu 5 Pro.
Billing example (Nulu 5 Ultra)

A request with 1M uncached input tokens + 1M output tokens costs $0.348 + $1.392 = $1.740 off-peak, and $0.696 + $2.784 = $3.480 during peak.

If those 1M input tokens were already cached: $0.00696 + $1.392 = $1.399 off-peak, and $0.01392 + $2.784 = $2.798 during peak.

Errors
Main status codes the API can return.
CodeMeaning
400Invalid JSON, unsupported streaming, or invalid parameters.
401Invalid or revoked API key.
402Insufficient balance. A minimum of $0.50 is required to use the API.
404Public model unavailable.
502Provider request failed.
opencode custom provider
Example configuration as an OpenAI-compatible provider.
{
  "provider": {
    "world-app-technologies-api": {
      "npm": "@ai-sdk/openai-compatible",
      "name": "Nulu API",
      "options": {
        "baseURL": "https://api.nuluai.com/v1",
        "apiKey": "sk_wat_your_key"
      },
      "models": {
        "nulu-5-pro": {
          "name": "Nulu 5 Pro",
          "reasoning": true,
          "tool_call": true,
          "interleaved": { "field": "reasoning_content" },
          "limit": { "context": 1000000, "output": 384000 },
          "variants": {
            "low": { "reasoningEffort": "low" },
            "medium": { "reasoningEffort": "medium" },
            "high": { "reasoningEffort": "high" },
            "max": { "reasoningEffort": "max" }
          }
        },
        "nulu-5-ultra": {
          "name": "Nulu 5 Ultra",
          "reasoning": true,
          "tool_call": true,
          "interleaved": { "field": "reasoning_content" },
          "limit": { "context": 1000000, "output": 384000 },
          "variants": {
            "low": { "reasoningEffort": "low" },
            "medium": { "reasoningEffort": "medium" },
            "high": { "reasoningEffort": "high" },
            "max": { "reasoningEffort": "max" }
          }
        },
        "nulu-image-1.5": {
          "name": "Nulu Image 1.5 (Legacy)"
        },
        "nulu-image-3": {
          "name": "Nulu Image 3"
        }
      }
    }
  }
}
After creating a key, point `baseURL` to `https://api.nuluai.com/v1`. The model must be one of the public IDs listed above. Reasoning models include `low`, `medium`, `high`, and `max` variants: pick them in the model selector or cycle through them with the `variant_cycle` keybind.