# CheapSub API Documentation

> **Base URL**: `https://api.cheapsub.im`
>
> 모든 요청에 `Authorization: Bearer csk_YOUR_API_KEY` 헤더가 필요합니다.

---

## 1. 모델 목록 조회

```
GET /api/v1/models
```

**응답 예시:**
```json
{
  "object": "list",
  "data": [
    { "id": "google/gemini-3-flash", "object": "model", "owned_by": "cheapsub" },
    { "id": "openai/gpt-5.4", "object": "model", "owned_by": "cheapsub" },
    { "id": "anthropic/claude-sonnet-4.6", "object": "model", "owned_by": "cheapsub" }
  ]
}
```

---

## 2. 채팅 (Chat Completions)

OpenAI 호환 형식입니다. GPT, Gemini, Claude 모든 모델을 동일한 인터페이스로 호출합니다.

```
POST /api/v1/chat
Content-Type: application/json
Authorization: Bearer csk_YOUR_API_KEY
```

### 요청 본문

| 필드 | 타입 | 필수 | 설명 |
|------|------|------|------|
| `model` | string | ✅ | 모델 ID (예: `gemini-3-flash`, `gpt-5.4`, `claude-sonnet-4.6`) |
| `messages` | array | ✅ | 대화 메시지 목록 |
| `messages[].role` | string | ✅ | `system`, `user`, `assistant` 중 하나 |
| `messages[].content` | string | ✅ | 메시지 내용 |
| `stream` | boolean | ❌ | `true` 설정 시 SSE 스트리밍 응답 |
| `temperature` | number | ❌ | 0.0 ~ 2.0 (기본값: 1.0) |
| `max_tokens` | number | ❌ | 최대 출력 토큰 수 |

### cURL 예시

```bash
curl -X POST https://api.cheapsub.im/api/v1/chat \
  -H "Authorization: Bearer csk_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gemini-3-flash",
    "messages": [
      {"role": "system", "content": "You are a helpful assistant."},
      {"role": "user", "content": "안녕하세요, 오늘 날씨 어때요?"}
    ]
  }'
```

### Python 예시 (OpenAI SDK 호환)

```python
from openai import OpenAI

client = OpenAI(
    api_key="csk_YOUR_API_KEY",
    base_url="https://api.cheapsub.im/api/v1"
)

response = client.chat.completions.create(
    model="gemini-3-flash",
    messages=[
        {"role": "system", "content": "You are a helpful assistant."},
        {"role": "user", "content": "한국의 수도는 어디인가요?"}
    ]
)

print(response.choices[0].message.content)
```

### Node.js 예시

```javascript
const response = await fetch("https://api.cheapsub.im/api/v1/chat", {
  method: "POST",
  headers: {
    "Authorization": "Bearer csk_YOUR_API_KEY",
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    model: "gemini-3-flash",
    messages: [
      { role: "user", content: "Hello, how are you?" }
    ]
  })
});

const data = await response.json();
console.log(data.choices[0].message.content);
```

### 스트리밍 예시 (Python)

```python
from openai import OpenAI

client = OpenAI(
    api_key="csk_YOUR_API_KEY",
    base_url="https://api.cheapsub.im/api/v1"
)

stream = client.chat.completions.create(
    model="gemini-3-flash",
    messages=[{"role": "user", "content": "Tell me a short story."}],
    stream=True
)

for chunk in stream:
    if chunk.choices[0].delta.content:
        print(chunk.choices[0].delta.content, end="")
```

---

## 3. 이미지 생성

```
POST /api/v1/image
Content-Type: application/json
Authorization: Bearer csk_YOUR_API_KEY
```

### 요청 본문

| 필드 | 타입 | 필수 | 설명 |
|------|------|------|------|
| `model` | string | ✅ | 이미지 모델 ID (예: `nano-banana-2`, `nano-banana-pro`, `nano-banana`) |
| `prompt` | string | ✅ | 이미지 생성 프롬프트 |
| `images` | string[] | ❌ | 참조 이미지 URL 배열 (최대 4개). 이미지 편집/변형에 사용 |
| `aspect_ratio` | string | ❌ | 가로세로 비율 (`1:1`, `16:9`, `9:16`, `4:3`, `3:4` 등) |
| `image_size` | string | ❌ | 해상도 — `"1k"`, `"2k"`, `"4k"` (`nano-banana-pro`, `nano-banana-2` 전용) |
| `callback_url` | string | ❌ | 완료 시 POST 콜백을 받을 URL |
| `failed_if_no_image` | boolean | ❌ | `true` 시 안전 필터로 이미지 미생성되면 실패 처리 (크레딧 차감됨) |

> 💡 이미지 생성은 **비동기 방식**입니다. `task_id`를 반환받고, `/api/v1/task`로 완료 여부를 조회하세요.

### cURL 예시

```bash
curl -X POST https://api.cheapsub.im/api/v1/image \
  -H "Authorization: Bearer csk_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "nano-banana-2",
    "prompt": "A cute cat sitting on a windowsill watching the rain",
    "aspect_ratio": "16:9",
    "image_size": "2k"
  }'
```

### 응답 예시 (비동기)

```json
{
  "code": 0,
  "message": "ok",
  "data": {
    "task_id": "tadaafec-7dd5-4f49-afb7-b08fbd324b96"
  }
}
```

### 태스크 완료 시 결과

```json
{
  "code": 0,
  "message": "ok",
  "data": {
    "task_id": "tadaafec-7dd5-4f49-afb7-b08fbd324b96",
    "status": "success",
    "result": [
      {
        "image": "https://google.datas.systems/fileSystem/response_images/..."
      }
    ],
    "consumed": "0.500000",
    "created_at": "2026-03-20T15:25:57.464Z"
  }
}
```

> `result[].image`는 이미지 URL 또는 `data:image/png;base64,...` 형식일 수 있습니다.

> 완료 확인: `GET /api/v1/task?task_id={task_id}`

### Python 예시

```python
import requests

response = requests.post(
    "https://api.cheapsub.im/api/v1/image",
    headers={
        "Authorization": "Bearer csk_YOUR_API_KEY",
        "Content-Type": "application/json"
    },
    json={
        "model": "nano-banana-2",
        "prompt": "서울 남산타워의 야경, 포토리얼리스틱",
        "aspect_ratio": "16:9",
        "image_size": "2k"
    }
)

data = response.json()
task_id = data["data"]["task_id"]
print(f"Task ID: {task_id}")
```

### Node.js 예시

```javascript
const response = await fetch("https://api.cheapsub.im/api/v1/image", {
  method: "POST",
  headers: {
    "Authorization": "Bearer csk_YOUR_API_KEY",
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    model: "nano-banana-2",
    prompt: "A futuristic cityscape with flying cars",
    aspect_ratio: "16:9",
    image_size: "2k"
  })
});

const data = await response.json();
console.log(data.data.task_id); // 태스크 조회로 결과 확인
```

---

## 4. 비디오 생성

비디오 생성은 비동기 방식입니다. 요청 후 `task_id`를 받고, 완료 시까지 상태를 조회합니다.

```
POST /api/v1/video
Content-Type: application/json
Authorization: Bearer csk_YOUR_API_KEY
```

### 요청 본문

| 필드 | 타입 | 필수 | 설명 |
|------|------|------|------|
| `model` | string | ✅ | 비디오 모델 ID (예: `sora-2`) |
| `prompt` | string | ✅ | 비디오 생성 프롬프트 |
| `duration` | string | ❌ | 비디오 길이 — `"10"`, `"15"`, `"25"` 중 택 1 (기본: `"10"`) |
| `resolution` | string | ❌ | 해상도 (예: `720p`, `1080p`) |

### cURL 예시

```bash
curl -X POST https://api.cheapsub.im/api/v1/video \
  -H "Authorization: Bearer csk_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "sora-2",
    "prompt": "A golden retriever running on a beach at sunset, cinematic quality",
    "duration": "10"
  }'
```

### 응답 예시 (비동기)

```json
{
  "task_id": "abc123-def456",
  "status": "processing",
  "message": "Video generation started"
}
```

---

## 5. 태스크 상태 조회

비디오 등 비동기 작업의 완료 상태를 확인합니다.

```
GET /api/v1/task?task_id={task_id}
Authorization: Bearer csk_YOUR_API_KEY
```

### cURL 예시

```bash
curl "https://api.cheapsub.im/api/v1/task?task_id=abc123-def456" \
  -H "Authorization: Bearer csk_YOUR_API_KEY"
```

### 응답 예시 (완료)

```json
{
  "task_id": "abc123-def456",
  "status": "completed",
  "result": {
    "video_url": "https://..."
  }
}
```

### Python 폴링 예시

```python
import requests
import time

API_KEY = "csk_YOUR_API_KEY"
BASE = "https://api.cheapsub.im"
HEADERS = {"Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json"}

# 1. 비디오 생성 요청
res = requests.post(f"{BASE}/api/v1/video", headers=HEADERS, json={
    "model": "sora-2",
    "prompt": "A cat playing piano in a jazz club",
    "duration": "10"
})
task_id = res.json().get("task_id")
print(f"Task ID: {task_id}")

# 2. 완료 대기 (폴링)
while True:
    status_res = requests.get(
        f"{BASE}/api/v1/task?task_id={task_id}",
        headers=HEADERS
    )
    result = status_res.json()
    print(f"Status: {result.get('status')}")
    
    if result.get("status") in ("completed", "failed"):
        print(result)
        break
    
    time.sleep(10)
```

---

## 6. 지원 모델 목록

### 언어 모델 (Chat)
| 모델 ID | 설명 |
|---------|------|
| `gemini-3.1-pro-preview` | Google Gemini 3.1 Pro |
| `gemini-3-pro-thinking` | Google Gemini 3 Pro Thinking |
| `gemini-3-pro` | Google Gemini 3 Pro |
| `gemini-3-flash-thinking` | Google Gemini 3 Flash Thinking |
| `gemini-3-flash` | Google Gemini 3 Flash |
| `claude-sonnet-4.6` | Anthropic Claude Sonnet 4.6 |
| `claude-opus-4.6` | Anthropic Claude Opus 4.6 |
| `claude-haiku-4.5-thinking` | Anthropic Claude Haiku 4.5 Thinking |
| `claude-haiku-4.5` | Anthropic Claude Haiku 4.5 |
| `claude-opus-4.5` | Anthropic Claude Opus 4.5 |
| `claude-sonnet-4.5` | Anthropic Claude Sonnet 4.5 |
| `gpt-5.4` | OpenAI GPT 5.4 |
| `gpt-5.2` | OpenAI GPT 5.2 |

### 이미지 모델
| 모델 ID | 설명 |
|---------|------|
| `nano-banana-2` | Google Nano Banana 2 |
| `nano-banana-pro` | Google Nano Banana Pro |
| `nano-banana` | Google Nano Banana |
| `gpt-image-1.5` | OpenAI GPT Image 1.5 |

### 비디오 모델
| 모델 ID | 설명 |
|---------|------|
| `sora-2` | OpenAI Sora 2 |
| `sora-2-stable` | OpenAI Sora 2 Stable |
| `sora-2-pro` | OpenAI Sora 2 Pro |
| `veo3-fast` | Google Veo 3 Fast |
| `veo3-pro` | Google Veo 3 Pro |
| `veo3.1` | Google Veo 3.1 |
| `veo3.1-fast` | Google Veo 3.1 Fast |
| `veo3.1-components` | Google Veo 3.1 Components |

---

## 7. 에러 코드

| HTTP 코드 | 의미 |
|-----------|------|
| `401` | API 키 누락 |
| `403` | 유효하지 않거나 비활성 API 키 |
| `404` | 알 수 없는 엔드포인트 |
| `405` | 허용되지 않은 HTTP 메서드 |
| `502` | 업스트림 서버 에러 |
| `504` | 업스트림 타임아웃 |

---

## 8. 레거시 엔드포인트

기존 Gemini/Claude 네이티브 API 형식도 지원합니다.

| 엔드포인트 | 설명 |
|-----------|------|
| `POST /api/v1beta/models/{model}:generateContent` | Gemini 네이티브 |
| `POST /api/v1beta/models/{model}:streamGenerateContent` | Gemini 스트리밍 |
| `POST /api/v1/messages` | Claude 네이티브 |
| `POST /api/image/gen` | 이미지 생성 (레거시) |
| `POST /api/sora2/gen` | Sora 비디오 생성 (레거시) |
| `POST /api/google/veo/generate` | Veo 비디오 생성 (레거시) |
| `GET /api/task/query?task_id={id}` | 태스크 조회 (레거시) |

---

## 과금 방식

- **1원 = 1크레딧**
- 입력/출력 토큰에 따라 크레딧 차감
- 실시간 USD/KRW 환율 반영
- 이미지/비디오 생성은 작업 단위 과금

---

> 💡 **OpenAI SDK 호환**: `base_url`을 `https://api.cheapsub.im/api/v1`로 설정하면 기존 OpenAI Python/Node.js SDK를 그대로 사용할 수 있습니다.
