Skip to content

Xylolabs Face API 레퍼런스

기본 URL: https://face-api.xylolabs.com

대화형 문서: /docs (Swagger UI) | /redoc (ReDoc)


인증

API 키 (공개 엔드포인트)

FACE_API_API_KEY_ENABLED=true로 설정하면 공개 엔드포인트(/api/v1/detect, /api/v1/mask)에 X-API-Key 헤더가 필요하다.

X-API-Key: xyl_a1b2c3d4e5f6...

API 키는 관리자 API에서 관리한다. 키마다 요청 수와 최근 사용 시각을 추적한다.

API 키 인증을 끄면(기본값) 인증 없이 공개 엔드포인트를 쓸 수 있다.

HTTP Basic Auth (관리자 엔드포인트)

모든 /api/v1/admin/* 엔드포인트는 HTTP Basic 인증이 필요하다.

Authorization: Basic base64(username:password)

인증 정보는 FACE_API_ADMIN_USERFACE_API_ADMIN_PASSWORD 환경 변수로 설정한다. 로그인 실패는 IP별로 추적하고, 10회 연속 실패 시 차단한다.


요청 제한 (Rate Limiting)

엔드포인트 기본 제한 설정 변수
POST /api/v1/detect 60회/분 FACE_API_RATE_LIMIT_DETECT
POST /api/v1/mask 30회/분 FACE_API_RATE_LIMIT_MASK

IP별로 제한한다. 초과하면 429 Too Many RequestsRetry-After 헤더를 반환한다.


공개 엔드포인트

GET /health

서버 상태 확인. 인증 불필요.

응답:

{
  "status": "ok",
  "model": "scrfd_10g",
  "model_loaded": true,
  "input_size": [640, 640],
  "supports_landmarks": true,
  "storage_enabled": false
}

예제

curl https://face-api.xylolabs.com/health

POST /api/v1/detect

이미지에서 얼굴을 감지한다. 바운딩 박스, 랜드마크, 신뢰도 점수가 담긴 JSON을 반환한다.

Content-Type: multipart/form-data

요청 파라미터:

파라미터 타입 기본값 범위 설명
image file 필수 이미지 파일 (JPEG, PNG, WebP, BMP, TIFF)
confidence float 0.5 0.01–1.0 최소 감지 신뢰도
nms_threshold float 0.4 0.01–1.0 NMS(Non-Maximum Suppression) IoU 임계값
max_faces int 0 ≥ 0 반환할 최대 얼굴 수 (0 = 무제한)

응답 (DetectResponse):

{
  "request_id": "550e8400-e29b-41d4-a716-446655440000",
  "image_id": null,
  "job_id": null,
  "image": {
    "width": 1920,
    "height": 1080,
    "channels": 3,
    "size_bytes": 245760
  },
  "detections": [
    {
      "index": 0,
      "bbox": { "x1": 100.5, "y1": 200.3, "x2": 300.1, "y2": 400.7 },
      "confidence": 0.9876,
      "landmarks": {
        "left_eye": { "x": 150.2, "y": 250.1 },
        "right_eye": { "x": 250.3, "y": 248.9 },
        "nose": { "x": 200.1, "y": 310.5 },
        "left_mouth": { "x": 160.7, "y": 360.2 },
        "right_mouth": { "x": 240.5, "y": 358.8 }
      },
      "size": { "width": 199.6, "height": 200.4 },
      "area": 39999.84,
      "center": { "x": 200.3, "y": 300.5 },
      "relative": {
        "x_center": 0.1043,
        "y_center": 0.2782,
        "width": 0.104,
        "height": 0.1855
      }
    }
  ],
  "summary": {
    "face_count": 1,
    "avg_confidence": 0.9876,
    "min_confidence": 0.9876,
    "max_confidence": 0.9876,
    "total_face_area": 39999.84,
    "face_area_ratio": 0.019
  },
  "processing": {
    "model": "scrfd_10g",
    "input_size": [640, 640],
    "confidence_threshold": 0.5,
    "nms_threshold": 0.4,
    "inference_ms": 12.34,
    "total_ms": 18.56
  },
  "profiling": {
    "decode_ms": 3.21,
    "preprocess_ms": 1.05,
    "blob_ms": 0.82,
    "inference_ms": 12.34,
    "postprocess_ms": 0.45,
    "nms_ms": 0.12,
    "mask_ms": null,
    "encode_ms": null,
    "storage_ms": null,
    "total_ms": 18.56,
    "candidates_before_nms": 15,
    "candidates_after_nms": 1,
    "det_scale": 0.333333,
    "input_resolution": "1920x1080",
    "det_resolution": "640x640",
    "input_bytes": 245760,
    "output_bytes": null,
    "input_pixels": 2073600,
    "model_name": "scrfd_10g",
    "model_size_mb": 16.1,
    "onnx_runtime_version": "1.21.0",
    "opencv_version": "4.11.0",
    "python_version": "3.14.3",
    "worker_pid": 12345
  }
}

예제

curl:

curl -X POST https://face-api.xylolabs.com/api/v1/detect \
  -H "X-API-Key: xyl_your_api_key_here" \
  -F "image=@photo.jpg" \
  -F "confidence=0.6" \
  -F "max_faces=5"

Python (requests):

import requests

response = requests.post(
    "https://face-api.xylolabs.com/api/v1/detect",
    headers={"X-API-Key": "xyl_your_api_key_here"},
    files={"image": open("photo.jpg", "rb")},
    params={"confidence": 0.6, "max_faces": 5},
)
data = response.json()
print(f"{data['summary']['face_count']}개의 얼굴 감지")

for face in data["detections"]:
    bbox = face["bbox"]
    print(f"  얼굴 #{face['index']}: 신뢰도={face['confidence']:.4f}, "
          f"영역=({bbox['x1']:.0f},{bbox['y1']:.0f})-({bbox['x2']:.0f},{bbox['y2']:.0f})")

JavaScript (fetch):

const formData = new FormData();
formData.append("image", fileInput.files[0]);

const response = await fetch(
  "https://face-api.xylolabs.com/api/v1/detect?confidence=0.6",
  {
    method: "POST",
    headers: { "X-API-Key": "xyl_your_api_key_here" },
    body: formData,
  }
);
const data = await response.json();
console.log(`${data.summary.face_count}개의 얼굴 감지`);

Python (바이너리 바이트 업로드):

import requests

with open("photo.jpg", "rb") as f:
    image_bytes = f.read()

response = requests.post(
    "https://face-api.xylolabs.com/api/v1/detect",
    headers={"X-API-Key": "xyl_your_api_key_here"},
    files={"image": ("photo.jpg", image_bytes, "image/jpeg")},
)

POST /api/v1/mask

이미지에서 얼굴을 감지하고 마스킹(블러/픽셀화)한다. 처리된 이미지를 바이너리로 반환한다.

Content-Type: multipart/form-data

요청 파라미터:

파라미터 타입 기본값 범위 설명
image file 필수 이미지 파일 (JPEG, PNG, WebP, BMP, TIFF)
confidence float 0.5 0.01–1.0 최소 감지 신뢰도
nms_threshold float 0.4 0.01–1.0 NMS IoU 임계값
max_faces int 0 ≥ 0 마스킹할 최대 얼굴 수 (0 = 전체)
method string gaussian gaussian, pixelate, solid, elliptical 마스킹 방식
strength int 199 1–999 블러 커널 크기 또는 픽셀화 블록 크기
padding float 0.15 0.0–2.0 마스킹 전 바운딩 박스 확장 비율
format string jpeg jpeg, png, webp 출력 이미지 포맷
quality int 95 1–100 출력 압축 품질

응답: 메타데이터 헤더가 붙은 원시 이미지 바이트:

헤더 설명
X-Request-Id 요청 UUID
X-Face-Count 마스킹된 얼굴 수
X-Decode-Ms 이미지 디코딩 시간 (ms)
X-Inference-Ms 모델 추론 시간 (ms)
X-Mask-Ms 마스킹 시간 (ms)
X-Encode-Ms 출력 인코딩 시간 (ms)
X-Total-Ms 총 처리 시간 (ms)
X-Model 모델명
X-Image-Width 원본 이미지 너비
X-Image-Height 원본 이미지 높이
X-Blur-Method 사용된 마스킹 방식
X-Det-Scale 감지 스케일 팩터
X-Image-Id 이미지 UUID (스토리지 활성화 시)
X-Job-Id 작업 UUID (스토리지 활성화 시)

예제

curl (마스킹된 이미지 저장):

curl -X POST https://face-api.xylolabs.com/api/v1/mask \
  -H "X-API-Key: xyl_your_api_key_here" \
  -F "image=@photo.jpg" \
  -F "method=pixelate" \
  -F "strength=20" \
  -F "format=png" \
  -o masked_output.png

Python (바이너리 이미지 다운로드):

import requests

response = requests.post(
    "https://face-api.xylolabs.com/api/v1/mask",
    headers={"X-API-Key": "xyl_your_api_key_here"},
    files={"image": open("photo.jpg", "rb")},
    params={"method": "gaussian", "strength": 99, "format": "jpeg", "quality": 90},
)

# 마스킹된 이미지 저장
with open("masked.jpg", "wb") as f:
    f.write(response.content)

# 헤더에서 메타데이터 읽기
face_count = response.headers.get("X-Face-Count")
total_ms = response.headers.get("X-Total-Ms")
print(f"{face_count}개 얼굴을 {total_ms}ms에 마스킹 완료")

JavaScript (브라우저에서 표시):

const formData = new FormData();
formData.append("image", fileInput.files[0]);

const response = await fetch(
  "https://face-api.xylolabs.com/api/v1/mask?method=pixelate&strength=15",
  {
    method: "POST",
    headers: { "X-API-Key": "xyl_your_api_key_here" },
    body: formData,
  }
);

const blob = await response.blob();
const url = URL.createObjectURL(blob);
document.getElementById("result").src = url;

console.log("마스킹된 얼굴 수:", response.headers.get("X-Face-Count"));

Python (메모리 내 바이트 변환):

import requests
from io import BytesIO
from PIL import Image

# 외부 소스에서 이미지 바이트 읽기
image_bytes = download_from_somewhere()

response = requests.post(
    "https://face-api.xylolabs.com/api/v1/mask",
    headers={"X-API-Key": "xyl_your_api_key_here"},
    files={"image": ("image.jpg", image_bytes, "image/jpeg")},
    params={"method": "elliptical", "padding": 0.3},
)

# 결과를 메모리에서 직접 사용
masked_image = Image.open(BytesIO(response.content))

마스킹 방식

방식 설명
gaussian 가우시안 블러 (기본값). strength로 커널 크기를 정한다.
pixelate 픽셀화 효과. strength로 블록 크기를 정한다.
solid 얼굴 영역을 검은색 사각형으로 덮는다.
elliptical 타원형 마스크를 씌운 가우시안 블러. 자연스럽다.

관리자 엔드포인트

모든 관리자 엔드포인트는 HTTP Basic 인증이 필요하고 /api/v1/admin 경로를 쓴다.

FACE_API_STORAGE_ENABLED=true가 필요하다. 꺼져 있으면 501을 반환한다.

GET /api/v1/admin/stats

전체 통계 조회.

curl -u admin:password https://face-api.xylolabs.com/api/v1/admin/stats

응답:

{
  "total_images": 1250,
  "total_jobs": 3420,
  "total_faces_detected": 8910,
  "avg_inference_ms": 14.52,
  "storage_used_bytes": 524288000,
  "jobs_by_type": { "detect": 2100, "mask": 1320 },
  "jobs_by_method": { "gaussian": 800, "pixelate": 400, "solid": 120 }
}

GET /api/v1/admin/images

저장된 이미지 목록 (페이지네이션).

파라미터 타입 기본값 범위 설명
page int 1 ≥ 1 페이지 번호
per_page int 20 1–100 페이지당 항목 수
curl -u admin:password "https://face-api.xylolabs.com/api/v1/admin/images?page=1&per_page=10"

응답:

{
  "items": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "filename": "photo.jpg",
      "content_type": "image/jpeg",
      "size_bytes": 245760,
      "width": 1920,
      "height": 1080,
      "original_url": "https://static.face-api.xylolabs.com/originals/550e.../photo.jpg",
      "job_count": 3,
      "created_at": "2026-04-06T10:30:00+00:00"
    }
  ],
  "total": 1250,
  "page": 1,
  "per_page": 10,
  "pages": 125
}

GET /api/v1/admin/images/{image_id}

이미지 상세 정보와 관련 작업 목록.

curl -u admin:password https://face-api.xylolabs.com/api/v1/admin/images/550e8400-...

DELETE /api/v1/admin/images/{image_id}

이미지와 관련 작업, S3 객체를 모두 삭제한다. 204 No Content 반환.

curl -u admin:password -X DELETE https://face-api.xylolabs.com/api/v1/admin/images/550e8400-...

GET /api/v1/admin/images/{image_id}/download

원본 이미지의 presigned S3 URL로 리다이렉트(302)한다.

GET /api/v1/admin/jobs

작업 목록 (페이지네이션, 필터 지원).

파라미터 타입 기본값 설명
page int 1 페이지 번호
per_page int 20 페이지당 항목 수 (1–100)
job_type string 필터: detect 또는 mask
method string 블러 방식으로 필터
curl -u admin:password "https://face-api.xylolabs.com/api/v1/admin/jobs?job_type=mask&method=gaussian"

GET /api/v1/admin/jobs/{job_id}

작업 상세 정보 (감지 데이터 포함).

GET /api/v1/admin/jobs/{job_id}/download

처리된 이미지의 presigned S3 URL로 리다이렉트(302)한다.


API 키 관리

API 키 관리를 위한 관리자 엔드포인트다. HTTP Basic 인증과 FACE_API_STORAGE_ENABLED=true가 필요하다.

POST /api/v1/admin/api-keys

새 API 키 생성.

요청 본문 (JSON):

{ "name": "운영 앱" }

응답 (201 Created):

{
  "id": "a1b2c3d4-e5f6-...",
  "key": "xyl_7f3a8b2c1d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e",
  "name": "운영 앱",
  "is_active": true,
  "request_count": 0,
  "last_used_at": null,
  "created_at": "2026-04-06T10:30:00+00:00"
}

전체 키는 평문으로 저장하며, 관리자 API에서 언제든 확인할 수 있다.

예제

curl -u admin:password -X POST \
  https://face-api.xylolabs.com/api/v1/admin/api-keys \
  -H "Content-Type: application/json" \
  -d '{"name": "운영 앱"}'

GET /api/v1/admin/api-keys

API 키 전체 목록과 사용량 조회.

파라미터 타입 기본값 설명
page int 1 페이지 번호
per_page int 20 페이지당 항목 수 (1–100)
curl -u admin:password https://face-api.xylolabs.com/api/v1/admin/api-keys

GET /api/v1/admin/api-keys/{key_id}

특정 API 키 상세 정보와 사용량 조회.

curl -u admin:password https://face-api.xylolabs.com/api/v1/admin/api-keys/a1b2c3d4-...

PATCH /api/v1/admin/api-keys/{key_id}

키 이름 변경 또는 비활성화/재활성화.

요청 본문 (JSON, 모든 필드 선택):

{ "name": "새 이름", "is_active": false }

키 비활성화

curl -u admin:password -X PATCH \
  https://face-api.xylolabs.com/api/v1/admin/api-keys/a1b2c3d4-... \
  -H "Content-Type: application/json" \
  -d '{"is_active": false}'

DELETE /api/v1/admin/api-keys/{key_id}

API 키를 영구 삭제한다. 204 No Content 반환.

curl -u admin:password -X DELETE \
  https://face-api.xylolabs.com/api/v1/admin/api-keys/a1b2c3d4-...

오류 코드

상태 코드 설명
400 잘못된 요청: 지원 안 하는 이미지 포맷, 10x10 미만, 디코딩 실패
401 인증 실패: API 키나 관리자 인증 정보가 없거나 틀림
403 접근 거부: API 키가 비활성 상태
404 없음: 해당 이미지나 작업 ID가 없음
413 크기 초과: 이미지가 20 MB 또는 3천만 픽셀을 넘음
429 요청 제한 초과: Retry-After 헤더 시간만큼 기다린 뒤 재시도
501 스토리지 꺼짐: FACE_API_STORAGE_ENABLED=true 필요
503 서버 바쁨: 동시 요청 수 초과, 1초 후 재시도

오류 응답 형식:

{ "detail": "오류 설명 문자열" }

이미지 제한

제한 항목 기본값 설정 변수
최대 파일 크기 20 MB FACE_API_MAX_IMAGE_BYTES
최대 픽셀 수 30,000,000 FACE_API_MAX_IMAGE_PIXELS
최소 크기 10 x 10 px
지원 포맷 JPEG, PNG, WebP, BMP, TIFF

동시성

설정 기본값 설정 변수
최대 동시 요청 수 10 FACE_API_MAX_CONCURRENT_REQUESTS
최대 병렬 추론 수 2 FACE_API_MAX_CONCURRENT_INFERENCE

최대 동시 요청 수에 도달하면 503 Server busyRetry-After: 1을 반환한다.


설정 레퍼런스

모든 설정은 환경 변수로 구성한다.

핵심 설정

변수 타입 기본값 설명
FACE_API_MODEL string scrfd_10g SCRFD 모델 변형
FACE_API_MODEL_DIR string models .onnx 모델 파일 디렉터리
FACE_API_DET_SIZE int 640 감지 입력 크기 (정사각형)
FACE_API_CONFIDENCE float 0.5 기본 신뢰도 임계값
FACE_API_NMS_THRESHOLD float 0.4 기본 NMS 임계값
FACE_API_BLUR_METHOD string gaussian 기본 마스킹 방식
FACE_API_BLUR_STRENGTH int 199 기본 블러 강도
FACE_API_PADDING float 0.15 기본 바운딩 박스 패딩
FACE_API_MAX_IMAGE_BYTES int 20971520 최대 업로드 크기 (바이트)
FACE_API_MAX_IMAGE_PIXELS int 30000000 최대 이미지 픽셀 수
FACE_API_ORT_THREADS int 0 ONNX Runtime 스레드 수 (0 = 자동)

동시성

변수 타입 기본값 설명
FACE_API_MAX_CONCURRENT_INFERENCE int 2 최대 병렬 추론 호출 수
FACE_API_MAX_CONCURRENT_REQUESTS int 10 최대 동시 요청 수

인증

변수 타입 기본값 설명
FACE_API_API_KEY_ENABLED bool false 공개 엔드포인트에 API 키 인증 활성화
FACE_API_ADMIN_USER string 관리자 사용자명
FACE_API_ADMIN_PASSWORD string 관리자 비밀번호

스토리지 (선택)

변수 타입 기본값 설명
FACE_API_STORAGE_ENABLED bool false DB + S3 저장 활성화
FACE_API_DATABASE_URL string postgresql+asyncpg://... PostgreSQL 연결 문자열
FACE_API_S3_ENDPOINT string http://localhost:9000 S3/MinIO 엔드포인트
FACE_API_S3_ACCESS_KEY string S3 접근 키
FACE_API_S3_SECRET_KEY string S3 비밀 키
FACE_API_S3_BUCKET string face-api S3 버킷 이름
FACE_API_S3_REGION string us-east-1 S3 리전
FACE_API_S3_PUBLIC_ENDPOINT string 저장된 객체의 퍼블릭 URL

네트워크

변수 타입 기본값 설명
FACE_API_CORS_ORIGINS string 허용된 출처 (쉼표 구분)
FACE_API_RATE_LIMIT_DETECT string 60/minute 감지 엔드포인트 요청 제한
FACE_API_RATE_LIMIT_MASK string 30/minute 마스킹 엔드포인트 요청 제한

보안 헤더

모든 응답에 붙는 보안 헤더:

헤더
X-Content-Type-Options nosniff
X-Frame-Options DENY
Referrer-Policy strict-origin-when-cross-origin
Permissions-Policy camera=(), microphone=()
Strict-Transport-Security max-age=63072000; includeSubDomains

응답 스키마

모든 응답 스키마는 app/models.py에 Pydantic 모델로 정의되어 있다. 전체 스키마는 /docs 또는 /redoc에서 확인할 수 있다.