JavaScript SDK
Run any Segmind model from Node.js and TypeScript. Async-by-default run(), a blocking runSync(), submit/wait jobs for long renders, a normalized LLM chat(), presigned file uploads, request history, webhooks and PixelFlow workflows — with retries that never bill twice.
The Segmind JavaScript SDK is a typed client over the AI Gateway
for Node.js 22+ and TypeScript. It mirrors the Python SDK:
run() submits to the v2 async API,
waits for the result and returns it, and runSync() makes one blocking v1 call.
Version 0.2. This page covers models, LLM chat, file uploads,
request history, webhooks, PixelFlow workflows, the catalog and your account.
Realtime sessions arrive in 0.3. Until 1.0, a breaking change ships in a new
minor version, so pin segmind@~0.2 if you need the API to hold still.
Install
npm install segmindThe package ships ESM and CommonJS builds with TypeScript types, and has no
dependencies. It needs Node.js 22 or later, which provides the global
fetch it uses.
Set your API key once via the environment:
export SEGMIND_API_KEY="YOUR_API_KEY"The SDK reads SEGMIND_API_KEY automatically. Grab a key from the
API Keys page.
Quick start
import { Segmind } from 'segmind';
const segmind = new Segmind(); // reads SEGMIND_API_KEY
// Submit to v2, wait until COMPLETED, return the result body.
const result = await segmind.run('seedream-4.5', { prompt: 'a red rose on a wooden table' });
console.log(result.output); // URL to the generated imageimport { writeFile } from 'node:fs/promises';
import { Segmind } from 'segmind';
const segmind = new Segmind();
// One blocking v1 call. Returns the fetch Response.
const response = await segmind.runSync('seedream-4.5', { prompt: 'a red rose on a wooden table' });
await writeFile('rose.jpg', Buffer.from(await response.arrayBuffer()));Prefer plain functions? The module-level exports use a shared client built from
SEGMIND_API_KEY:
import * as segmind from 'segmind';
const result = await segmind.run('seedream-4.5', { prompt: 'a cyberpunk city at night' });Model inputs and call options
Every call takes the model inputs as its second argument, exactly as the model page lists them, and call options as an optional third:
await segmind.run(
'seedream-4.5',
{ prompt: 'a lighthouse in a storm', aspect_ratio: '16:9' }, // model inputs
{ timeout: 120_000, signal: controller.signal }, // call options
);Keeping them apart means a model input that happens to be called timeout
still reaches the model. Times are in milliseconds.
API
segmind.run(slug, params?, options?)
Submits to POST /v2/{slug}, polls until the request reaches COMPLETED, and
resolves with the result body. Polls every 1 s and gives up after
10 minutes unless you pass timeout and interval.
| Option | Default | Description |
|---|---|---|
timeout | 600000 | Give up waiting after this many ms (the request keeps running server-side). |
interval | 1000 | Milliseconds between status checks. |
signal | — | An AbortSignal that cancels the call. |
requestId | generated | Idempotency key. See Retries that never bill twice. |
const result = await segmind.run('seedream-4.5', { prompt: 'a sunset over the ocean' });
console.log(result.output);segmind.runSync(slug, params?, options?)
One blocking call to POST /v1/{slug}. Resolves with the standard fetch
Response: read .arrayBuffer() for binary output (images, audio) or
.json() for JSON. Fine for fast models; v1 is in maintenance mode.
runSync is never retried, because every v1 call bills.
segmind.submit(slug, params?, options?)
Submits a v2 request and resolves with a Job immediately, without
waiting for the result. Use it for long renders, to keep the requestId, or
to start many requests in parallel.
const job = await segmind.submit('seedance-2.0', { prompt: 'a sunset timelapse' });
console.log(job.requestId); // e.g. "2c7f59ea-13f1-402c-9353-915a2b5a2124"
const result = await job.wait({ timeout: 15 * 60_000, interval: 5_000 });Job
| Member | Returns | Description |
|---|---|---|
.requestId | string | Unique id for the request. |
.statusUrl | string | Lightweight status endpoint (no payload). |
.responseUrl | string | Full result endpoint. |
.status() | Promise<JobStatus> | Current status without waiting. status is one of QUEUED, PROCESSING, COMPLETED, FAILED. |
.result() | Promise<RunResult> | The finished result. Throws InferenceFailed if the request failed, and a SegmindError if it hasn't finished yet. |
.wait({ timeout, interval, signal }) | Promise<RunResult> | Wait for a terminal state and return the result. Throws InferenceFailed / InferenceTimeout. |
Retries that never bill twice
Every v2 submit carries an X-Request-ID. If the connection drops or the
gateway answers 429, 502 or 504, the SDK sends the submit again with the
same id, and the gateway returns the request it already started instead of
starting (and billing) a second one.
To make a submit idempotent across process restarts, pass your own id
(8–128 characters of A-Z, a-z, 0-9, _, -):
const job = await segmind.submit('seedream-4.5', { prompt: 'a red rose' }, { requestId: `order-${orderId}` });Status checks and reads retry connection errors and 408, 429, 502, 503
and 504 (honouring Retry-After), up to maxRetries times.
LLM chat
For text and LLM models, chat() returns a ChatResponse with a
provider-agnostic .text. It is async-by-default like run(), with
chatSync() and submitChat() counterparts.
const reply = await segmind.chat('gpt-5.5', { prompt: 'Write a haiku about the sea' });
console.log(reply.text);Pass either prompt or the model's own messages: an array for OpenAI- and
Anthropic-style models, or Gemini's { system_instruction, contents } object
for Gemini models, sent unchanged. Images go in a
message with the imageUrl() helper, which accepts a URL, a data URI, a file
path (Node) or bytes:
import { imageUrl } from 'segmind';
const described = await segmind.chat('gpt-5.5', {
messages: [
{
role: 'user',
content: [{ type: 'text', text: "What's in this image?" }, await imageUrl('cat.png')],
},
],
});| Member | Description |
|---|---|
.text | The assistant's text, whichever model family answered. |
.json() | .text parsed as JSON, for structured output (response_format). |
.usage | Token usage as the model reported it. |
.finishReason | Why generation stopped, as the model reported it. |
.toolCalls | Tool calls requested by the model, or []. |
.raw | The model's full response body. |
Streaming is not supported by the gateway, and stream: true is refused. See
the v2 async reference
for the underlying response shape.
Catalog and account
const catalog = (await segmind.models.list())['Inference Model Information List'];
console.log(catalog.length, catalog[0].slug, catalog[0].type);
const account = await segmind.accounts.current();Each catalog entry carries slug, title, type (imageToVideo,
textToImage, …), description, parameters_schema, and flags such as
is_new and is_depreciated.
Files
files.upload() hosts a file so you can pass its URL as a model input. It
accepts a path (Node), a Blob or File, or bytes, and sends the file straight
to storage on a presigned URL, so large videos never travel as base64 JSON.
const { url } = await segmind.files.upload('portrait.jpg');
const result = await segmind.run('some-image-to-image-model', { image: url });
// Several at once, in order
const uploaded = await segmind.files.upload([firstBytes, secondBlob]);Each call returns { url, filename, size }. Bytes without a name need
{ filename: 'clip.mp4' } unless their type can be read from the first bytes
(PNG, JPEG, GIF, WebP, MP4, MP3, WAV and PDF can).
These URLs are public and temporary: uploads are kept for at least two
days, long enough for a model to fetch them. The
Python SDK's files.upload
returns persistent Segmind Storage
URLs instead; use that API when you need a file to stay.
Request history
// Generated outputs, newest first
const page = await segmind.generations.list({ modelName: 'flux-schnell' });
console.log(page.total_records, page.data[0]?.generation_url);
// Every request with its cost and outcome, failures included
const failed = await segmind.generations.history({ status: 'FAILED', perPage: 50 });
// One request by id, of any age; the newest few for one model
const one = await segmind.generations.get(requestId);
const latest = await segmind.generations.recent('flux-schnell');| Call | Returns |
|---|---|
list({ page, modelName, startDate, endDate, userId }) | Generated outputs, with status, credits_deduction (USD), latency_ms, prompt and the input parameters. |
history({ page, perPage, fromDate, toDate, modelName, status, sortBy, sortOrder, userId, userEmail }) | Requests, failed and in-flight ones included, with request_body and error. The window defaults to the last 7 days and spans at most 31. |
get(requestId) | One of your own requests. A teammate's id is not found. |
recent(modelName) | The newest generations for one model. |
Dates are YYYY-MM-DD. Both pages carry total_records, total_pages,
current_page, per_page, has_next, has_previous and data. history
lists a request once per output, each row with the request's full cost:
de-duplicate on request_id before summing credits_deduction.
Webhooks
Manage webhook registrations without opening the console:
const hook = await segmind.webhooks.add('https://example.com/hooks/segmind', ['PIXELFLOW']);
const hooks = await segmind.webhooks.get(); // each carries its signing_secret
await segmind.webhooks.update(hook.id, 'https://example.com/hooks/v2', ['PIXELFLOW']);
await segmind.webhooks.logs(hook.id); // delivery history
await segmind.webhooks.delete(hook.id); // deactivates itEvent types are subscription types: PIXELFLOW for PixelFlow runs. One active
registration is allowed per set of types, so a second add for the same types
is refused with a 409. See Webhooks for what
each delivery contains.
PixelFlow workflows
Run a published workflow by its API slug, which you can copy from the workflow's API tab. Inputs are keyed by each input's keyname.
const result = await segmind.pixelflows.run('your-workflow-slug', {
Text_Prompt: 'An infographic about green tea',
});
console.log(result.outputs); // { image_out: 'https://images.segmind.com/...' }run() submits, polls and returns { status, outputs, raw }. outputs maps
each output keyname to its URL or value; raw is the engine's own body. For
long workflows, submit and wait with your own deadline:
const job = await segmind.pixelflows.submit('your-workflow-slug', { Text_Prompt: '...' });
console.log(job.requestId);
const done = await job.wait({ timeout: 20 * 60_000, interval: 5_000 });
// Later, from another process:
const state = await segmind.pixelflows.getStatus(job.requestId);
const finished = await segmind.pixelflows.poll(job.requestId);Runs use the current workflow engine (/workflows/v2/{slug}, see the
Workflow API Reference); pass
{ engine: 'v1' } for the legacy one. A submit is never retried, because a
run bills. A failed run throws InferenceFailed whose detail names the node
that failed, and a run past its timeout throws InferenceTimeout.
Error handling
| Error | Thrown when | Properties |
|---|---|---|
SegmindError | Base class for every SDK error, including HTTP errors (401, 404, 5xx). | .status, .detail, .requestId, .body |
APIConnectionError | No response arrived: a network failure or the per-request timeout. | — |
InferenceFailed | A request or PixelFlow run reached FAILED. | .detail (the reason; for a workflow, the failing node), .requestId, .statusBody |
InferenceTimeout | wait() passed its timeout first. The request may still finish — call job.wait() again. | .requestId, .elapsedMs |
import { InferenceFailed, InferenceTimeout, Segmind } from 'segmind';
const segmind = new Segmind();
try {
const result = await segmind.run('seedream-4.5', { prompt: 'a red rose' });
console.log(result.output);
} catch (err) {
if (err instanceof InferenceFailed) console.error(`Model failed: ${err.detail}`);
else if (err instanceof InferenceTimeout) console.error(`Still running: ${err.requestId}`);
else throw err;
}Client options
const segmind = new Segmind({
apiKey: process.env.SEGMIND_API_KEY, // the default
timeout: 600_000, // ms per HTTP request
maxRetries: 2, // for retryable reads and submits
fetch: customFetch, // any fetch implementation
defaultHeaders: { 'x-my-header': 'value' },
});SEGMIND_BASE_URL overrides the gateway host, for example to point at a proxy.
Browsers and React Native
Keep your API key on a server. Anyone using a web or mobile app can read a
key shipped inside it. The constructor refuses an API key in a browser or
React Native app, so call Segmind from your backend and give the app your
own endpoint. Pass dangerouslyAllowBrowser: true only for internal tools
where exposing the key is acceptable.
Worked example: slow video model
Video generation can take several minutes, longer than run()'s default
10-minute wait for some models. Submit, keep the id, and wait with your own
deadline:
import { InferenceFailed, InferenceTimeout, Segmind } from 'segmind';
const segmind = new Segmind();
const job = await segmind.submit('seedance-2.0', { prompt: 'a sunset timelapse over the ocean' });
console.log(`Submitted ${job.requestId}`);
try {
const result = await job.wait({ timeout: 15 * 60_000, interval: 5_000 });
console.log(result.output); // URL to the generated video
} catch (err) {
if (err instanceof InferenceTimeout) console.log(`Not done yet; poll ${job.statusUrl} later`);
else if (err instanceof InferenceFailed) console.log(`Failed: ${err.detail}`);
else throw err;
}For truly long-running or fire-and-forget jobs, register a webhook instead of polling.
See also
- Python SDK — the same verbs in Python.
- API Reference · Async Inference (V2) — the HTTP endpoints the SDK calls.
- Authentication — API keys and headers.
- Source on GitHub — issues and changelog.