Node.js SDK

The Node.js SDK is the recommended integration path for Node.js and TypeScript applications. It handles authentication, uploads, polling, result parsing, and streaming.

Installation

The SDK requires Node.js 20.9 or newer.

npm install @ice9/sdk

Image censoring is optional. Install Sharp only if your application calls image.moderation.censor():

npm install sharp

Authentication

Pass your API key directly or use ICE9_API_KEY:

export ICE9_API_KEY=ice9_...
import { Ice9 } from "@ice9/sdk";

const client = new Ice9({ apiKey: process.env.ICE9_API_KEY });

Quickstart

import { Ice9 } from "@ice9/sdk";

const client = new Ice9({ apiKey: process.env.ICE9_API_KEY });
const image = await client.analyze("photo.jpg");

console.log(image.isNsfw);
console.log(image.category);
console.log(image.scene?.type);
console.log(image.moderation.reason);

Input types

analyze() accepts:

  • local file paths
  • image URLs
  • Buffer
  • Uint8Array
const resultA = await client.analyze("photo.jpg");
const resultB = await client.analyze("https://example.com/photo.jpg");
const resultC = await client.analyze(buffer);

JPEG, PNG, WebP, HEIC, and HEIF buffers are identified from their file signatures. For in-memory uploads you may also supply mediaType and filename; the SDK validates the media type and normalizes the filename extension to match the detected image:

const image = await client.analyze(buffer, {
  mediaType: "image/webp",
  filename: "upload.webp",
});

Unsupported or unidentifiable bytes fail locally with ImageRejectedError. Image URLs are downloaded by the SDK, validated, limited to 10 MB, and then uploaded to Ice9.

Tiers and service discovery

If you omit tier, the SDK uses the baseline basic tier.

const image = await client.analyze("photo.jpg");
const upgraded = await client.analyze("photo.jpg", { tier: "basic" });

const tiers = await client.tiers();
const services = await client.services();

See API Reference for the raw tier definitions.

Result model

The SDK exposes image-level helpers first and keeps raw service outputs available under image.services.

Core helpers

  • image.isNsfw
  • image.isSafe
  • image.category
  • image.caption
  • image.scene
  • image.moderation
  • image.nouns
  • image.verbs
  • image.services

Scene summary

image.scene is derived from content_analysis and provides a product-shaped summary:

  • image.scene?.type
  • image.scene?.intimacy
  • image.scene?.activity
  • image.scene?.activities
  • image.scene?.anatomyExposed
  • image.scene?.people
  • image.scene?.gender

Moderation helpers

const image = await client.analyze("photo.jpg");

if (image.isNsfw) {
  console.log(image.moderation.reason);
  await image.moderation.censor("photo.jpg", { output: "photo-censored.jpg" });
}

The default censoring policy covers exposed genitalia, breasts, buttocks, and anus detections at confidence 0.5 or higher. Pass labels or minConfidence to define your own policy. Censoring supports fill and pixelate, returns a Buffer, and writes JPEG, PNG, or WebP according to format, the output filename, or the input format where possible.

Sharp is not loaded for analysis or result parsing. Applications that do not use censoring do not need to install it.

Raw service access

Use image.services when you need service-level payloads:

console.log(image.services.nudenet);
console.log(image.services.colors);
console.log(image.services.metadata);

Streaming

Use stream: true to receive partial AnalysisResult objects as services complete:

import { Ice9 } from "@ice9/sdk";

const client = new Ice9({ apiKey: process.env.ICE9_API_KEY });

for await (const partial of client.analyze("photo.jpg", { tier: "basic", stream: true })) {
  if (partial.isComplete) {
    console.log("final caption:", partial.caption);
  } else {
    console.log("services seen:", partial.servicesSubmitted);
  }
}

Retrieving results later

Use the retrieval helpers if you already have an imageId:

const status = await client.getStatus(1234);
const result = await client.getResult(1234);

getStatus() returns the raw /status payload. getResult() returns a parsed AnalysisResult.

Timeouts, retries, and partial results

The default analysis timeout is 95 seconds. Set timeout in seconds on the client or for an individual analysis. Network failures and server errors are retried up to three times by default; configure this with maxRetries.

By default, an analysis with failed services raises PartialResultError. The exception includes the partial result. Set raiseOnPartial: false to return it normally instead:

const image = await client.analyze("photo.jpg", {
  timeout: 95,
  raiseOnPartial: false,
});

Serialization

Use toJSON() or toJson() to serialize results:

const payload = image.toJSON();
const pretty = image.toJson(2);

Examples

Basic analysis

import { Ice9 } from "@ice9/sdk";

const client = new Ice9({ apiKey: process.env.ICE9_API_KEY });
const image = await client.analyze("photo.jpg", { tier: "basic" });

console.log(image.caption);
console.log(image.category);
console.log(image.scene?.type, image.scene?.intimacy);

for (const noun of image.nouns?.validated ?? []) {
  console.log(noun);
}

Streaming

import { Ice9 } from "@ice9/sdk";

const client = new Ice9({ apiKey: process.env.ICE9_API_KEY });

for await (const partial of client.analyze("photo.jpg", { tier: "basic", stream: true })) {
  console.log(partial.isComplete ? partial.caption : partial.servicesSubmitted);
}

Full runnable examples

For complete scripts and local downloads:

  • https://github.com/ice9innovations/ice9-sdk/tree/main/nodejs/examples