Keural API
API 키로 OpenAI Chat Completions 형식의 요청을 보냅니다.
Keural에 등록된 모델을 OpenAI Chat Completions 형식으로 호출합니다.
Base URL은 https://<환경별 공개 API 도메인>/llm/v1 형식입니다. 공개 API 도메인은 환경마다 다르므로 관리자에게 확인합니다.
| 메서드 | 경로 | 설명 |
|---|---|---|
POST | /llm/v1/chat/completions | 대화 생성 (스트리밍 지원) |
GET | /llm/v1/models | 키에 연결된 모델 목록 |
GET | /llm/v1/models/{model} | 키에 연결된 모델 정보 |
임베딩, 이미지 생성, 음성, Responses API 경로는 지원하지 않으며 404를 반환합니다.
이용 흐름
| 단계 | 하는 일 |
|---|---|
| 1. 키 발급 | 설정 → API에서 모델을 고르고 키를 받습니다. 모델 ID와 키를 복사해 둡니다. |
| 2. 클라이언트 연결 | 앱이나 코드에 Base URL, API 키, 모델 ID를 입력합니다. |
| 3. 이용과 관리 | 같은 화면에서 사용량을 확인하고, 키를 재발급하거나 회수합니다. |
준비 사항
| 항목 | 내용 |
|---|---|
| Keural 계정 | 로그인 가능한 계정 |
| API 메뉴 | 설정에 API 메뉴가 없으면 관리자에게 기능 활성화를 요청합니다. |
| 조직 멤버 권한 | 조직 워크스페이스에서 키를 받으려면 조직 관리자가 내 API 사용을 허용해야 합니다. 조직 멤버의 API 권한 참고 |
| Base URL | 관리자가 안내한 공개 API 주소 |
| 클라이언트 | OpenAI API 호환 연결을 지원하는 앱이나 SDK |
API 키 발급
- 계정 메뉴에서 설정 → API를 엽니다. 데스크톱 앱과 모바일 앱에서는 도구 구역에 있습니다.
- API 키 탭에서 키 소속(개인 워크스페이스 또는 조직 워크스페이스)과 Model ID를 고릅니다.
- 필요하면 새 키 만료일을 정합니다. 비워 두면 회수할 때까지 무기한입니다.
- 키 발급을 누르고 키 복사로 키를 복사해 보관합니다. 키 원문은 이때 한 번만 표시됩니다.
- 키는 고른 워크스페이스와 모델 하나에 연결됩니다. 사용량은 키를 발급한 사람에게 귀속됩니다.
- 같은 워크스페이스·모델에는 키가 하나만 있습니다. 새 키가 필요하면 기존 키를 재발급하거나 회수합니다.
- 키를 잃어버리면 재발급합니다. 재발급하면 이전 키는 즉시 무효가 됩니다.
- 발급할 수 있는 모델이 없으면 모델 공개 설정, 요금제, 조직 API 권한을 관리자에게 확인합니다.
조직 멤버의 API 권한
조직 소유자·관리자용입니다. 조직 워크스페이스에서 멤버가 키를 받으려면 관리자가 먼저 허용해야 합니다. 모든 멤버의 기본값은 차단입니다.
조직 관리 → 조직 API 관리는 다음 세 구역으로 구성됩니다. 메뉴가 없으면 API 기능이 꺼져 있거나 조직 관리 권한이 없는 계정입니다.
| 구역 | 하는 일 |
|---|---|
| 사용자별 API 권한 | 멤버마다 API 사용 허용을 켜고 끕니다. |
| 발급된 키 | 조직에서 발급된 키의 상태를 보고 회수합니다. 키 원문은 볼 수 없습니다. |
| API 사용량 | 조직에서 발급된 키의 요청과 토큰 사용량을 봅니다. |
| 동작 | 결과 |
|---|---|
| 허용 켜기 | 그 멤버가 조직 워크스페이스에서 키를 발급할 수 있습니다. |
| 허용 끄기 | 새 발급과 기존 키의 호출이 막힙니다. 발급된 키는 자동으로 삭제되지 않으므로 필요하면 발급된 키에서 회수합니다. |
멤버가 조직을 나가거나 제거되면 그 멤버가 조직에서 받은 키는 자동으로 회수됩니다.
클라이언트에 연결
OpenAI API 호환 앱(챗 클라이언트, IDE 확장, 자동화 도구 등)에 다음 세 값을 입력합니다.
| 값 | 입력할 내용 | 앱별 항목 이름 |
|---|---|---|
| Base URL | 관리자가 안내한 공개 API 주소(…/llm/v1) | Base URL, API Base, Endpoint, Host |
| API Key | 발급받은 키(앞뒤 공백 없이) | API Key, Token, Secret Key, Authorization |
| Model | 발급 화면의 Model ID. 키에 연결된 모델과 정확히 같아야 합니다 | Model, Model ID, Model Name |
- 앱 설정에서 OpenAI 호환 연결(Connection)을 추가합니다.
- Base URL과 API Key를 입력하고 저장합니다.
- 모델 목록에서 모델을 고릅니다. 목록에는 키에 연결된 모델 하나만 표시됩니다.
- 짧은 메시지를 보내 응답을 확인합니다.
요청 형식은 다음과 같습니다.
| 구분 | 내용 |
|---|---|
| 대화 요청 | POST <Base URL>/chat/completions |
| 모델 조회 | GET <Base URL>/models |
| 인증 헤더 | Authorization: Bearer <발급받은 키> |
| 콘텐츠 헤더 | Content-Type: application/json |
첫 요청
export KEURAL_API_KEY='발급받은_키'
export KEURAL_BASE_URL='https://<환경별 공개 API 도메인>/llm/v1'
export MODEL_ID='발급 화면의 모델 ID'
curl "$KEURAL_BASE_URL/chat/completions" \
-H "Authorization: Bearer $KEURAL_API_KEY" \
-H 'Content-Type: application/json' \
-d "{\"model\":\"$MODEL_ID\",\"messages\":[{\"role\":\"user\",\"content\":\"안녕하세요.\"}]}"요청 본문의 model은 키에 연결된 모델 ID와 같아야 합니다. provider/모델ID처럼 앞에 경로가 붙으면 마지막 / 뒤를 비교합니다. 다르면 403을 반환합니다. 응답의 model에는 항상 키의 모델 ID가 담깁니다.
from openai import OpenAI
import os
client = OpenAI(
api_key=os.environ["KEURAL_API_KEY"],
base_url=os.environ["KEURAL_BASE_URL"],
)
res = client.chat.completions.create(
model=os.environ["MODEL_ID"],
messages=[{"role": "user", "content": "안녕하세요."}],
)
print(res.choices[0].message.content)지원하는 요청 필드
최상위 필드는 아래 18개만 받습니다. 다른 필드를 보내면 400을 반환합니다. 값이 null인 선택 필드는 보내지 않은 것으로 처리합니다.
| 필드 | 규칙 |
|---|---|
model | 필수. 키의 모델 ID |
messages | 필수. 1~4,096개 |
stream · stream_options | stream_options는 stream: true일 때만 보냅니다. include_usage를 지원합니다. |
tools | 최대 128개. 함수 이름은 [A-Za-z0-9_-] 1~64자 |
tool_choice | none · auto · required · 특정 함수. required는 tools가 있어야 합니다. |
parallel_tool_calls | 모델이 지원하지 않으면 400 |
response_format | text · json_object · json_schema |
max_tokens · max_completion_tokens | 둘 중 하나만 보냅니다. 보내지 않으면 4,096(모델 상한이 더 낮으면 그 값). 모델 상한을 넘으면 400 |
temperature | 0~2. 모델이 지원하지 않으면 400 |
top_p | 0~1 |
frequency_penalty · presence_penalty | -2~2 |
stop | 문자열 또는 최대 4개 배열 |
seed | 정수 |
user | 최대 256자 |
n | 1만 |
메시지
| 역할 | 받는 필드 |
|---|---|
system · developer | content, name |
user | content(문자열 또는 텍스트·이미지 파트 배열), name |
assistant | content, name, refusal, tool_calls |
tool | content, tool_call_id |
tool메시지는 앞선tool_calls의id에 한 번씩만 답합니다.- 이미지 파트(
image_url)는 이미지를 받는 모델의user메시지에만 넣습니다.http·https주소나 PNG·JPEG·GIF·WebP의 base64 data URI를 넣고,detail은auto·low·high입니다.
본문
| 항목 | 한도 |
|---|---|
| Content-Type | application/json |
| 크기 | 1MiB |
| JSON 구조 | 최상위 객체 하나, 중첩 깊이 64 이하, 같은 키 중복 금지 |
| 요청 시간 | 최대 600초 |
본문 규칙을 어기면 400이고 param은 body입니다.
스트리밍
요청에 "stream": true를 넣으면 SSE로 응답합니다. 청크는 data: ... 이벤트로 전송되고, 마지막에 data: [DONE]이 전송됩니다. "stream_options": {"include_usage": true}를 넣으면 [DONE] 직전에 토큰 사용량 청크가 추가로 전송됩니다.
include_usage를 넣으면 모든 청크에 usage: null이 담기고, 마지막 사용량 청크는 choices가 빈 배열입니다. 비스트리밍 응답은 입력·출력 토큰이 모두 확인됐을 때만 usage를 담습니다.
응답 id는 chatcmpl-<x-request-id> 형식입니다. 모든 응답 헤더에는 x-request-id가 붙습니다.
스트리밍 중 오류가 나면 오류 JSON을 담은 data: 이벤트가 전송되고 [DONE] 없이 연결이 끝납니다. 청크 하나가 1MiB를 넘거나 응답이 16MiB를 넘으면 503으로 끝납니다.
curl -N "$KEURAL_BASE_URL/chat/completions" \
-H "Authorization: Bearer $KEURAL_API_KEY" \
-H 'Content-Type: application/json' \
-d "{\"model\":\"$MODEL_ID\",\"messages\":[{\"role\":\"user\",\"content\":\"안녕하세요.\"}],\"stream\":true,\"stream_options\":{\"include_usage\":true}}"사용 한도
사용자·조직·모델 단위로 동시 요청 수, 분당 요청 수(RPM), 분당 토큰 수(TPM)가 제한됩니다. 실제 값은 환경마다 다릅니다.
분당 토큰은 요청을 받을 때 미리 차감합니다. 입력은 영문 4글자당 1토큰, 그 밖의 글자는 1글자당 1토큰, 이미지 1장은 2,000토큰으로 계산합니다. 출력은 max_tokens(최대 4,096)만큼 차감하므로, 출력 상한을 필요한 만큼만 지정하면 한도 여유가 커집니다.
429 code | 원인 | Retry-After |
|---|---|---|
rate_limit_exceeded | RPM · TPM · 동시 요청 수 초과 | RPM·TPM은 60초, 동시 요청은 1초 |
api_busy | 서버의 처리 용량 초과 | 없음. 잠시 뒤 다시 시도합니다. |
upstream_rate_limit | 모델 공급사의 한도 초과 | 없음 |
키 관리
설정 → API → API 키 탭에서 키 상태(유효 · 만료 · 회수됨)를 확인하고 재발급하거나 회수합니다.
키는 krl_llm_으로 시작하는 51자 문자열입니다. 키 소속 하나에 키는 하나이며, 이미 있으면 재발급합니다. 만료일은 오늘 이후로만 정할 수 있습니다.
| 동작 | 결과 | 유의할 점 |
|---|---|---|
| 재발급 | 새 키를 받습니다. | 이전 키는 즉시 무효가 됩니다. 연결한 앱의 키도 바꿉니다. |
| 회수 | 키를 즉시 차단합니다. | 키 노출이 의심될 때 씁니다. 회수한 키는 복구할 수 없습니다. |
사용량 확인
API 요청과 토큰 사용량은 다음 두 곳에서 확인합니다.
| 위치 | 내용 |
|---|---|
| 설정 → API → API 사용량 탭 | 지금 워크스페이스에서 내가 발급한 키의 사용량 |
| 조직 관리 → 조직 API 관리 아래쪽 API 사용량 | 조직에서 발급된 모든 키의 사용량(조직 소유자·관리자) |
조회 조건
| 조건 | 설명 |
|---|---|
| 시작 시각 · 종료 시각 | 한국 시간 기준. 시작 시각은 포함, 종료 시각은 미포함입니다. 비워 두면 최근 7일이며, 한 번에 최대 90일까지 조회합니다. |
| Model ID | 특정 모델의 요청만 조회합니다. 비워 두면 전체입니다. |
| 요청 상태 | 전체 · 진행 중 · 완료 · 실패 · 중단 · 미확정 중에서 고릅니다. |
조건을 정하고 조회를 누릅니다. 최근 요청을 반영하려면 새로고침을 누릅니다.
화면에 나오는 값
- 적용 조회 기간: 실제로 조회된 기간입니다. 시작·종료 시각을 비웠거나 90일을 넘기면 조정된 기간이 표시됩니다.
- 요청 수와 입력 토큰 · 출력 토큰 · 전체 토큰 합계: 확인된 값만 합산합니다. 확인되지 않은 토큰은 미확인으로 표시합니다.
- 요청 목록: 요청마다 요청 시각, Model ID, 요청 상태, 입력·출력·전체 토큰을 표시합니다. 한 쪽에 20개씩 표시되며 이전 · 다음으로 넘깁니다.
행의 요청 세부 정보를 누르면 다음 값이 표시됩니다.
| 항목 | 설명 |
|---|---|
| 요청 ID | 문의할 때 전달하는 값 |
| 응답 방식 | 스트리밍 또는 일반 응답 |
| 사용량 확인 상태 | 확인됨 · 일부 확인 · 미확인 |
| 종료 시각 · 응답 시간(ms) | 요청이 끝난 시각과 걸린 시간 |
| 캐시 읽기 · 캐시 생성 · 추론 토큰 | 모델이 이 값을 반환한 경우에만 표시 |
스트리밍 요청은 stream_options.include_usage 없이도 사용량이 기록됩니다. 요청이 중간에 끊기면 일부 확인이나 미확인으로 남을 수 있습니다.
오류 형식
오류는 OpenAI 호환 형식으로 반환합니다.
{
"error": {
"message": "사람이 읽을 수 있는 오류 설명",
"type": "authentication_error",
"code": "invalid_api_key",
"param": null
}
}type은 상태 코드로 정해지고, code로 원인을 구분합니다.
| 상태 | type | code | 원인 |
|---|---|---|---|
400 | invalid_request_error | invalid_request | 필드·값·본문 규칙 위반. param에 문제가 된 필드 |
400 | invalid_request_error | upstream_rejected_request | 모델 공급사가 요청을 거부 |
401 | authentication_error | invalid_api_key | 키 형식 오류, 키 없음, 만료·회수·재발급, Authorization 헤더가 둘 이상 |
403 | permission_error | api_disabled | Keural API 기능이 꺼져 있음 |
403 | permission_error | model_api_disabled | 모델의 API 공개가 꺼져 있음 |
403 | permission_error | model_not_allowed | model이 키의 모델과 다름, 멤버 API 권한 없음 |
404 | invalid_request_error | not_found | 없는 경로 |
405 | invalid_request_error | method_not_allowed | 잘못된 메서드. Allow 헤더에 허용 메서드 |
429 | rate_limit_error | 위 사용 한도 표 | 한도 초과 |
503 | server_error | upstream_timeout | 600초 안에 응답이 끝나지 않음 |
503 | server_error | invalid_upstream_response | 공급사 응답 형식 이상, 스트림 크기 초과 |
503 | server_error | dependency_unavailable · unsupported_provider | 일시적인 내부 문제. x-request-id를 담아 문의 |
문제 해결
| 증상 | 응답 코드 | 확인할 항목 |
|---|---|---|
| 모델 목록이 비어 있거나 연결에 실패합니다 | 404 · 405 | Base URL이 …/llm/v1로 끝나는지 |
| 인증에 실패합니다 | 401 | 키 오타·앞뒤 공백, 만료·회수·재발급 여부 |
| 연결은 되는데 모델을 쓸 수 없습니다 | 403 | 모델 ID가 키의 모델과 같은지, 조직 API 권한 |
| 응답이 늦거나 가끔 실패합니다 | 429 | 사용 한도 |
| 잠시 응답이 오지 않습니다 | 5xx | 잠시 뒤 다시 시도 |
| 사용량이 보이지 않거나 실제와 다릅니다 | — | 조회 기간과 키 소속 워크스페이스 |
해결되지 않으면 다음 정보를 담아 관리자에게 문의합니다. 키 원문과 프롬프트 내용은 보내지 않습니다.
- 발생 시각
- 사용한 모델 ID
- 클라이언트 앱의 이름과 버전
- 응답 헤더의
x-request-id - 화면에 나온 오류 메시지