에러와 한도
에러 응답
에러는 HTTP 상태 코드와 함께 항상 이 모양으로 와요.
{
"error": {
"code": "invalid_request",
"message": "prompt: Field required",
"request_id": "177bddb303e0425f807f16b6b8bc4ffc"
}
}
| 필드 | 설명 |
|---|---|
code | 에러 종류. 프로그램에서는 이 값으로 나눠 처리하세요 |
message | 사람이 읽을 설명(영어). 어느 필드가 왜 틀렸는지 알려 줘요. 문구는 바뀔 수 있어요 |
request_id | 이 요청의 ID. 응답 헤더 X-Request-Id와 같아요. 문의할 때 알려 주세요 |
에러 코드
| 상태 | code | 뜻과 할 일 |
|---|---|---|
| 401 | missing_key | 키를 보내지 않았어요. Authorization: Bearer DDSK-… 헤더를 넣으세요 |
| 401 | invalid_key | 키가 틀렸거나 폐기됐어요 |
| 402 | insufficient_credits | 크레딧이 모자라요. 충전한 뒤 다시 보내세요 |
| 403 | forbidden_media_link | 다른 계정의 데브다이브 파일 링크를 입력으로 넣었어요 |
| 404 | model_not_found | 없는 모델 ID거나, 이 엔드포인트에서 쓰지 않는 모델이에요. GET /v1/models로 확인하세요 |
| 404 | not_found | 없는 경로거나 찾을 수 없는 작업이에요 |
| 405 | method_not_allowed | 경로는 맞지만 메서드(GET, POST)가 달라요 |
| 410 | link_expired | 3일이 지난 데브다이브 파일 링크예요. GET /v1/files에서 새 링크를 받으세요 |
| 413 | payload_too_large | JSON 본문이 2MB를, 또는 파일이 50MB를 넘었어요 |
| 415 | unsupported_media_type | 올릴 수 없는 파일 형식이에요. → 파일 |
| 422 | invalid_request | 필드가 빠졌거나, 값이 틀렸거나, 모르는 필드가 있어요. message에 어느 필드인지 나와요 |
| 422 | invalid_media_link | 데브다이브 파일 링크가 깨졌어요. 받은 링크를 고치지 말고 그대로 넣으세요 |
| 422 | idempotency_key_reused | 같은 Idempotency-Key를 다른 요청에 썼어요 |
| 429 | rate_limited | 계정의 분당 호출 수(또는 파일 올리기 수)를 넘었어요 |
| 429 | too_many_concurrent_requests | 계정에서 동시에 처리 중인 요청이 너무 많아요 |
| 429 | model_rate_limited | 이 모델의 분당 호출 수를 넘었어요 |
| 429 | model_token_rate_limited | 이 모델의 분당 토큰 수를 넘었어요 |
| 429 | model_busy | 이 모델로 동시에 돌릴 수 있는 작업이 꽉 찼어요 |
| 429 | too_many_invalid_keys | 같은 곳에서 틀린 키를 계속 보냈어요. 키를 확인하고 잠시 뒤에 보내세요 |
| 429 | upstream_rate_limited | 데브다이브 모델 서버가 붐벼요 |
| 500 | internal_error | 저희 쪽 문제예요. request_id와 함께 알려 주세요 |
| 502 | upstream_error | 모델 서버가 처리하지 못했어요. message에 이유가 있어요 |
| 503 | upstream_unreachable | 모델 서버가 답하지 않았어요 |
| 503 | keystone_unavailable | 키를 확인하는 서버가 답하지 않았어요 |
작업이 실패한 경우는 HTTP 에러가 아니라, 작업 객체의 status: "failed"와 error.code: "generation_failed"로
알려 줘요. → 작업 결과 받기
다시 보내도 되는 에러
| 상태 | 다시 보내기 |
|---|---|
| 429 | 응답 헤더 Retry-After(초)만큼 기다렸다가 다시 보내세요. 동시 실행 한도는 5초, 분당 한도는 60초예요 |
| 503 | Retry-After(5초)만큼 기다렸다가 다시 보내세요 |
| 500, 502 | 한 번쯤 다시 보내 볼 수 있어요. 같은 에러가 계속되면 request_id와 함께 알려 주세요 |
| 그 밖의 4xx | 다시 보내도 같아요. 요청을 고치세요 |
크레딧이 드는 요청을 다시 보낼 때
응답을 못 받았다고 해서 요청이 처리되지 않은 건 아니에요. 생성 요청을 그냥 다시 보내면 두 번 만들고 두 번
차감될 수 있어요. 아래 Idempotency-Key를 함께 보내세요.
한도
| 항목 | 한도 |
|---|---|
| 계정당 분당 호출 | 300회 |
| 계정당 동시에 처리 중인 요청 | 20개 |
| 모델별 호출·토큰·동시 실행 | 모델마다 달라요. 이미지 모델은 계정당 분당 5회 |
| JSON 요청 본문 | 2MB |
| 올리는 파일 | 파일당 50MB, 계정당 분당 20개 |
작업 결과 기다리기(wait) | 한 번에 최대 60초 |
| 바로 오는 호출의 처리 시간 | 약 100초. 더 오래 걸리는 일은 작업으로 처리해요 |
| 결과·파일 링크 | 3일 동안 열림 |
한도는 키가 아니라 계정 단위예요. 같은 계정의 키를 여러 개 써도 한도는 하나로 묶여요.
같은 요청을 두 번 보내지 않게
크레딧이 드는 POST 요청에 Idempotency-Key 헤더를 넣으면, 같은 키로 다시 보낸 요청은 새로 처리하지 않고
처음 응답을 그대로 돌려줘요. 다시 만들지도, 다시 차감하지도 않아요.
curl https://api.devdive.ai/v1/videos \
-H "Authorization: Bearer $DEVDIVE_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 6f1c2a8e-order-1042-video" \
-d '{"prompt": "해 질 녘 바닷가를 달리는 강아지", "duration": 5}'
- 키는 1~255자의 아무 문자열이에요. 요청 하나마다 새 UUID를 만들고, 재시도할 때는 같은 값을 쓰세요.
- 24시간 동안 기억해요. 그 안에 같은 키로 같은 경로·같은 본문을 보내면 처음 응답이 와요. 이때 응답 헤더에
Idempotent-Replayed: true가 붙어요. - 처음 요청이 아직 처리 중이면, 끝날 때까지 기다렸다가 같은 결과를 돌려줘요.
- 같은 키로 다른 본문을 보내면
422 idempotency_key_reused가 와요. - 처음 요청이 에러로 끝났으면 기억하지 않아요. 같은 키로 다시 보내면 새로 처리해요.
- 파일 올리기(
POST /v1/files)는 이 헤더를 보지 않아요. 크레딧이 들지 않으니 그냥 다시 올리면 돼요.
import os
import uuid
import requests
key = str(uuid.uuid4()) # 이 생성 하나에 쓸 키
for attempt in range(3):
try:
res = requests.post(
"https://api.devdive.ai/v1/images",
headers={"Authorization": f"Bearer {os.environ['DEVDIVE_API_KEY']}", "Idempotency-Key": key},
json={"prompt": "눈 덮인 산장과 오로라"},
timeout=120,
)
break
except (requests.ConnectionError, requests.Timeout):
continue # 같은 키로 다시 보내도 두 번 만들지 않아요
타임아웃
- 바로 오는 호출(글, 음성, 분석, 검색)은 모델에 따라 수십 초 걸릴 수 있어요. 클라이언트 타임아웃은 120초 이상으로 두세요.
GET /v1/jobs/{id}?wait=60을 쓴다면 타임아웃은 90초 정도로 두세요.- 타임아웃이 나도 서버에서는 처리가 계속될 수 있어요. 생성 요청이라면
Idempotency-Key로 다시 보내거나, 작업이면 같은 작업 ID로 다시 확인하세요.