할당량 및 요청 속도 제 한
Kaleidr는 사용량을 두 가지 방식으로 제한하며, 두 경우 모두 HTTP 429를 반환합니다.
- 할당량은 청구 기간 동안 조직이 사용할 수 있는 총량입니다. 소진되면 기간이 초기화될 때까지 기다리거나 추가 사용량을 확보해야 합니다.
- 요청 속도 제한은 호출 가능한 속도를 정합니다. 제한에 도달하면 몇 초 동안 호출 속도를 낮춰야 합니다.
응답 본문을 확인하면 어느 제한에 도달했는지 알 수 있습니다. 이 페이지에서는 각 응답 형식, 호출별 과금 방식과 처리 방법을 설명합니다.
두 가지 사용량 풀
사용량은 조직 단위로 두 풀에 나누어 집계됩니다.
| 풀 | 차감되는 작업 | Free | Pro | Enterprise |
|---|---|---|---|---|
| AI 크레딧 | 채팅 답변, 장소 조회, 주소 검색, 디자인 분석 | 기간당 500,000 | 기간당 5,000,000 | 계약에 따른 맞춤 한도 |
| 지도 로드 | 각 Tile 세션 및 게시하거나 삽입한 지도를 볼 때마다 | 기간당 50,000 | 기간당 50,000 | 기본 제공량은 동일하며 초과 물량은 계약으로 정함 |
이 두 풀에 대해 혼동하기 쉬운 점이 세 가지 있습니다.
- AI 크레딧은 조직 전체가 하나의 풀을 공유합니다. API 키는 팀이 Kaleidr 자체 앱에서 사용하는 것과 동일한 AI 크레딧을 차감합니다. API 전용 예산은 없으며 모든 키 가 같은 풀을 사용합니다.
- 기간은 청구 기간을 의미합니다. 유료 구독의 경우 가입일을 시작으로 하는 월별 주기입니다. 구독이 없으면 달력상 한 달이 적용됩니다. 기간은 미국 동부 시간 자정에 전환됩니다.
- 추가 패키지는 한도를 늘립니다. 구매한 AI 크레딧 및 지도 로드 패키지가 요금제 제공량에 합산되며, 응답의
limit에 포함됩니다.
요금제를 변경하면 몇 분 이내에 기존 키에도 반영됩니다. 새 키를 생성할 필요는 없습니다.
호출별 과금 방식
| 호출 | 풀 | 과금 기준 |
|---|---|---|
POST /chat/control/stream, /chat/summary/stream, /chat/button/stream, /spot/overview/stream | AI 크레딧 | 답변에 사용된 입력 및 출력 토큰 수입니다. 스트리밍 과금 방식을 참조하세요. |
POST /chat/control/route | 없음 | 과금되지 않습니다. 경로를 계획하는 동안 크레딧이 임시로 예약되지만 항상 해제됩니다. |
POST /spot | AI 크레딧 | 결과 유무와 관계없이 호출당 고정 요금이 부과됩니다. want에 content 또는 photos가 있고 이 호출을 위해 콘텐츠를 가져오면 /retrieval/poi/enrich 요금을 적용하고, 그렇지 않으며 address가 있으면 /retrieval/geocode/forward 요금을 적용합니다. 이전 요청에서 이미 가져온 콘텐츠 는 요금이 없습니다. 장소, 지역, 경계선만 요청하는 호출은 무료입니다. |
GET /retrieval/poi/enrich | AI 크레딧 | 콘텐츠가 없어도 호출마다 고정 요금이 청구됩니다. |
GET /retrieval/geocode/forward | AI 크레딧 | 결과가 없어도 호출당 더 작은 고정 요금이 청구됩니다. 조회 자체가 실패하면 청구되지 않습니다. |
POST /design/analyze | AI 크레딧 | 호출에 성공했을 때만 고정 요금이 부과됩니다. 실패하면 과금되지 않습니다. |
POST /design/apply-direct, GET /design/styles, /design/samples, /design/themes | 없음 | 과금되지 않습니다. |
POST /sdk/sessions의 chat, editor, viewer | 없음 | 과금되지 않습니다. |
POST /sdk/sessions의 tile | 지도 로드 | 세션 교환마다 지도 로드 1회로 계산됩니다. |
POST /sdk/share-sessions | 없음 | 과금되지 않습니다. 해당 세션으로 실행한 채팅 호출은 지도 소유자의 크레딧에서 차감됩니다. |
POST /inference-api/vendor/mapping/propose(비즈니스 데이터 업로드 API의 매핑 제안) | AI 크레딧 | 제안 생성에 사용한 토큰만큼 과금됩니다. API 키가 아닌 계정 로그인 인증으로 호출합니다. 크레딧이 소진되면 429 quota_exceeded가 반환됩니다. 별도로 계정당 시간당 제안 30회 제한이 있으며, 두 429 응답의 본문은 서로 다릅니다. 업로드, 게시, 게시 취소 및 삭제에는 요금이 없습니다. |
현재 사용량과 한도는 kaleidr.com/billing에서 확인할 수 있습니다.
스트리밍 과금 방식
스트리밍 호출은 완료되기 전까지 비용을 알 수 없으므로 다음 두 단계로 과금합니다.
- 예약. 요청이 수락되면 풀에서 일정량의 크레딧을 예약합니다. 예약에 필요한 크레딧이 없으면 답변이 생성되기 전에
quota_exceeded로 거부합니다. 따라서 실제 비용이 적을 수도 있어도 시작할 때는 예약을 감당할 여유 크레딧이 필요합니다. - 정산. 스트림이 종료되면 예약 금액을 실제 과금액, 즉 답변에 사용한 토큰 수로 대체합니다. 실제 과금액은 예약 금액보다 크거나 작을 수 있습니다.
스트림을 중단하거나 답변이 완료되기 전에 오류가 발생하면 정확한 토큰 수를 알 수 없으므로 추정 비용을 청구합니다.
| 스트림 | 완료되지 않고 종료된 경우의 과금 |
|---|---|
/chat/control/stream | 요청에 보낸 대화(user와 assistant 메시지)의 추정량과 이미 스트리밍한 텍스트의 추정량(예약 금액까지 계산) 중 큰 값입니다. 예약 금액보다 클 수 있습니다. |
/chat/summary/stream, /chat/button/stream, /spot/overview/stream | 이미 스트리밍한 텍스트의 추정량이며 예약 금액을 넘지 않습니다. |
따라서 채팅 스트림을 일찍 닫아도 긴 대화가 무료가 되지는 않습니다. 답변을 거의 읽지 않았더라도 최소한 전송한 요청의 크기만큼 과금됩니다. 다음 답변에 필요한 최근 대화만 보내 요청을 작게 유지하세요. 스트림 읽기를 참조하세요.
스트림이 실행 중일 때 사용 한도에 도달해도 해당 스트림은 중단되지 않습니다. 한도 검사는 요청 수락 시 수행되며 다음 요청이 거부됩니다.
거부된 요청은 과금되지 않습니다. 속도 또는 동시 실행 제한, 유효성 검사 오류, 본문 크기 초과 등으로 처리 전에 거부된 요청은 AI 크레딧이나 동시 실행 슬롯을 계속 점유하지 않습니다. 거부된 요청에 비용을 지불하지 않고 재시도할 수 있습니다.
키별 한도
키에는 키 관리 API에서 설정하는 월간 상한(monthly_cap_tokens)을 둘 수 있습니다. 한 청구 기간 동안 해당 키가 조직의 AI 크레딧에서 소비할 수 있는 양을 제한합니다. 조직 전체 풀의 상한은 여전히 적용됩니다.
개별 상한을 가진 키의 요청이 상한을 넘게 되면 같은 quota_exceeded 본문으로 거부되지만, limit과 used에는 전체 풀 대신 해당 키의 수치가 표시됩니다.
할당량 초과
HTTP/1.1 429 Too Many Requests
Retry-After: 1296000
Content-Type: application/json
{
"error": "quota_exceeded",
"detail": "Usage limit reached. Please try again later.",
"product": "b2b",
"meter": "b2b.tokens",
"tier": "pro",
"limit": 5000000,
"used": 4999100,
"remaining": 900,
"reset_at": "2026-11-01T04:00:00+00:00",
"period": "month",
"reset_in_seconds": 1296000
}
| 필드 | 의미 |
|---|---|
error | 이 경우 항상 quota_exceeded입니다. 이 필드를 기준으로 처리 로직을 분기하세요. |
detail | 사용자에게 보여 주기 위한 문장입니다. 프로그램으로 파싱하지 마세요. |
product, meter | API의 AI 크레딧 사용량 카운터를 나타냅니다. 플랫폼 키에서는 항상 b2b와 b2b.tokens입니다. |
tier | 조직의 요금제입니다. |
limit, used, remaining | 공유 크레딧 풀의 수치이며, 키별 한도로 호출이 거부된 경우에는 해당 키의 수치입니다. remaining이 0보다 클 수 있습니다. 임시로 예약할 크레딧을 확보할 수 없어 호출이 거부되었다는 의미입니다. |
reset_at | 기간 종료 시점을 시간대 오프셋이 포함된 ISO 8601 타임스탬프로 표시합니다. 사용자에게 서비스 재개 시점을 안내할 때 이 값을 사용하세요. |
period | month입니다. |
reset_in_seconds | reset_at까지 남은 초 단위 시간으로, 크레딧 풀이 재설정되는 기간의 종료 시점을 뜻합니다(구독 결제 주기 또는 구독이 없는 경우 역월). Retry-After 헤더에도 같은 숫자가 포함됩니다. |
본문은 평면 구조입니다. 필드가 detail 객체 안이 아니라 최상위 수준에 있습니다.
세션 토큰으로 호출하는 경우
위의 본문은 서버 키 또는 레거시 키로 호출한 경우에 반환됩니다. 세션 토큰을 사용한 요청은 방문자의 브라우저에서 실행되므로 거부 응답에 조직의 요금제, 사용량 또는 한도가 공개되지 않습니다. 게시 가능한 키에서 교환한 세션과 게시된 지도의 방문자 세션 모두에 적용됩니다.
{
"error": "quota_exceeded",
"detail": "Usage limit reached. Please try again later.",
"meter": "b2b.tokens",
"reset_at": "2026-11-01T04:00:00+00:00",
"reset_in_seconds": 1296000
}
| 필드 | 서버 키 또는 레거시 키 | 세션 토큰 |
|---|---|---|
error, detail | 예 | 예 |
meter | 예 | 예 |
reset_at, reset_in_seconds | 예 | 예 |
product, period | 예 | 아니요 |
tier | 예 | 아니요 |
limit, used, remaining | 예 | 아니요 |
상태 코드, Retry-After 헤더와 의미는 동일합니다. 브라우저 코드에서는 error에 따라 처리하고 reset_at을 보여 주세요. limit, used, remaining을 읽으려 해서는 안 됩니다. 사용량 숫자가 필요하면 백엔드에서 서버 키를 사용하거나 kaleidr.com/billing에서 확인하세요.
조직에서 사용한 기록이 있는데 본문에 used: 0이 표시된다면, API가 사용량을 읽지 못해 안전을 위해 호출을 거부했을 수 있습니다. reset_at까지 기다리지 말고 몇 초 후 재시도하세요.
요청 속도 및 동시 실행 제한
모든 런타임 호출에는 세 가지 제한이 먼저 적용됩니다. 초과 요청은 즉시 거부되며 대기열에 들어가지 않습니다.
| 제한 | 계산 단위 | 적용 대상 |
|---|---|---|
| 분당 요청 수 | 키 | 키로 실행한 모든 런타임 호출 및 해당 키에서 교환한 모든 세션의 호출을 이동하는 60초 구간으로 계산합니다. 동일한 게시 가능한 키를 쓰는 사이트의 모든 방문자가 이 한도를 공유합니다. |
| 분당 세션 교환 수 | 키 | POST /sdk/sessions 호출입니다. 별도의 한도이므로 임베드를 마운트해도 런타임 호출 예산은 소모하지 않습니다. |
| 동시 요청 수 | 조직 | 조직의 모든 키에서 동시에 진행 중인 호출 수입니다. 스트림은 열려 있는 동안 계속 계산됩니다. |
키의 분당 요청 한도는 API 키 페이지나 키 관리 API에서 변경할 수 없습니다. 더 높은 호출 속도가 필요하 면 문의해 주세요.
이러한 거부 응답은 모두 같은 본문 형식과 헤더를 사용합니다.
HTTP/1.1 429 Too Many Requests
Retry-After: 5
Content-Type: application/json
{ "error": "rate_limited", "detail": "API key rate limit exceeded. Slow down." }
error | 의미 | 조치 |
|---|---|---|
rate_limited | 키의 분당 요청 또는 세션 교환 한도를 모두 사용했습니다. | Retry-After에 표시된 초만큼 기다렸다가 임의 지연을 추가하여 재시도하세요. |
org_overloaded | 조직에서 진행 중인 호출이 너무 많습니다. | 열려 있는 호출 하나가 끝날 때까지 기다렸다가 재시도하세요. 필요 없는 스트림은 닫으세요. |
b2b_overloaded | 모든 호출자에 대한 API 전체 처리 용량이 가득 찼습니다. | Retry-After만큼 대기한 후 백오프를 적용해 재시도하세요. |
limiter_unavailable | 속도 제한 서비스에서 응답하지 못했습니다. | 잠시 후 재시도하세요. 호출 속도 때문이 아니므로 속도를 낮춰도 도움이 되지 않습니다. |
visitor_limit_reached | 게시된 지도 채팅의 방문자 한 명이 분당 또는 일일 요청 한도를 초과했습니다. | 방문자에게 나중에 다시 시도하라고 안내하세요. 게시된 지도의 채팅을 참조하세요. |
네트워크 경계에서도 IP 주소와 자격 증명별로 속도를 제한합니다. 그 지점에서 차단된 요청에는 이 페이지에 설명된 JSON 본문이 포함되지 않습니다. Kaleidr 응답 본문이 없는 4xx는 호출 속도를 낮추고 나중에 재시도해야 하는 상황으로 처리하세요.
지도 로드
Kaleidr 지도가 방문자에게 시작될 때마다 지도 로드 1회가 계산됩니다.
product: "tile"의 각 세션 교환. SDK 새로고침과 요소의 재마운트도 포함됩니다.- 게시 또는 임베드된 지도의 각 조회.
임베드된 Editor의 세션 교환은 지도 로드에 해당하지 않습니다. 대신 게시 가능한 키를 포함한 기본 지도 요청(스타일, 스프라이트, 글꼴)은 묶어서 계산합니다. 조직이 한 시간 동안 이러한 요청을 20회 할 때마다 나중에 지도 로드 1회가 추가됩니다. 사용 한도 때문에 이러한 요청이 거부되는 일은 없습니다. 지도 타일 데이터 자체는 계산하지 않습니다.
모든 요금제는 할당량에 도달하면 중단됩니다. 풀이 소진되면 다음 로드는 거부되고 사용량에도 계산되지 않습니다. 유료 요금제에도 별도의 유예 구간은 없습니다. 지도 로드 팩을 구매하면 즉시 한도가 늘어납니다.
세션 교환에서 반환되는 거부 응답은 다음과 같습니다.
HTTP/1.1 429 Too Many Requests
Content-Type: application/json
{ "detail": "map_load_quota_exceeded" }
이 본문은 위의 평면 할당량 응답과 달리 detail로 감싸져 있으며 Retry-After 헤더가 없습니다. 기간이 초기화되거나 팩을 추가하기 전까지 재시도해도 소용없습니다. 페이지에서 Tile 임베드가 시작되지 않고 SDK에 kaleidr session exchange failed (429) 로그가 남습니다.
타일 직접 요청
기본 지도 타일 호스트는 공개 타일 API가 아닙니다. Kaleidr 기본 지도를 표시하는 지원 방식은 Tile 제품입니다. 허용된 출처에서 유효한 게시 가능 키로 실행하는 Kaleidr 임베드가 아닌 요청이 tile.kaleidr.com에 도착하면 거부됩니다.
HTTP/1.1 429 Too Many Requests
Retry-After: 3600
Cache-Control: no-store
Content-Type: application/json
{
"error": "tile_access_restricted",
"message": "Direct access to tile.kaleidr.com requires a Kaleidr publishable key. Mint a free one at kaleidr.com/api-keys and pass it as ?key= from an allowed origin. Kaleidr-hosted maps and embeds are unaffected.",
"docs": "https://docs.kaleidr.com/get-an-api-key"
}
이 응답은 플랫폼 API가 아닌 타일 호스트에서 오므로 API의 detail 대신 message를 사용합니다. 새로 만들거나 수정한 키가 타일에 적용되기까지 몇 분이 걸릴 수 있습니다.
스트림의 quota 이벤트
채팅 스트림은 과금이 완료된 후 풀 상태를 보고합니다. quota 이벤트는 스트림의 마지막 이벤트이며 end 또는 error 다음에 전송됩니다.
event: quota
data: {"type": "quota", "product": "b2b", "meter": "b2b.tokens", "tier": "pro", "used": 184223, "limit": 5000000, "reset_at": "2026-11-01T04:00:00+00:00", "period": "month"}
이것은 서버 키 또는 레거시 키로 읽은 스트림의 이벤트입니다. 세션 토큰(게시 가능한 키에서 교환했거나 게시된 지도의 방문자 세션으로 발급된 토큰)으로 읽는 경우 이벤트에 요금제와 사용량 숫자가 포함되지 않습니다.
event: quota
data: {"type": "quota", "meter": "b2b.tokens", "reset_at": "2026-11-01T04:00:00+00:00"}
| 필드 | 서버 키 또는 레거시 키 | 세션 토큰 |
|---|---|---|
type, meter, reset_at | 예 | 예 |
product, period | 예 | 아니요 |
tier | 예 | 아니요 |
used, limit | 예 | 아니요 |
서버 키에서는 두 번째 요청 없이 이 이벤트로 사용량 표시기를 업데이트할 수 있습니다. 다만 세 가지 제한이 있습니다.
remaining필드는 없습니다.limit - used를 계산하세요.- 이벤트 전송은 최선의 노력 방식입니다. 이벤트 없이 스트림이 종료될 수 있으므로 기다리지 마세요.
- 이 이벤트는 사후 보고 이지 사전 경고가 아닙니다. 답변 중간에 도착하거나 스트림을 중단시키는 일은 없습니다.
이벤트를 받으려면 응답 본문이 닫힐 때까지 읽으세요. end에서 읽기를 중단하는 클라이언트는 이 이벤트를 놓칩니다.
코드에서 429 처리하기
결정하기 전에 본문을 읽으세요. 상태 코드만으로는 재시도 여부를 판단할 수 없습니다.
| 수신 내용 | 재시도? | 시점 |
|---|---|---|
error: "quota_exceeded" | 아니요 | reset_at 이후 또는 크레딧을 추가하거나 키 상한을 높인 후입니다. 사용자에게 한도 도달 메시지를 보여 주세요. |
error: "rate_limited", "org_overloaded", "b2b_overloaded", "limiter_unavailable" | 예 | Retry-After 초 이후에 임의 지연을 추가하고 최대 몇 번만 재시도하세요. |
error: "visitor_limit_reached" | 아니요 | 나중에 시도하도록 방문자에게 알려 주세요. |
detail: "map_load_quota_exceeded" | 아니요 | 기간이 초기화되거나 지도 로드 팩을 추가한 후입니다. |
error: "tile_access_restricted" | 아니요 | 임베드 설정을 수정해 허용된 출처에서 게시 가능한 키로 Tile 제품을 사용하세요. |
const RETRYABLE = new Set(['rate_limited', 'org_overloaded', 'b2b_overloaded', 'limiter_unavailable']);
async function callKaleidr(url, init, attempt = 0) {
const res = await fetch(url, init);
if (res.status !== 429) return res;
const body = await res.json().catch(() => ({}));
if (RETRYABLE.has(body.error) && attempt < 3) {
const seconds = Number(res.headers.get('retry-after')) || 5;
await new Promise((resolve) => setTimeout(resolve, (seconds + Math.random()) * 1000));
return callKaleidr(url, init, attempt + 1);
}
const error = new Error(body.error || body.detail || 'rate limited');
error.status = 429;
error.code = body.error || body.detail;
error.resetAt = body.reset_at; // present for quota_exceeded
throw error;
}
브라우저에서는 JavaScript로 플랫폼 API 응답의 Retry-After 헤더를 읽을 수 있습니다.