---
title: SDK
description: Node.js·TypeScript용 @sume-com/client와 Python용 sume-client — 설치하고, api.sume.com을 가리키게 하고, 이미지나 비디오를 만들고, 오류를 처리해 보세요.
---

Sume는 SDK 두 개를 제공합니다. 둘 다 [Image Router와 Video Router](/models)로
이미지와 비디오를 만들고, job을 기다린 뒤 결과를 돌려줍니다.

| 언어 | 패키지 | 설치 |
|---|---|---|
| Node.js·TypeScript | [`@sume-com/client`](https://www.npmjs.com/package/@sume-com/client) `0.3.0` | `npm install @sume-com/client` |
| Python | [`sume-client`](https://pypi.org/project/sume-client/) `0.1.0` | `pip install sume-client` |

```bash
npm install @sume-com/client
```

```bash
pip install sume-client
```

`@sume-com/client`는 Node.js 18 이상의 서버에서 동작합니다. `sume-client`는
Python 3.8 이상이 필요합니다.

**다루는 범위.** 이미지·비디오 생성, job 기다리기, 파일 업로드, 타입이 있는
오류, 그리고 (TypeScript에서는) 웹훅 검증입니다. Format, Action, Agent
Completion을 비롯한 나머지 경로에는 SDK 호출이 없습니다. 같은 키로 HTTP를 직접
호출하세요. [Format 호출하기](/formats/call), [실행과 결과](/formats/runs),
[API 레퍼런스](/api/reference)에 각 요청이 있습니다.

## 클라이언트가 `api.sume.com`을 가리키게 하기

두 패키지 모두 기본값은 open.sume.com API인 `https://api.open.sume.com`입니다.
sume.com 워크스페이스를 쓰려면 base URL을 `https://api.sume.com`으로 지정하세요.

둘 다 `SUME_API_KEY`에서 키를 읽습니다. [API 키](https://www.sume.com/dashboard/api-keys)에서
만들고 서버에만 두세요.

```bash
export SUME_API_KEY="sume_live_..."
```

```ts
import { createSumeClient } from "@sume-com/client";

const sume = createSumeClient({ baseURL: "https://api.sume.com" });
```

```python
import sume_client

sume = sume_client.SyncClient(base_url="https://api.sume.com")
```

`createSumeClient`는 `credentials`(키), `timeout`, `maxRetries`, `pollInterval`,
`maxPollTime`도 받습니다. `SyncClient`와 `AsyncClient`는 `base_url`, `timeout`,
`api_key`를 받습니다.

클라이언트는 키를 `Authorization: Bearer <key>`로 보냅니다. `x-api-key` 헤더를
함께 붙이지 마세요. 두 헤더가 함께 오면 API가 거절합니다
(`401 unauthorized`, `Send only one API key credential.`).

**서버 전용입니다.** Sume API 키는 잔액을 씁니다. `@sume-com/client`는 브라우저에서
실행을 거부합니다. 클라이언트 JavaScript, 모바일 번들, `NEXT_PUBLIC_*` 변수에 키를
넣지 마세요. 앞에 자체 엔드포인트를 두고, 그 엔드포인트에서 Sume를 호출하세요.
전체 보관 규칙은 [제품에 Format 임베드](/cookbooks/embed-a-format#1-키-보관)에 있습니다.

## 이미지 만들기

```ts
import { createSumeClient } from "@sume-com/client";

const sume = createSumeClient({ baseURL: "https://api.sume.com" });

const response = await sume.subscribe("/v1/image-router/generate", {
  input: {
    model: "gpt-image-2",
    prompt: "A ceramic coffee cup on a wooden table, soft morning light",
    quality: "low",
    aspect_ratio: "1:1",
  },
});

console.log(response.status, response.images?.[0]?.url);
```

```python
import sume_client

sume = sume_client.SyncClient(base_url="https://api.sume.com")

result = sume.subscribe(
    "gpt-image-2",
    arguments={
        "prompt": "A ceramic coffee cup on a wooden table, soft morning light",
        "quality": "low",
        "aspect_ratio": "1:1",
    },
)

print(result["result"]["artifacts"][0]["url"])
```

`subscribe`는 제출한 뒤 `/v1/jobs/{id}/status`를 폴링하고, job이 완료되면
`/v1/jobs/{id}/result`를 읽습니다.

- **TypeScript**는 `input`에 `model`을 담은 라우터 경로(`/v1/image-router/generate`
  또는 `/v1/video-router/generate`)나 모델 id를 받습니다. 결과에는 `status`
  (`completed`, `failed`, `nsfw`, `canceled`), `images`, `video`, `artifacts`,
  `error`가 있습니다.
- **Python**은 모델 id를 받고 결과의 `data`(`result.artifacts`)를 돌려줍니다.
  `failed`나 `canceled`로 끝난 job은 대신 `job.error`가 담긴 상태 레코드를
  돌려줍니다.
- 모델 id는 공개 모델 카탈로그(`GET /v1/open/catalog/{id}`)로 라우팅되므로,
  없는 id는 job이 생기기 전에 실패합니다.

## 비디오 만들기

```ts
const response = await sume.subscribe("/v1/video-router/generate", {
  input: {
    model: "seedance-2.5",
    prompt: "Steam rises from the cup as the camera slowly pushes in",
    duration: 5,
    resolution: "480p",
    aspect_ratio: "16:9",
  },
});

console.log(response.status, response.video?.url);
```

```python
result = sume.subscribe(
    "seedance-2.5",
    arguments={
        "prompt": "Steam rises from the cup as the camera slowly pushes in",
        "duration": 5,
        "resolution": "480p",
        "aspect_ratio": "16:9",
    },
)

print(result["result"]["artifacts"][0]["url"])
```

비디오는 보통 몇 분 걸립니다. TypeScript 클라이언트는 `maxPollTime`(기본 5분)이
지나면 기다리기를 멈추고 `TimeoutError`를 던집니다. 그 `requestId`가 job id이고,
job은 계속 실행됩니다. 긴 job에는 `maxPollTime`을 늘리거나, 나중에
`/v1/jobs/{id}`로 읽으세요. Python 클라이언트는 job이 끝날 때까지 기다립니다.

## 지금 제출하고 나중에 기다리기

```python
controller = sume.submit(
    "gpt-image-2",
    arguments={"prompt": "A paper boat on a pond", "quality": "low"},
    webhook_url="https://example.com/hooks/sume",
)
print(controller.request_id)

for status in controller.poll_request_status():
    print(type(status).__name__)

result = controller.get()
```

`SyncClient`에는 `status(request_id)`, `result(request_id)`, `cancel(request_id)`도
있습니다. `AsyncClient`는 `asyncio`용으로 같은 메서드를 제공합니다.

TypeScript에서는 `withPolling: false`를 넘기면 접수되는 즉시 job을 돌려받고,
`webhook: { url }`을 넘기면 Sume가 결과를 그 엔드포인트로 POST합니다. 그 전달은
[웹훅 검증](/sdk/webhooks)으로 확인하세요.

## 파일 업로드

모델은 입력 미디어를 URL로 받습니다. 로컬 파일은 먼저 업로드하세요.

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

const imageUrl = await sume.uploadImage(await readFile("photo.png"), "png");
const audioUrl = await sume.upload(await readFile("speech.wav"), "audio/wav");
```

```python
image_url = sume.upload_file("photo.png")
audio_url = sume.upload(open("speech.wav", "rb").read(), "audio/wav")
```

각각 `POST /v1/files/generate-upload-url`에 업로드할 자리를 받고, 바이트를 올린 뒤
파일의 공개 URL을 돌려줍니다. 업로드는 무료입니다.

## 오류

| TypeScript | Python | 언제 |
|---|---|---|
| `AuthenticationError` | `SumeClientError`, `status_code` 401 | 키가 없거나, 틀렸거나, 폐기됨 |
| `NotEnoughCreditsError` | `SumeClientError`, `status_code` 402 | 잔액이 가격을 감당하지 못함 |
| `BadInputError` | `SumeClientError`, `status_code` 400 | 필드가 없거나 잘못됨 |
| `APIError` | `SumeClientError` | 그 밖의 거절. `statusCode`(`status_code`)와 `code`를 읽으세요 |
| `TimeoutError` | — | `maxPollTime`이 지남. job은 계속 실행됨 |
| `CredentialsMissedError` | `CredentialsMissedError` | 키를 넘기지 않았고 `SUME_API_KEY`도 없음 |

각 오류에는 API의 `code`와 `request_id`(TypeScript는 `requestId`)가 담깁니다.
코드 목록은 [오류와 크레딧](/workflows/errors-and-credits)에 있습니다.

```ts
import { NotEnoughCreditsError } from "@sume-com/client";

try {
  await sume.subscribe("/v1/image-router/generate", { input });
} catch (error) {
  if (error instanceof NotEnoughCreditsError) return askToTopUp();
  throw error;
}
```

**안전한 재시도.** 모든 제출에 `Idempotency-Key`가 붙으므로, 다시 보낸 제출이
과금되는 두 번째 job을 만들지 않습니다. TypeScript에서는 `idempotencyKey`로 직접
키를 줄 수 있습니다. 클라이언트는 `429`와 `5xx` 응답을 스스로 재시도합니다.

## 다음 단계

| 하고 싶은 일 | 읽을 문서 |
|---|---|
| job, 또는 Format·Action·Agent 실행 기다리기 | [실행과 Job 기다리기](/sdk/runs) |
| 서명된 웹훅 전달 확인 | [웹훅 검증](/sdk/webhooks) |
| 정확한 요청·응답 필드 | [API 레퍼런스](/api/reference) |
| 파트너 통합 전체 | [제품에 Format 임베드](/cookbooks/embed-a-format) |
