> For the complete documentation index, see [llms.txt](https://docs.apismart.ai/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.apismart.ai/code-examples-and-migration/javascript-and-typescript.md).

# JavaScript and TypeScript

Learn how to call ApiSmart APIs with JavaScript and TypeScript, including model discovery, text streaming, image generation, file uploads, and asynchronous video tasks.

Use JavaScript or TypeScript to integrate ApiSmart into Node.js applications, backend services, serverless functions, and AI-powered products.

This guide uses the standard `fetch` API to demonstrate authentication, model discovery, text and image generation, streaming responses, file uploads, and asynchronous video tasks.

### 🚀 Before You Start

Make sure you have:

* Node.js installed
* An ApiSmart account
* An active API Key
* Sufficient wallet balance for paid requests

The examples below use modern Node.js with built-in `fetch`.

You do not need to install an additional HTTP client library.

### 🔑 Set Your API Key

Do not hard-code your API Key directly in your application source code.

Store it in an environment variable:

```bash
export APISMART_API_KEY="your-api-key"
```

Then access it in JavaScript or TypeScript:

```javascript
const API_KEY = process.env.APISMART_API_KEY;

if (!API_KEY) {
  throw new Error("APISMART_API_KEY is not set");
}
```

Set the ApiSmart Base URL:

```javascript
const BASE_URL = "https://gw.apismart.ai/v1";
```

For JSON requests, you can reuse these headers:

```javascript
const headers = {
  Authorization: `Bearer ${API_KEY}`,
  "Content-Type": "application/json",
};
```

Keep your API Key private and never expose it in frontend browser code, public repositories, or client-side bundles.

### 🧩 Get Available Models

Retrieve the current model list with:

`GET /v1/models`

```javascript
const response = await fetch(`${BASE_URL}/models`, {
  headers: {
    Authorization: `Bearer ${API_KEY}`,
  },
});

if (!response.ok) {
  throw new Error(`Request failed: ${response.status}`);
}

const result = await response.json();

for (const model of result.data ?? []) {
  console.log(model.id);
}
```

Use the returned:

```
data[].id
```

as the `model` value in supported API requests.

Model IDs must match exactly, including periods (`.`), hyphens (`-`), and version numbers.

For model categories and current IDs, see [**Models and Model IDs**](/models-and-pricing/models-and-model-ids.md).

### 💬 Chat Completions

Use:

`POST /v1/chat/completions`

Example:

```javascript
const response = await fetch(`${BASE_URL}/chat/completions`, {
  method: "POST",
  headers,
  body: JSON.stringify({
    model: "deepseek-v4-flash",
    messages: [
      {
        role: "user",
        content: "Hello! Please introduce yourself.",
      },
    ],
  }),
});

if (!response.ok) {
  throw new Error(`Request failed: ${response.status}`);
}

const result = await response.json();

console.log(result);
```

The request uses the OpenAI-compatible `messages` format documented by ApiSmart.

For complete request behavior, see [**Chat Completions API**](/api-guides/chat-completions-api.md).

### 🌊 Stream a Text Response

Set:

```json
{
  "stream": true
}
```

to enable streaming.

```javascript
const response = await fetch(`${BASE_URL}/chat/completions`, {
  method: "POST",
  headers,
  body: JSON.stringify({
    model: "deepseek-v4-flash",
    messages: [
      {
        role: "user",
        content: "Write a short story.",
      },
    ],
    stream: true,
  }),
});

if (!response.ok) {
  throw new Error(`Request failed: ${response.status}`);
}

if (!response.body) {
  throw new Error("Response body is empty");
}

const reader = response.body.getReader();
const decoder = new TextDecoder();

while (true) {
  const { value, done } = await reader.read();

  if (done) {
    break;
  }

  process.stdout.write(decoder.decode(value, { stream: true }));
}
```

This example reads and prints data as it arrives.

ApiSmart currently documents enabling Chat streaming with `"stream": true`; the API specification does not define a separate JavaScript event parser or detailed chunk schema, so applications should process the returned stream according to their own requirements.

For an overview of streaming behavior, see [**Response Modes**](/api-guides/response-modes.md).

### 🖼️ Generate an Image

Use:

`POST /v1/images/generations`

```javascript
const response = await fetch(`${BASE_URL}/images/generations`, {
  method: "POST",
  headers,
  body: JSON.stringify({
    model: "doubao-seedream-5-0",
    prompt: "A mountain village at sunrise, watercolor style",
    size: "2K",
    watermark: false,
  }),
});

if (!response.ok) {
  throw new Error(`Request failed: ${response.status}`);
}

const result = await response.json();

console.log(result);
```

Image generation is synchronous by default.

A single image typically takes approximately **20–40 seconds**, while high-resolution, multi-reference, or sequential requests may take longer.

Your HTTP client should allow at least **90 seconds** for image generation.

### ⏱️ Set an Image Request Timeout

You can use `AbortSignal.timeout()` in modern Node.js:

```javascript
const response = await fetch(`${BASE_URL}/images/generations`, {
  method: "POST",
  headers,
  signal: AbortSignal.timeout(120_000),
  body: JSON.stringify({
    model: "doubao-seedream-5-0",
    prompt: "A futuristic city at sunset",
    size: "2K",
  }),
});
```

The example uses a **120-second** timeout to provide additional time beyond the recommended minimum.

If a client disconnects early, generation may still complete and may still incur a charge.

### 🔄 Generate from a Reference Image

Provide a public image URL using `image`:

```javascript
const response = await fetch(`${BASE_URL}/images/generations`, {
  method: "POST",
  headers,
  signal: AbortSignal.timeout(120_000),
  body: JSON.stringify({
    model: "doubao-seedream-5-0",
    prompt: "Transform this image into a watercolor painting",
    image: "https://example.com/input.jpg",
    size: "2048x2048",
  }),
});

if (!response.ok) {
  throw new Error(`Request failed: ${response.status}`);
}

console.log(await response.json());
```

Multiple reference image URLs can also be supplied as an array where supported.

For image limits and supported parameters, see [**Image Generation API**](/api-guides/image-generation-api.md).

### 📤 Upload a Local Image

Local image uploads use:

`POST /v1/images/edits`

This endpoint uses **multipart form data**, not a JSON request body.

Example for modern Node.js:

```javascript
import { readFile } from "node:fs/promises";

const image = await readFile("./character.png");

const form = new FormData();

form.append("model", "doubao-seedream-5-0");
form.append(
  "image",
  new Blob([image], { type: "image/png" }),
  "character.png"
);
form.append(
  "prompt",
  "Place the character in a cyberpunk environment"
);
form.append("size", "2K");
form.append("response_format", "url");

const response = await fetch(`${BASE_URL}/images/edits`, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${API_KEY}`,
  },
  body: form,
  signal: AbortSignal.timeout(120_000),
});

if (!response.ok) {
  throw new Error(`Request failed: ${response.status}`);
}

console.log(await response.json());
```

Do not manually set:

```
Content-Type: application/json
```

for this request.

When using `FormData`, let the runtime generate the multipart `Content-Type` and boundary automatically.

### 🌊 Stream Image Generation

Supported image workflows can also use SSE streaming.

Set:

```json
{
  "stream": true
}
```

Example:

```javascript
const response = await fetch(`${BASE_URL}/images/generations`, {
  method: "POST",
  headers,
  body: JSON.stringify({
    model: "doubao-seedream-5-0",
    prompt: "Create a minimalist product design sequence",
    size: "2K",
    sequential_image_generation: "auto",
    sequential_image_generation_options: {
      max_images: 4,
    },
    stream: true,
  }),
});

if (!response.ok) {
  throw new Error(`Request failed: ${response.status}`);
}

if (!response.body) {
  throw new Error("Response body is empty");
}

const reader = response.body.getReader();
const decoder = new TextDecoder();

while (true) {
  const { value, done } = await reader.read();

  if (done) {
    break;
  }

  process.stdout.write(decoder.decode(value, { stream: true }));
}
```

For sequential generation, SSE behavior, and supported image parameters, see [**Image Generation API**](/api-guides/image-generation-api.md) and [**Response Modes**](/api-guides/response-modes.md).

### 🎬 Create a Video Task

Video generation is asynchronous.

Create a task with:

`POST /v1/video/tasks`

```javascript
const response = await fetch(`${BASE_URL}/video/tasks`, {
  method: "POST",
  headers,
  body: JSON.stringify({
    model: "happyhorse-1.0",
    prompt:
      "A miniature city at night with a train moving through the streets",
    duration: 5,
    size: "720p",
  }),
});

if (!response.ok) {
  throw new Error(`Request failed: ${response.status}`);
}

const task = await response.json();

console.log(task);
```

Save the returned task ID so you can query the generation status.

Video generation follows a create-task-and-query workflow rather than keeping the original request open until generation finishes.

For models where resolution and duration affect billing, explicitly provide those values instead of relying on defaults.

### 🔄 Query a Video Task

Use:

`GET /v1/video/tasks/{id}`

```javascript
const taskId = "task_xxxxxx";

const response = await fetch(
  `${BASE_URL}/video/tasks/${taskId}`,
  {
    headers: {
      Authorization: `Bearer ${API_KEY}`,
    },
  }
);

if (!response.ok) {
  throw new Error(`Request failed: ${response.status}`);
}

const task = await response.json();

console.log(task);
```

Possible task statuses include:

| Status      | Meaning                |
| ----------- | ---------------------- |
| `queued`    | Waiting in queue       |
| `running`   | Generation in progress |
| `succeeded` | Generation completed   |
| `failed`    | Generation failed      |
| `cancelled` | Task was cancelled     |
| `expired`   | Task expired           |

These are the video task states defined by the current API.

### ⏳ Poll Until Video Generation Completes

A simple polling loop can look like:

```javascript
const sleep = (ms) =>
  new Promise((resolve) => setTimeout(resolve, ms));

async function waitForVideo(taskId) {
  while (true) {
    const response = await fetch(
      `${BASE_URL}/video/tasks/${taskId}`,
      {
        headers: {
          Authorization: `Bearer ${API_KEY}`,
        },
      }
    );

    if (!response.ok) {
      throw new Error(`Request failed: ${response.status}`);
    }

    const task = await response.json();

    console.log("Status:", task.status);

    if (task.status === "succeeded") {
      return task;
    }

    if (
      ["failed", "cancelled", "expired"].includes(task.status)
    ) {
      throw new Error(
        `Video task ended with status: ${task.status}`
      );
    }

    await sleep(5000);
  }
}

const result = await waitForVideo("task_xxxxxx");

console.log(result);
```

This example checks the task every 5 seconds.

For HappyHorse, Seedance, multimodal inputs, and complete video parameters, see [**Video Generation API**](/api-guides/video-generation-api.md).

### 🕘 Retrieve Recent Generated Images

Use:

`GET /v1/images`

```javascript
const params = new URLSearchParams({
  page: "1",
  page_size: "20",
});

const response = await fetch(
  `${BASE_URL}/images?${params}`,
  {
    headers: {
      Authorization: `Bearer ${API_KEY}`,
    },
  }
);

if (!response.ok) {
  throw new Error(`Request failed: ${response.status}`);
}

console.log(await response.json());
```

### 🕘 Retrieve Recent Generated Videos

Use:

`GET /v1/videos`

```javascript
const params = new URLSearchParams({
  page: "1",
  page_size: "20",
});

const response = await fetch(
  `${BASE_URL}/videos?${params}`,
  {
    headers: {
      Authorization: `Bearer ${API_KEY}`,
    },
  }
);

if (!response.ok) {
  throw new Error(`Request failed: ${response.status}`);
}

console.log(await response.json());
```

Image and video history can currently be queried for the most recent **7 days**.

For history fields and storage information, see [**Generated Media History**](/api-usage/generated-media-history.md).

### 🟦 TypeScript Example

ApiSmart uses the same HTTP API from TypeScript. You can add your own interfaces to make application code easier to maintain.

For example:

```typescript
interface ChatMessage {
  role: "user" | "assistant" | "system";
  content: string;
}

interface ChatRequest {
  model: string;
  messages: ChatMessage[];
  stream?: boolean;
}

const payload: ChatRequest = {
  model: "deepseek-v4-flash",
  messages: [
    {
      role: "user",
      content: "Explain REST APIs in simple terms.",
    },
  ],
};

const response = await fetch(
  `${BASE_URL}/chat/completions`,
  {
    method: "POST",
    headers,
    body: JSON.stringify(payload),
  }
);

if (!response.ok) {
  throw new Error(`Request failed: ${response.status}`);
}

const result = await response.json();

console.log(result);
```

These TypeScript interfaces are application-side examples rather than additional fields defined by the ApiSmart API.

You can expand them according to the response structures your application uses.

### ⚠️ Handle API Errors

Always check `response.ok` before processing the response:

```javascript
const response = await fetch(`${BASE_URL}/models`, {
  headers: {
    Authorization: `Bearer ${API_KEY}`,
  },
});

if (!response.ok) {
  const errorBody = await response.text();

  console.error("HTTP status:", response.status);
  console.error("Response:", errorBody);

  throw new Error(`ApiSmart request failed`);
}
```

Common API responses include:

| Status        | Example Code            | Meaning                                   |
| ------------- | ----------------------- | ----------------------------------------- |
| `400`         | `invalid_request_error` | Invalid request                           |
| `401`         | `invalid_api_key`       | Invalid API Key                           |
| `402`         | `insufficient_quota`    | Insufficient balance                      |
| `403`         | `model_not_allowed`     | Model unavailable to the API Key          |
| `429`         | `rate_limit_error`      | Request rate limit exceeded               |
| `502` / `504` | Gateway errors          | Temporary upstream or network issue       |
| `503`         | Service unavailable     | Provider or dependent service unavailable |

The current API error definitions also include model-capability and resource-specific errors.

For complete details, see [**HTTP Status Codes and API Errors**](/troubleshooting/http-status-codes-and-api-errors.md).

### 🔁 Retry Temporary Failures

Temporary `5xx` responses may be retried with a delay.

Example:

```javascript
const sleep = (ms) =>
  new Promise((resolve) => setTimeout(resolve, ms));

async function fetchWithRetry(url, options, maxAttempts = 3) {
  for (let attempt = 0; attempt < maxAttempts; attempt++) {
    const response = await fetch(url, options);

    if (response.status < 500) {
      return response;
    }

    if (attempt === maxAttempts - 1) {
      return response;
    }

    await sleep(2 ** attempt * 1000);
  }
}
```

Use retries for temporary service failures.

Do not repeatedly retry authentication errors or invalid request parameters without correcting the underlying problem.

### ✅ JavaScript and TypeScript Checklist

Before deploying your integration, verify that:

* Your API Key is stored securely
* API calls are made from trusted server-side environments
* The Base URL is correct
* Model IDs are copied exactly
* JSON requests use `Content-Type: application/json`
* Multipart uploads allow `FormData` to set the content type
* Image requests allow sufficient processing time
* Streaming responses are consumed incrementally
* Video tasks are polled until they reach a final state
* Temporary failures use an appropriate retry strategy
* Your account has sufficient wallet balance

### ➡️ Next Steps

Continue with:

* [**Models and Model IDs**](/models-and-pricing/models-and-model-ids.md) — Find valid model IDs
* [**Chat Completions API**](/api-guides/chat-completions-api.md) — Build text generation workflows
* [**Image Generation API**](/api-guides/image-generation-api.md) — Generate and edit images
* [**Video Generation API**](/api-guides/video-generation-api.md) — Create and query video tasks
* [**Response Modes**](/api-guides/response-modes.md) — Understand standard, streaming, and asynchronous behavior
* [**Generated Media History**](/api-usage/generated-media-history.md) — Retrieve recent generated media
