Python · 주얼리 try-on API
주얼리 try-on API Python
한 줄 요약
requests를 설치하고 환경 변수에서 PHOTTA_API_KEY를 로드한 뒤, Bearer 키를 전송하고 2xx 이외의 응답에 타입 정의된 PhottaError를 발생시키는 photta() 래퍼를 작성하세요. 그런 다음 /api/v1/tryon/jewelry로 POST 요청을 보내고 status가 completed가 될 때까지 3초마다 /api/v1/tryon/jewelry/{id}를 폴링합니다.
- 일반적인 소요 시간: 1.5–4 min
- 크레딧: 2K에서 5 크레딧 · 4K에서 6 크레딧
- 처리 방식: 비동기, 폴링
- 제한 사항: 600 requests/min
- 인증: Bearer 키
이 페이지의 모든 수치는 다음 출처를 기반으로 합니다: Photta API 문서 · 확인 완료
RESPONSE=$(curl -s -X POST https://api.photta.app/api/v1/tryon/jewelry \
-H "Authorization: Bearer $PHOTTA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"jewelry_image": "https://cdn.yourstore.com/products/gold-necklace.jpg",
"jewelry_type": "necklace",
"mannequin_id": "mnk_j9k1l2",
"background": "soft window light on dark velvet",
"resolution": "2K",
"aspect_ratio": "3:4",
"user_instruction": "Make the pendant 10% smaller."
}')
ID=$(echo "$RESPONSE" | jq -r '.data.id')
# $ID is data.id from the POST response. Poll every 3 seconds until status is "completed" or "failed".
for i in $(seq 1 120); do
RESULT=$(curl -s "https://api.photta.app/api/v1/tryon/jewelry/$ID" -H "Authorization: Bearer $PHOTTA_API_KEY")
STATUS=$(echo "$RESULT" | jq -r '.data.status')
[ "$STATUS" = "completed" ] && break
[ "$STATUS" = "failed" ] && echo "generation failed" && exit 1
sleep 3
done
echo "$RESULT" | jq -r '.data.output_url'const res = await fetch("https://api.photta.app/api/v1/tryon/jewelry", {
method: "POST",
headers: { Authorization: `Bearer ${process.env.PHOTTA_API_KEY}`, "Content-Type": "application/json" },
body: JSON.stringify({
"jewelry_image": "https://cdn.yourstore.com/products/gold-necklace.jpg",
"jewelry_type": "necklace",
"mannequin_id": "mnk_j9k1l2",
"background": "soft window light on dark velvet",
"resolution": "2K",
"aspect_ratio": "3:4",
"user_instruction": "Make the pendant 10% smaller."
}),
});
const { data } = await res.json(); // data.id, data.status
async function waitFor(id) {
for (let attempt = 0; attempt < 120; attempt++) {
const res = await fetch(`https://api.photta.app/api/v1/tryon/jewelry/${id}`, { headers: { Authorization: `Bearer ${process.env.PHOTTA_API_KEY}` } });
const { data } = await res.json();
if (data.status === "completed") return data.output_url;
if (data.status === "failed") throw new Error(data.error_message ?? "generation failed");
await new Promise((r) => setTimeout(r, 3000));
}
throw new Error("timed out after 6 minutes");
}import os, requests
res = requests.post(
"https://api.photta.app/api/v1/tryon/jewelry",
headers={"Authorization": f"Bearer {os.environ['PHOTTA_API_KEY']}"},
json={
"jewelry_image": "https://cdn.yourstore.com/products/gold-necklace.jpg",
"jewelry_type": "necklace",
"mannequin_id": "mnk_j9k1l2",
"background": "soft window light on dark velvet",
"resolution": "2K",
"aspect_ratio": "3:4",
"user_instruction": "Make the pendant 10% smaller."
},
)
data = res.json()["data"] # data["id"], data["status"]
import time
def wait_for(job_id):
for _ in range(120):
res = requests.get(f"https://api.photta.app/api/v1/tryon/jewelry/{job_id}", headers={"Authorization": f"Bearer {os.environ['PHOTTA_API_KEY']}"})
data = res.json()["data"]
if data["status"] == "completed":
return data["output_url"]
if data["status"] == "failed":
raise RuntimeError(data.get("error_message", "generation failed"))
time.sleep(3)
raise TimeoutError("timed out after 6 minutes")한눈에 보기
한눈에 보기
- 엔드포인트
- POST /api/v1/tryon/jewelry
- 인증
- Bearer API 키
- 처리 방식
- 비동기 — 상태 엔드포인트 폴링
- 일반적인 소요 시간
- 1.5–4 min
- 크레딧
- 2K에서 5 크레딧 · 4K에서 6 크레딧
- 화면비
- 3:4 · 1:1 · 4:3 · 9:16 · 16:9
- 입력 제한
- 이미지당 최대 10 MB; JPEG, PNG, WebP
- 멱등성
- 아니요
동작 원리
5단계 구성, Photta 전용 의존성 제로 — requests만으로 모든 작업이 가능합니다.
단계 1
회원가입 및 키 생성
다른 언어와 동일한 경로: ai.photta.app → Developers → Generate API key. 키는 `photta_live_`로 시작하며 모든 Photta 기능에 동작합니다.
단계 2
환경 변수에서 키 로드
환경 변수에 `PHOTTA_API_KEY`를 설정하고 `os.environ`을 통해 읽어옵니다. FastAPI나 Django에서는 부팅 시 설정 모듈로 한 번 로드하세요. 소스 코드나 공개 Git 기록에 키를 직접 포함하지 마세요.
python3 -m pip install requests export PHOTTA_API_KEY="photta_live_xxxxx"단계 3
requests 래퍼 작성
30줄짜리 `photta_request()` 함수 하나로 기본 URL, Authorization 헤더, JSON 직렬화, 에러 변환까지 모두 처리합니다. Python SDK가 출시되기 전까지 필요한 유일한 추상화입니다.
# photta.py — one helper for every endpoint import os, requests BASE = "https://api.photta.app/api/v1" class PhottaError(Exception): def __init__(self, status, error, retry_after=None): super().__init__(error.get("message", "request failed")) self.status, self.type, self.retry_after = status, error.get("type"), retry_after def photta(method, path, **kwargs): res = requests.request(method, BASE + path, headers={"Authorization": f"Bearer {os.environ['PHOTTA_API_KEY']}"}, timeout=30, **kwargs) body = res.json() if not res.ok: raise PhottaError(res.status_code, body.get("error", {}), res.headers.get("Retry-After")) return body단계 4
요청 제출 및 polling
`/api/v1/tryon/jewelry`로 POST 요청을 보내 generation ID를 받은 다음, 3초 간격으로 `/api/v1/tryon/jewelry/{id}`를 polling하세요. Celery나 RQ 환경에서는 polling 루프를 여러 태스크로 분산하여 몇 분씩 걸리는 작업이 워커를 점유하지 않도록 하세요.
import os, requests res = requests.post( "https://api.photta.app/api/v1/tryon/jewelry", headers={"Authorization": f"Bearer {os.environ['PHOTTA_API_KEY']}"}, json={ "jewelry_image": "https://cdn.yourstore.com/products/gold-necklace.jpg", "jewelry_type": "necklace", "mannequin_id": "mnk_j9k1l2", "background": "soft window light on dark velvet", "resolution": "2K", "aspect_ratio": "3:4", "user_instruction": "Make the pendant 10% smaller." }, ) data = res.json()["data"] # data["id"], data["status"] import time def wait_for(job_id): for _ in range(120): res = requests.get(f"https://api.photta.app/api/v1/tryon/jewelry/{job_id}", headers={"Authorization": f"Bearer {os.environ['PHOTTA_API_KEY']}"}) data = res.json()["data"] if data["status"] == "completed": return data["output_url"] if data["status"] == "failed": raise RuntimeError(data.get("error_message", "generation failed")) time.sleep(3) raise TimeoutError("timed out after 6 minutes")단계 5
결과 저장
완료된 payload에는 `output_url`과 `thumbnail_url`이 포함됩니다. 장기 렌더링 측면에서 프로덕트가 Photta의 CDN에 종속되지 않도록 바이트 데이터를 자체 오브젝트 스토리지(S3, GCS, Cloudflare R2)에 다운로드하세요.
오류
- 400
invalid_request_error - The request is malformed or contains invalid parameters.
- 401
authentication_error - The API key is missing, invalid, or expired.
- 402
insufficient_credits_error - Your account does not have enough credits.
- 404
not_found_error - The requested resource does not exist.
- 409
conflict_error - The request conflicts with current state — e.g. your concurrent-batch cap is reached.
- 429
rate_limit_error - Too many requests. Slow down and retry.Retry-After: 30
- 500
api_error - An internal server error occurred. Retry later.
try:
photta("POST", "/tryon/jewelry", json={
"jewelry_image": "https://cdn.yourstore.com/products/gold-necklace.jpg",
"jewelry_type": "necklace",
"mannequin_id": "mnk_j9k1l2",
"background": "soft window light on dark velvet",
"resolution": "2K",
"aspect_ratio": "3:4",
"user_instruction": "Make the pendant 10% smaller."
})
except PhottaError as e:
if e.status == 402:
print("insufficient_credits_error: top up before retrying")
elif e.status == 429:
time.sleep(int(e.retry_after or 30))
else:
raise제한 사항
- API 키당 분당 600회 요청 (고정 윈도우 방식이며, 제한은 모든 요금제에서 동일합니다)
- 429 응답에는 Retry-After: 30초 헤더가 포함됩니다
- 이미지당 최대 10 MB
- 지원 형식: JPEG, PNG, WebP
- 요금제별 동시 배치 수: Free 1 · Hobby 2 · Seller 3 · Brand 4 · Studio 6
- 폴링 전용 — API는 웹훅을 전송하지 않습니다.
현재 requests가 최적의 선택인 이유
- Photta 전용 의존성 없음 — requests는 이미 대부분의 Python 프로젝트에 포함되어 있습니다
- Python이 구동되는 모든 환경 지원: Django, FastAPI, Flask, Celery 워커, Lambda, 스크립트
- 클라이언트 래퍼는 약 30줄에 불과합니다 — photta.py에 붙여넣고 바로 사용하세요
- 병렬 요청이 필요할 경우 httpx를 통해 asyncio와 결합 가능
지원하지 않는 항목
- 아직 공식 Photta Python 패키지는 없습니다 — requests를 사용한 REST 호출이 문서화된 표준 방식입니다
- 완료 감지는 오직 polling으로만 가능합니다. API는 웹훅을 전송하지 않습니다
- Bearer 키 방식만 지원합니다. OAuth client-credentials 플로우는 없습니다
개발자가 자주 묻는 질문
공식 Photta Python SDK가 있나요?
아직 없습니다. 공식 지원 연동 방식은 requests 라이브러리(동기) 또는 httpx(비동기)입니다. 둘 다 간단한 래퍼 함수 하나면 충분하며, 이 페이지의 래퍼는 모든 기능에 동작합니다.
어떤 Python 버전을 지원하나요?
Python 3.9 이상을 지원합니다. 해당 래퍼는 3.10+ 환경에서 유효한 타입 힌트, f-string, `str | None` 문법을 사용합니다. 3.9에서는 `str | None`을 `Optional[str]`로 변경하세요. requests 자체는 공식적으로 3.8+를 지원하지만, 현대적인 Photta 프로젝트 기준 실질적 최소 사양은 3.9+입니다.
requests와 httpx 중 무엇을 사용해야 하나요?
순차적인 호출을 몇 번만 수행한다면 requests를 권장합니다. 더 단순하고 이미 대부분의 코드베이스에 존재하기 때문입니다. 비동기 동시성(병렬 요청, 대량 백필)이나 HTTP/2가 필요하다면 httpx를 사용하세요. 어느 쪽이든 래퍼는 매우 단순하며, `requests.request`를 `httpx.Client().request`로 바꾸기만 하면 나머지 코드는 동일하게 유지됩니다.
Django에서 호출할 수 있나요?
네. 래퍼를 `services/photta.py`에 두고 view나 Celery 태스크에서 import해 사용하세요. 작업 처리 시간이 길다면 동기 요청 핸들러 내에서 API를 직접 호출하지 마세요. view에서는 요청만 제출하고 generation ID를 반환한 뒤, 백그라운드 태스크나 Channels consumer에서 polling하세요.
Celery 워커에서 어떻게 polling하나요?
한 태스크에서 작업을 제출한 다음, 단일 polling 후 작업이 완료되거나 최대 재시도 횟수에 도달할 때까지 자신을 다시 스케줄링하는 후속 태스크를 예약(`apply_async(countdown=3)`)하세요. 이렇게 하면 워커를 유휴 상태로 유지하고 오토스케일러에 문제를 일으키는 장기 실행 태스크를 방지할 수 있습니다.
API는 에러를 어떻게 전달하나요?
2xx 이외의 응답은 `error` 객체(`type`, `code`, `message`, 400의 경우 `param`, 429의 경우 `retry_after`)가 포함된 JSON 본문을 반환합니다. 이 페이지의 래퍼는 이 모든 정보를 담은 `PhottaError`를 발생시키므로 호출부에서 본문을 직접 파싱하지 않고도 상태 코드별로 분기 처리할 수 있습니다.
관련 페이지
Node.js
Node.js 환경의 주얼리 try-on API
네이티브 fetch를 사용하여 Node.js 백엔드에서 Photta의 주얼리 try-on API 기능을 호출하세요. SDK는 필요 없습니다: 키를 저장하고 작업을 제출한 뒤 결과를 폴링하면 됩니다.
읽기cURL
cURL 기반 주얼리 try-on API
cURL을 사용해 셸에서 Photta의 주얼리 try-on API 기능을 직접 호출하세요. 제출, polling, 다운로드까지 — 모든 스니펫은 스크립트 파이프라인 구축을 위해 jq와 연동됩니다.
읽기기능
주얼리 try-on API 목걸이, 반지, 팔찌 및 귀걸이 지원
REST 방식으로 AI 모델에 목걸이, 반지, 팔찌, 귀걸이를 착용해 보세요. 2K 기준 5 credits, 내장 클로즈업 마네킹 제공, 반사광을 정밀하게 반영하는 Photta 공식 렌더링 파이프라인을 활용하세요.
읽기빌드
AI 제품 사진 촬영 서비스 구축하기
Photta의 가상 try-on API를 화이트라벨 SaaS로 재판매하기 위한 창업자 플레이북: 테넌트 키, 배치 fanout, credit별 결제, 현실적인 SLA.
읽기빠른 시작
5분 안에 첫 번째 Photta 이미지를 생성하세요.
회원가입, API 키 생성, 첫 cURL 호출 실행, 완료될 때까지 폴링, 결과 저장까지. 엔드투엔드로 문서화된 5단계 가이드입니다.
읽기레퍼런스
Photta API 가격 정책
2K 및 4K에서 각 Photta API endpoint에 소요되는 크레딧 비용, 과금 및 환불 방식, 그리고 무료 endpoint 목록입니다. 이미지당 별도의 현금 가격은 없습니다.
읽기
이 페이지의 모든 수치는 다음 출처를 기반으로 합니다: Photta API 문서 · 확인 완료
5분 만에 API 체험하기
Photta API 키를 발급받고 위 cURL 스니펫을 붙여넣기만 하면, 커피가 다 내려지기도 전에 첫 번째 모델 착용 컷을 받아볼 수 있습니다.
- 1.5–4 min
- 2K에서 5 크레딧 · 4K에서 6 크레딧
- 비동기, 폴링
- 600 requests/min
- Bearer 키