API Documentation (English)¶
Overview¶
Base URL: https://face-api.xylolabs.com
Primary content types:
- multipart/form-data for image upload endpoints
- application/json for admin/auth endpoints
- Binary image responses for the public /api/v1/mask endpoint
Authentication¶
Public endpoints¶
POST /api/v1/detectPOST /api/v1/mask
These endpoints require X-API-Key when FACE_API_API_KEY_ENABLED=true (default: true).
Admin console / admin API¶
Admin authentication modes: - Browser admin console: cookie-backed admin session - Direct admin API clients: HTTP Basic Auth
Browser admin session endpoints:
- POST /api/v1/admin/auth/login
- GET /api/v1/admin/auth/session
- POST /api/v1/admin/auth/logout
Rate limits¶
POST /api/v1/detect:300/minuteby defaultPOST /api/v1/mask:150/minuteby default- Admin login:
10/minuteby default
Session defaults¶
- Admin session TTL:
43200seconds (12 hours) by default - Admin login rate limit:
10/minuteby default
Error format¶
Endpoints¶
GET /health¶
Auth: none
Returns service health and model metadata. The endpoint returns 200 only when the model and any required DB/storage dependencies are ready; otherwise it returns 503.
- Request format: none
- Response format: JSON (
HealthResponse)
import requests
response = requests.get("https://face-api.xylolabs.com/health")
print(response.json())
const response = await fetch("https://face-api.xylolabs.com/health");
console.log(await response.json());
using var client = new HttpClient();
var response = await client.GetAsync("https://face-api.xylolabs.com/health");
Console.WriteLine(await response.Content.ReadAsStringAsync());
{
"status": "ok",
"model": "scrfd_10g",
"model_loaded": true,
"input_size": [640, 640],
"supports_landmarks": true,
"database_enabled": true,
"database_ready": true,
"storage_enabled": true,
"storage_ready": true
}
POST /api/v1/detect¶
Auth: API key
Detects faces and returns JSON.
- Request format:
multipart/form-data - Response format: JSON (
DetectResponse)
Query parameters¶
| Name | Type | Default | Description |
|---|---|---|---|
confidence |
float | 0.5 |
Minimum confidence threshold |
nms_threshold |
float | 0.4 |
NMS IoU threshold |
max_faces |
int | 0 |
0 means no limit |
Example¶
curl -X POST "https://face-api.xylolabs.com/api/v1/detect?confidence=0.6&max_faces=5" \
-H "X-API-Key: xyl_your_api_key_here" \
-F "image=@photo.jpg"
import requests
response = requests.post(
"https://face-api.xylolabs.com/api/v1/detect?confidence=0.6&max_faces=5",
headers={"X-API-Key": "xyl_your_api_key_here"},
files={"image": open("photo.jpg", "rb")},
)
print(response.json())
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&max_faces=5", {
method: "POST",
headers: { "X-API-Key": "xyl_your_api_key_here" },
body: formData,
});
console.log(await response.json());
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("X-API-Key", "xyl_your_api_key_here");
using var content = new MultipartFormDataContent();
content.Add(new ByteArrayContent(File.ReadAllBytes("photo.jpg")), "image", "photo.jpg");
var response = await client.PostAsync(
"https://face-api.xylolabs.com/api/v1/detect?confidence=0.6&max_faces=5",
content
);
Console.WriteLine(await response.Content.ReadAsStringAsync());
Response shape¶
request_id: request UUIDimage: input image metadatadetections[]: bbox, landmarks, size, area, center, relative positionsummary: face count and confidence aggregatesprocessing: model + threshold/timing summaryprofiling: detailed timing breakdown
POST /api/v1/mask¶
Auth: API key
Detects and masks faces, then returns the processed image as binary bytes.
- Request format:
multipart/form-data - Response format: binary image (
image/jpeg,image/png, orimage/webp)
Query parameters¶
| Name | Type | Default | Description |
|---|---|---|---|
confidence |
float | 0.5 |
Minimum confidence threshold |
nms_threshold |
float | 0.4 |
NMS IoU threshold |
max_faces |
int | 0 |
0 means no limit |
method |
enum | gaussian |
gaussian, pixelate, solid, elliptical |
strength |
int | 199 |
Blur/pixelation strength |
padding |
float | 0.15 |
Expand bbox before masking |
format |
enum | jpeg |
jpeg, png, webp |
quality |
int | 95 |
Output quality |
Example¶
curl -X POST "https://face-api.xylolabs.com/api/v1/mask?method=pixelate&strength=32&format=webp" \
-H "X-API-Key: xyl_your_api_key_here" \
-F "image=@photo.jpg" \
-o masked.webp
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("X-API-Key", "xyl_your_api_key_here");
using var content = new MultipartFormDataContent();
content.Add(new ByteArrayContent(File.ReadAllBytes("photo.jpg")), "image", "photo.jpg");
var response = await client.PostAsync(
"https://face-api.xylolabs.com/api/v1/mask?method=pixelate&strength=32&format=webp",
content
);
await using var output = File.Create("masked.webp");
await response.Content.CopyToAsync(output);
import requests
response = requests.post(
"https://face-api.xylolabs.com/api/v1/mask?method=pixelate&strength=32&format=webp",
headers={"X-API-Key": "xyl_your_api_key_here"},
files={"image": open("photo.jpg", "rb")},
)
with open("masked.webp", "wb") as f:
f.write(response.content)
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=32&format=webp", {
method: "POST",
headers: { "X-API-Key": "xyl_your_api_key_here" },
body: formData,
});
const blob = await response.blob();
Response¶
Binary image bytes with metadata headers such as:
- X-Request-Id
- X-Face-Count
- X-Inference-Ms
- X-Mask-Ms
- X-Encode-Ms
- X-Total-Ms
- X-Model
- X-Image-Id / X-Job-Id when persistence is enabled
Admin auth/session endpoints¶
POST /api/v1/admin/auth/login¶
Auth: none
Creates a cookie-backed admin session.
- Request format: JSON
- Response format: empty body (
204 No Content) +Set-Cookie
curl -X POST https://face-api.xylolabs.com/api/v1/admin/auth/login \
-H "Content-Type: application/json" \
-d '{"username":"admin","password":"secret"}' \
-c cookies.txt -i
import requests
session = requests.Session()
response = session.post(
"https://face-api.xylolabs.com/api/v1/admin/auth/login",
json={"username": "admin", "password": "secret"},
)
print(response.status_code)
const response = await fetch("https://face-api.xylolabs.com/api/v1/admin/auth/login", {
method: "POST",
headers: { "Content-Type": "application/json" },
credentials: "include",
body: JSON.stringify({ username: "admin", password: "secret" }),
});
console.log(response.status);
using System.Text;
using var client = new HttpClient();
using var content = new StringContent("{\"username\":\"admin\",\"password\":\"secret\"}", Encoding.UTF8, "application/json");
var response = await client.PostAsync("https://face-api.xylolabs.com/api/v1/admin/auth/login", content);
Console.WriteLine(response.StatusCode);
Success: 204 No Content + Set-Cookie
GET /api/v1/admin/auth/session¶
Auth: cookie-backed admin session or HTTP Basic Auth
- Request format: none
- Response format: JSON (
AdminSessionResponse)
import requests
from requests.auth import HTTPBasicAuth
response = requests.get(
"https://face-api.xylolabs.com/api/v1/admin/auth/session",
auth=HTTPBasicAuth("admin", "password"),
)
print(response.json())
const basic = btoa("admin:password");
const response = await fetch("https://face-api.xylolabs.com/api/v1/admin/auth/session", {
headers: { Authorization: `Basic ${basic}` },
});
console.log(await response.json());
using System.Net.Http.Headers;
using System.Text;
using var client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(
"Basic",
Convert.ToBase64String(Encoding.ASCII.GetBytes("admin:password"))
);
var response = await client.GetAsync("https://face-api.xylolabs.com/api/v1/admin/auth/session");
Console.WriteLine(await response.Content.ReadAsStringAsync());
POST /api/v1/admin/auth/logout¶
Auth: none
Clears the cookie-backed admin session and returns 204.
- Request format: none
- Response format: empty body (
204 No Content)
import requests
session = requests.Session()
response = session.post("https://face-api.xylolabs.com/api/v1/admin/auth/logout")
print(response.status_code)
const response = await fetch("https://face-api.xylolabs.com/api/v1/admin/auth/logout", {
method: "POST",
credentials: "include",
});
console.log(response.status);
using var client = new HttpClient();
var response = await client.PostAsync("https://face-api.xylolabs.com/api/v1/admin/auth/logout", content: null);
Console.WriteLine(response.StatusCode);
Admin endpoints¶
All admin endpoints are under /api/v1/admin and require either a valid cookie-backed admin session or HTTP Basic Auth.
GET /api/v1/admin/stats¶
Returns aggregate stats across images/jobs. Requires storage-backed mode; returns 501 when FACE_API_STORAGE_ENABLED=false.
- Request format: none
- Response format: JSON (
StatsResponse) - Detailed examples: see Admin API
GET /api/v1/admin/images¶
Returns a paginated image list. Requires storage-backed mode; returns 501 when FACE_API_STORAGE_ENABLED=false.
- Request format: none (query string only)
- Response format: JSON (
PaginatedResponse[StoredImageResponse]) - Detailed examples: see Admin API
Response items include:
- original_url (private original download URL)
- processed_url (latest processed output when present)
- dimensions, size, job count, created timestamp
If no processed result exists yet, processed_url is null.
When FACE_API_S3_PUBLIC_ENDPOINT is configured, processed_url may point at the public processed-asset host.
GET /api/v1/admin/images/{image_id}¶
Returns image details together with the related jobs.
- Request format: none
- Response format: JSON (
StoredImageDetailResponse) - Detailed examples: see Admin API
DELETE /api/v1/admin/images/{image_id}¶
Deletes image metadata and registers tracked storage-cleanup tasks. Immediate object cleanup is attempted, and any failed cleanup remains queued for a later bootstrap or maintenance run instead of being silently forgotten. Returns 204.
- Request format: none
- Response format: empty body (
204 No Content) - Detailed examples: see Admin API
GET /api/v1/admin/images/{image_id}/download¶
Redirect to the original image URL.
- Request format: none
- Response format: redirect (
302 Found) - Detailed examples: see Admin API
GET /api/v1/admin/jobs¶
Returns a paginated job list with optional job_type and method filters. Requires storage-backed mode; returns 501 when FACE_API_STORAGE_ENABLED=false.
- Request format: none (query string only)
- Response format: JSON (
PaginatedResponse[JobResponse]) - Detailed examples: see Admin API
GET /api/v1/admin/jobs/{job_id}¶
Returns job details, including the detection payload and processed URL.
- Request format: none
- Response format: JSON (
JobDetailResponse) - Detailed examples: see Admin API
GET /api/v1/admin/jobs/{job_id}/download¶
Redirect to the processed image URL.
- Request format: none
- Response format: redirect (
302 Found) - Detailed examples: see Admin API
POST /api/v1/admin/test/mask¶
Returns the admin-only preview payload used by the console test page.
Returns JSON, not binary, with:
- all DetectResponse-style fields
- masking
- output.image_base64
- Request format:
multipart/form-data - Response format: JSON (
MaskResponse) - Detailed examples: see Admin Console
POST /api/v1/admin/api-keys¶
Creates a new API key. Requires the database-backed mode; returns 501 when the database tier is disabled.
Response returns the full key once at creation time.
- Request format: JSON
- Response format: JSON (
ApiKeyCreatedResponse) - Detailed examples: see API Key Management
GET /api/v1/admin/api-keys¶
Returns key metadata only. Requires the database-backed mode; returns 501 when the database tier is disabled.
- Request format: none (query string only)
Fields include:
- id
- name
- key_prefix
- key_suffix
- key_preview
- is_active
- request_count
- last_used_at
- created_at
- Response format: JSON (
PaginatedResponse[ApiKeyResponse]) - Detailed examples: see API Key Management
GET /api/v1/admin/api-keys/{key_id}¶
Returns key metadata only. Requires the database-backed mode; returns 501 when the database tier is disabled.
- Request format: none
- Response format: JSON (
ApiKeyResponse) - Detailed examples: see API Key Management
PATCH /api/v1/admin/api-keys/{key_id}¶
Updates a key name or activation state. Requires the database-backed mode; returns 501 when the database tier is disabled.
- Request format: JSON
- Response format: JSON (
ApiKeyResponse) - Detailed examples: see API Key Management
DELETE /api/v1/admin/api-keys/{key_id}¶
Deletes a key. Requires the database-backed mode; returns 501 when the database tier is disabled. Returns 204 on success.
- Request format: none
- Response format: empty body (
204 No Content) - Detailed examples: see API Key Management
Schemas¶
DetectResponse¶
{
"request_id": "uuid",
"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"
}
}
Admin mask preview response¶
MaskResponse extends DetectResponse with:
{
"masking": {
"method": "gaussian",
"strength": 199,
"padding": 0.15,
"regions_masked": 1
},
"output": {
"format": "jpeg",
"quality": 95,
"size_bytes": 123456,
"image_base64": "..."
}
}
PaginatedResponse¶
Detection models¶
| Model | Notes |
|---|---|
scrfd_10g |
highest accuracy, larger CPU cost |
scrfd_2.5g |
balanced option |
scrfd_500m |
smallest / fastest, lower accuracy |
Code examples¶
Python (requests)¶
import requests
with open("photo.jpg", "rb") as f:
response = requests.post(
"https://face-api.xylolabs.com/api/v1/detect?confidence=0.6",
headers={"X-API-Key": "xyl_your_api_key_here"},
files={"image": f},
)
print(response.json()["summary"]["face_count"])
JavaScript (fetch)¶
const formData = new FormData();
formData.append("image", fileInput.files[0]);
const response = await fetch(
"https://face-api.xylolabs.com/api/v1/mask?method=gaussian",
{
method: "POST",
headers: { "X-API-Key": "xyl_your_api_key_here" },
body: formData,
}
);
const blob = await response.blob();
C# (HttpClient)¶
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("X-API-Key", "xyl_your_api_key_here");
using var detectContent = new MultipartFormDataContent();
detectContent.Add(new ByteArrayContent(File.ReadAllBytes("photo.jpg")), "image", "photo.jpg");
var detectResponse = await client.PostAsync(
"https://face-api.xylolabs.com/api/v1/detect?confidence=0.6",
detectContent
);
Console.WriteLine(await detectResponse.Content.ReadAsStringAsync());
using var maskContent = new MultipartFormDataContent();
maskContent.Add(new ByteArrayContent(File.ReadAllBytes("photo.jpg")), "image", "photo.jpg");
var maskResponse = await client.PostAsync(
"https://face-api.xylolabs.com/api/v1/mask?method=gaussian",
maskContent
);
await using var output = File.Create("masked.jpg");
await maskResponse.Content.CopyToAsync(output);