← Back to research
Guides11 min readPublished 2026-07-18

How to Call an OpenAI-Compatible API in Node.js with LumeAPI

Use the OpenAI JavaScript SDK with LumeAPI in Node.js: configure baseURL, send and stream chat requests, switch models, and handle API errors.

By LumeAPI Engineering Team

Explore the OpenAI-Compatible API →

Last verified: July 18, 2026

A server-side Node.js application can call LumeAPI with the OpenAI JavaScript SDK by setting baseURL to https://api.lumeapi.site/v1, loading a LumeAPI key from the server environment, and selecting a model ID from the [LumeAPI catalog](/models). Existing Chat Completions code normally keeps the same messages and choices structure; the key, base URL, and model name are the main configuration changes.

This guide builds a minimal request, adds model configuration and streaming, and finishes with production-oriented timeout, retry, and error handling. If you use Python instead, see the [OpenAI-compatible API Python guide](/research/openai-compatible-api-python).

What you need

Prepare the following before you start:

  • A current server-side Node.js runtime.
  • npm, pnpm, or another compatible package manager.
  • A LumeAPI account and API key.
  • A text model listed in the [LumeAPI documentation](/docs).

Create a project and install the official JavaScript SDK:

bash
mkdir lumeapi-node-example
cd lumeapi-node-example
npm init -y
npm install openai

The examples use ECMAScript modules and the .mjs extension, so no package configuration change is required for the first request.

Keep the API key on the server

Set the key in the environment that runs Node.js. On macOS or Linux:

bash
export LUMEAPI_KEY='your-lumeapi-key'

In PowerShell:

powershell
$env:LUMEAPI_KEY = 'your-lumeapi-key'

Never embed this key in client-side JavaScript, a React bundle, a mobile application, or a public repository. Browser code should call your own authenticated backend route; the backend can then call LumeAPI. An environment variable hides the key from source control, but production systems should use their hosting platform's secret manager and access controls.

Send your first Node.js request

Create app.mjs:

javascript
import OpenAI from 'openai';

const apiKey = process.env.LUMEAPI_KEY;
if (!apiKey) {
  throw new Error('Set LUMEAPI_KEY before running this script.');
}

const client = new OpenAI({
  apiKey,
  baseURL: 'https://api.lumeapi.site/v1',
});

const completion = await client.chat.completions.create({
  model: 'gpt-5.4-mini',
  messages: [
    {
      role: 'user',
      content: 'Explain what an API gateway does in two sentences.',
    },
  ],
});

console.log(completion.choices[0].message.content);

Run the file:

bash
node app.mjs

The SDK appends the Chat Completions route to the configured gateway, sending the request to https://api.lumeapi.site/v1/chat/completions. The generated text is available at completion.choices[0].message.content.

LumeAPI currently documents Chat Completions for its compatible text models, so this guide uses client.chat.completions.create() rather than assuming every OpenAI-specific endpoint is available through the gateway.

Make the model configurable

A model environment variable lets staging, production, and local development use different models without editing application code.

javascript
import OpenAI from 'openai';

const client = new OpenAI({
  apiKey: process.env.LUMEAPI_KEY,
  baseURL: 'https://api.lumeapi.site/v1',
});

const model = process.env.LUMEAPI_MODEL ?? 'gpt-5.4-mini';

const completion = await client.chat.completions.create({
  model,
  messages: [
    { role: 'system', content: 'Answer clearly and concisely.' },
    { role: 'user', content: 'Suggest three names for a coding assistant.' },
  ],
});

console.log(completion.choices[0].message.content);

Switch models from the shell:

bash
export LUMEAPI_MODEL='gpt-5.6-terra'
node app.mjs

Examples currently listed by LumeAPI include gpt-5.4-mini, gpt-5.6-terra, claude-sonnet-4-6, and gemini-3.5-flash. Treat the live catalog as the source of truth because model IDs and availability can change. Copy the exact API ID rather than guessing it from a display name.

Stream text with async iteration

For chat interfaces, streaming lets the application show partial output while the model is generating the rest. The JavaScript SDK exposes the stream as an async iterable.

javascript
import OpenAI from 'openai';

const client = new OpenAI({
  apiKey: process.env.LUMEAPI_KEY,
  baseURL: 'https://api.lumeapi.site/v1',
});

const stream = await client.chat.completions.create({
  model: process.env.LUMEAPI_MODEL ?? 'gpt-5.4-mini',
  messages: [
    { role: 'user', content: 'Write a four-line welcome message.' },
  ],
  stream: true,
});

for await (const chunk of stream) {
  const text = chunk.choices[0]?.delta?.content ?? '';
  process.stdout.write(text);
}

process.stdout.write('\n');

Optional chaining matters because a stream event may not contain a text delta. In an HTTP server, forward chunks with a streaming response or Server-Sent Events. Do not wait for the full answer on the server if the browser is meant to display tokens as they arrive.

Also handle client disconnects in the web framework you use. Continuing an expensive model request after the user has closed the page can waste capacity and budget.

Add explicit timeouts, retries, and API errors

Production code should distinguish a provider response from a local programming error. Configure a bounded timeout and retry count, then inspect SDK API errors without logging secrets or sensitive prompts.

javascript
import OpenAI from 'openai';

const client = new OpenAI({
  apiKey: process.env.LUMEAPI_KEY,
  baseURL: 'https://api.lumeapi.site/v1',
  timeout: 30_000,
  maxRetries: 2,
});

async function generateAnswer(question) {
  try {
    const completion = await client.chat.completions.create({
      model: process.env.LUMEAPI_MODEL ?? 'gpt-5.4-mini',
      messages: [{ role: 'user', content: question }],
    });

    return completion.choices[0].message.content ?? '';
  } catch (error) {
    if (error instanceof OpenAI.APIError) {
      const details = {
        status: error.status,
        name: error.name,
        requestId: error.request_id,
      };

      console.error('LumeAPI request failed', details);

      if (error.status === 401) {
        throw new Error('Authentication failed. Check LUMEAPI_KEY.');
      }

      if (error.status === 429) {
        throw new Error('Rate limited. Retry later with bounded backoff.');
      }

      if (error.status && error.status >= 500) {
        throw new Error('Provider error. Retry or use a tested fallback.');
      }
    }

    throw error;
  }
}

const answer = await generateAnswer(
  'What should an API health check verify?',
);
console.log(answer);

The SDK has its own transient-error retry behavior, but your application still owns the overall policy. Avoid nested retry loops that multiply attempts. Use exponential backoff with jitter, cap the number of attempts, and do not retry invalid credentials or invalid model names. For a broader reliability design, read [LLM API timeouts, 429s, and provider failures](/research/handle-llm-api-timeouts-429-errors-provider-failures-production-lumeapi).

Create one reusable client module

Centralizing the gateway configuration reduces the chance that one part of an application accidentally calls a different host or uses a different timeout.

javascript
// lume-client.mjs
import OpenAI from 'openai';

const LUMEAPI_BASE_URL = 'https://api.lumeapi.site/v1';
const DEFAULT_MODEL = 'gpt-5.4-mini';

export function createLumeClient() {
  const apiKey = process.env.LUMEAPI_KEY;
  if (!apiKey) {
    throw new Error('LUMEAPI_KEY is required.');
  }

  return new OpenAI({
    apiKey,
    baseURL: LUMEAPI_BASE_URL,
    timeout: 30_000,
    maxRetries: 2,
  });
}

export function getLumeModel() {
  return process.env.LUMEAPI_MODEL ?? DEFAULT_MODEL;
}

Application services can import these functions instead of recreating clients with scattered configuration. In tests, replace the module or inject a fake client so unit tests do not send billable requests.

Common Node.js integration problems

The request still goes to api.openai.com

Check that the exact client making the request was constructed with baseURL: 'https://api.lumeapi.site/v1'. JavaScript uses the camel-case property baseURL; base_url is the Python spelling and will not configure the JavaScript client.

The browser exposes the API key

Move the SDK call into a server route, server action, worker, or backend service. Any credential included in browser-delivered JavaScript should be treated as public and rotated.

Node reports an import error

Use an .mjs file for the examples above, or configure the project for ESM. Do not mix require() examples with ESM imports without understanding the project's module setting.

The API returns 401

Verify that LUMEAPI_KEY is available to the running Node process. A variable set in one shell, container, or deployment stage may not exist in another. Log only whether the variable is present, never its value.

The API returns model not found

Copy the exact model ID from the live catalog and confirm that the base URL includes /v1. Do not assume that a provider's display name is accepted as an API identifier.

Streaming prints blank chunks

Some events do not contain text. Use chunk.choices[0]?.delta?.content ?? '' and ignore empty values.

TypeScript notes

The same SDK is written for JavaScript and TypeScript. In a TypeScript project, keep the client configuration the same and add types around your own application inputs and outputs. Validate user-supplied prompts, limit request size, and do not assume that a successful HTTP response contains the business-specific format your application expects.

If you need structured application data, create a separate implementation and validation plan for the exact model and compatibility surface rather than parsing arbitrary prose with a fragile regular expression.

Production checklist

Before sending real traffic:

  • Keep LumeAPI calls on the server.
  • Store the API key in a secret manager or protected environment variable.
  • Pin and periodically update the openai package.
  • Copy model IDs from the live LumeAPI catalog.
  • Set explicit timeouts and bounded retries.
  • Add latency, HTTP status, model, and request-ID logging.
  • Avoid logging sensitive prompts and completions.
  • Monitor rate limits, spend, errors, and fallback usage.
  • Test streaming disconnects and provider failures before launch.

Can an existing OpenAI Node.js integration migrate?

Usually, yes when it uses Chat Completions and common request fields. Start by changing the API key, baseURL, and model ID, then run your application's own integration tests. OpenAI compatibility describes a shared API shape; it does not guarantee that every OpenAI-specific endpoint, beta feature, or model parameter behaves identically through another gateway.

Review the [OpenAI-compatible API overview](/openai-compatible-api), [LumeAPI docs](/docs), and the selected model page before production deployment.

Sources and verification

  • [LumeAPI documentation](/docs) for the gateway base URL, Chat Completions endpoint, and current model IDs.
  • [OpenAI JavaScript quickstart](https://platform.openai.com/docs/quickstart/make-your-first-api-request) for the official server-side SDK installation and import pattern.
  • [Official OpenAI Node.js SDK](https://github.com/openai/openai-node) for JavaScript client configuration, streaming, retries, timeouts, and API error conventions.

The examples were checked for current SDK structure and JavaScript syntax on July 18, 2026. A billable end-to-end request requires a customer LumeAPI key, so this article does not claim model output quality or latency.