Skip to main content
Duru AI는 OpenAI SDK / Cursor 등 기존 OpenAI 호환 클라이언트에서 baseURLapiKey 만 교체하면 그대로 사용할 수 있는 OPEN API를 제공합니다.

1. 개요

  • OpenAI Chat Completions 규격 호환
  • OpenAI Responses 규격 호환 (Responses 전용 모델 지원)
  • 사용량은 크레딧(원화) 으로 차감됩니다.

2. 기본 정보 (Base URL)

환경Base URL
운영https://api.duruai.com/llm/v1
OpenAI SDK의 base_url 에는 끝의 /llm/v1 까지 포함해서 입력해야 합니다.

3. 인증

모든 요청은 발급받은 API 키를 Authorization: Bearer 헤더에 담아 보냅니다.
Authorization: Bearer duruai_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
  • API 키는 duruai_ 접두어로 시작합니다.
  • 키는 발급 시점에 한 번만 전체 값이 노출되며, 서버에는 해시만 저장됩니다. 분실 시 재발급이 필요합니다.
  • 개인용 키 / 조직(팀) 공유 키를 발급할 수 있습니다.

API 키 관리 엔드포인트 (로그인 세션 필요)

메서드경로설명
GET/llm/apis?isOrganization=true키 목록 조회 (조직/개인)
POST/llm/apis키 발급 (name, description?, isOrganization?)
PATCH/llm/apis/:uuid/status키 활성/비활성 (status: 1 또는 0)
DELETE/llm/apis/:uuid키 삭제

4. 지원 모델

GET /llm/v1/models 로 목록을 조회할 수 있습니다. 실제 텍스트 생성 호출이 가능한 모델은 다음과 같습니다.
모델 (model 값)제공사
gpt-5.4-miniOpenAI
gpt-5.4OpenAI
gpt-5.4-proOpenAI
gpt-5.5OpenAI
gemini-3-flashGoogle
gemini-3.1-flash-liteGoogle
gemini-3.1-proGoogle
gemini-3.5-flashGoogle
grok-4-20-reasoningxAI
grok-4-20-non-reasoningxAI
claude-4.5-haikuClaude
claude-4.5-sonnetClaude
claude-4.6-sonnetClaude
claude-4.6-opusClaude
claude-4.7-opusClaude
claude-4.8-opusClaude
claude-4.7-opus / claude-4.8-opus 는 temperature 파라미터가 자동 무시됩니다. gpt-5.4-pro / gpt-5.5 는 temperaturetop_p 파라미터가 모두 자동 무시됩니다.

5. 엔드포인트

5-1. 모델 목록 조회

GET /llm/v1/models
OpenAI Models API와 동일한 { "object": "list", "data": [...] } 형식으로 반환됩니다.

5-2. Chat Completions

POST /llm/v1/chat/completions
요청 본문
필드타입필수 여부설명
modelstringRequired모델 slug
messagesarrayRequiredrole/content 배열 (system/user/assistant)
streambooleanOptionaltrue 시 SSE 스트리밍 (기본값: false)
temperaturenumberOptional범위 0.0~2.0, 기본값 1.0. claude-4.7-opus / claude-4.8-opus / gpt-5.4-pro / gpt-5.5 호출 시 자동 무시됨
top_pnumberOptional범위 0.0~1.0, 기본값 1.0. gpt-5.4-pro / gpt-5.5 호출 시 자동 무시됨
max_tokensnumberOptional최대 출력 토큰
성공 응답 예시 (200 OK)
{
  "id": "chatcmpl-1749100000000",
  "object": "chat.completion",
  "created": 1749100000,
  "model": "gpt-5.4-mini",
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": "안녕하세요! 무엇을 도와드릴까요?"
      },
      "finish_reason": "stop"
    }
  ],
  "usage": {
    "prompt_tokens": 25,
    "completion_tokens": 12,
    "total_tokens": 37
  }
}

5-3. Responses

POST /llm/v1/responses
Responses 전용 모델까지 Cursor / OpenAI SDK에서 쓸 수 있도록 OpenAI Responses 규격을 호환합니다. 요청 본문
필드타입필수 여부설명
modelstringRequired모델 slug
inputstring/arrayRequired입력 (문자열 또는 role/content 배열)
instructionsstringOptional시스템 지시문 (system 메시지로 변환)
streambooleanOptionaltrue 시 Responses SSE 이벤트 스트리밍 (기본값: false)
temperaturenumberOptional범위 0.0~2.0, 기본값 1.0 (claude-4.7-opus / claude-4.8-opus / gpt-5.4-pro / gpt-5.5 자동 무시)
top_pnumberOptional범위 0.0~1.0, 기본값 1.0 (gpt-5.4-pro / gpt-5.5 자동 무시)
max_output_tokensnumberOptional최대 출력 토큰
성공 응답 예시 (200 OK)
{
  "id": "resp-1749100000000",
  "object": "response",
  "created_at": 1749100000,
  "model": "gpt-5.4",
  "output_text": "안녕하세요! 무엇을 도와드릴까요?",
  "output": [
    {
      "id": "resp-1749100000000-msg",
      "type": "message",
      "status": "completed",
      "role": "assistant",
      "content": [
        {
          "type": "output_text",
          "text": "안녕하세요! 무엇을 도와드릴까요?",
          "annotations": []
        }
      ]
    }
  ],
  "usage": {
    "input_tokens": 10,
    "input_tokens_details": { "cached_tokens": 0 },
    "output_tokens": 12,
    "output_tokens_details": { "reasoning_tokens": 0 },
    "total_tokens": 22
  }
}

6. 사용 예시

cURL
curl https://api.duruai.com/llm/v1/chat/completions \
  -H "Authorization: Bearer $DURUAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gpt-5.4-mini",
    "messages": [
      { "role": "system", "content": "너는 친절한 한국어 비서야." },
      { "role": "user", "content": "안녕!" }
    ]
  }'
Python
from openai import OpenAI

client = OpenAI(
    api_key="duruai_xxxxxxxxxxxxxxxxxxxx",
    base_url="https://api.duruai.com/llm/v1",
)

# Chat Completions
res = client.chat.completions.create(
    model="gpt-5.4-mini",
    messages=[{"role": "user", "content": "안녕!"}],
)
print(res.choices[0].message.content)

# Responses
res = client.responses.create(
    model="gpt-5.4",
    input="한 문장으로 자기소개 해줘",
)
print(res.output_text)
Node.js
import OpenAI from "openai";

const client = new OpenAI({
  apiKey: process.env.DURUAI_API_KEY,
  baseURL: "https://api.duruai.com/llm/v1",
});

// Chat Completions
const res = await client.chat.completions.create({
  model: "gemini-3-flash",
  messages: [{ role: "user", content: "안녕!" }],
});
console.log(res.choices[0].message.content);

스트리밍 (Streaming)

stream: true 로 설정하면 OpenAI와 동일하게 data: ... (Chat) / event: response.* ... (Responses) SSE 청크가 전송되며, 마지막에 data: [DONE] 으로 종료됩니다.

cURL (Chat Completions - 스트리밍)

cURL
curl https://api.duruai.com/llm/v1/chat/completions \
  -H "Authorization: Bearer $DURUAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gpt-5.4-mini",
    "stream": true,
    "messages": [
      { "role": "system", "content": "너는 친절한 한국어 비서야." },
      { "role": "user", "content": "안녕!" }
    ]
  }'

Python (OpenAI SDK - 스트리밍)

Python
from openai import OpenAI

client = OpenAI(
    api_key="duruai_xxxxxxxxxxxxxxxxxxxx",
    base_url="https://api.duruai.com/llm/v1",
)

# Chat Completions 스트리밍
with client.chat.completions.stream(
    model="gpt-5.4-mini",
    messages=[{"role": "user", "content": "안녕!"}],
) as stream:
    for text in stream.text_stream:
        print(text, end="", flush=True)

# Responses 스트리밍
with client.responses.stream(
    model="gpt-5.4",
    input="한 문장으로 자기소개 해줘",
) as stream:
    for event in stream:
        if event.type == "response.output_text.delta":
            print(event.delta, end="", flush=True)

Node.js (OpenAI SDK - 스트리밍)

Node.js
import OpenAI from "openai";

const client = new OpenAI({
  apiKey: process.env.DURUAI_API_KEY,
  baseURL: "https://api.duruai.com/llm/v1",
});

// Chat Completions 스트리밍
const stream = await client.chat.completions.create({
  model: "gpt-5.4-mini",
  stream: true,
  messages: [{ role: "user", content: "안녕!" }],
});

for await (const chunk of stream) {
  process.stdout.write(chunk.choices[0]?.delta?.content ?? "");
}

// Responses 스트리밍
const responseStream = await client.responses.create({
  model: "gpt-5.4",
  stream: true,
  input: "한 문장으로 자기소개 해줘",
});

for await (const event of responseStream) {
  if (event.type === "response.output_text.delta") {
    process.stdout.write(event.delta);
  }
}

SSE 응답 데이터 형식

Chat Completions 스트리밍 응답

각 청크는 data: {...} 형식으로 전송됩니다.
# 텍스트 청크 (여러 전송됨)
data: {"id":"chatcmpl-1749100000000","object":"chat.completion.chunk","created":1749100000,"model":"gpt-5.4-mini","choices":[{"index":0,"delta":{"content":"안녕"},"finish_reason":null}]}

# 마지막 청크 (usage 포함)
data: {"id":"chatcmpl-1749100000000","object":"chat.completion.chunk","created":1749100000,"model":"gpt-5.4-mini","choices":[{"index":0,"delta":{},"finish_reason":"stop"}],"usage":{"prompt_tokens":25,"completion_tokens":12,"total_tokens":37}}

# 종료 시그널
data: [DONE]

Responses 스트리밍 응답

event: 이벤트명 + data: {...} 쌍으로 전송됩니다.
event: response.created
data: {"type":"response.created","response":{"id":"resp-1749100000000","object":"response","created_at":1749100000,"model":"gpt-5.4","output_text":"","output":[{"id":"resp-1749100000000-msg","type":"message","status":"completed","role":"assistant","content":[{"type":"output_text","text":"","annotations":[]}]}],"usage":{"input_tokens":0,"input_tokens_details":{"cached_tokens":0},"output_tokens":0,"output_tokens_details":{"reasoning_tokens":0},"total_tokens":0}}}

event: response.in_progress
data: {"type":"response.in_progress","response_id":"resp-1749100000000"}

event: response.output_item.added
data: {"type":"response.output_item.added","response_id":"resp-1749100000000","output_index":0,"item":{"id":"resp-1749100000000-msg","type":"message","status":"in_progress","role":"assistant","content":[]}}

event: response.content_part.added
data: {"type":"response.content_part.added","response_id":"resp-1749100000000","item_id":"resp-1749100000000-msg","output_index":0,"content_index":0,"part":{"type":"output_text","text":"","annotations":[]}}

# 텍스트 델타 (여러 전송됨)
event: response.output_text.delta
data: {"type":"response.output_text.delta","response_id":"resp-1749100000000","item_id":"resp-1749100000000-msg","output_index":0,"content_index":0,"delta":"안녕하세요!"}

event: response.output_text.done
data: {"type":"response.output_text.done","response_id":"resp-1749100000000","item_id":"resp-1749100000000-msg","output_index":0,"content_index":0,"text":"안녕하세요! 무엇을 도와드릴까요?"}

event: response.content_part.done
data: {"type":"response.content_part.done","response_id":"resp-1749100000000","item_id":"resp-1749100000000-msg","output_index":0,"content_index":0,"part":{"type":"output_text","text":"안녕하세요! 무엇을 도와드릴까요?","annotations":[]}}

event: response.output_item.done
data: {"type":"response.output_item.done","response_id":"resp-1749100000000","output_index":0,"item":{"id":"resp-1749100000000-msg","type":"message","status":"completed","role":"assistant","content":[{"type":"output_text","text":"안녕하세요! 무엇을 도와드릴까요?","annotations":[]}]}}

event: response.completed
data: {"type":"response.completed","response":{"id":"resp-1749100000000","object":"response","created_at":1749100000,"model":"gpt-5.4","output_text":"안녕하세요! 무엇을 도와드릴까요?","output":[{"id":"resp-1749100000000-msg","type":"message","status":"completed","role":"assistant","content":[{"type":"output_text","text":"안녕하세요! 무엇을 도와드릴까요?","annotations":[]}]}],"usage":{"input_tokens":10,"input_tokens_details":{"cached_tokens":0},"output_tokens":12,"output_tokens_details":{"reasoning_tokens":0},"total_tokens":22}}}

# 종료 시그널
data: [DONE]

Cursor 연동

Cursor의 OpenAI 호환 설정에서 Override Base URL 에 https://api.duruai.com/llm/v1, API Key 에 발급받은 duruai_... 키를 입력하면 됩니다.

7. 과금

  • 요청 성공 시 입력/출력 토큰 × 모델 단가(USD) × 환율 → 원화 로 환산해 크레딧을 차감합니다.
  • 환율은 한국수출입은행 API 기준 전일 최종 환율을 사용합니다 (매일 자동 갱신, 고정 환율 아님).
  • 모델/조직별 할인율이 적용될 수 있습니다.
  • 요청 직전 잔액이 1원 미만이면 호출이 차단됩니다. (조직 키는 조직 잔액 기준)

8. 응답 상태 코드

오류 발생 시 HTTP 4xx 상태 코드와 함께, 아래 형식의 JSON이 반환됩니다. status 필드는 HTTP 상태 코드가 아닌 앱 전용 에러 코드입니다.
status (앱 코드)HTTP 상태 코드의미
100400요청 값 검증 오류
110401권한 오류 / 잘못된 API 키
120404대상 없음
300400 / 500LLM 처리 오류
400400조직 크레딧 부족
401400사용자 크레딧 부족
오류 응답 예시
{
  "status": 110,
  "message": "Invalid API key"
}