Skip to content

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/detect
  • POST /api/v1/mask

These endpoints require X-API-Key when FACE_API_API_KEY_ENABLED=true (default: true).

X-API-Key: xyl_your_api_key_here

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

Authorization: Basic base64(username:password)

Rate limits

  • POST /api/v1/detect: 300/minute by default
  • POST /api/v1/mask: 150/minute by default
  • Admin login: 10/minute by default

Session defaults

  • Admin session TTL: 43200 seconds (12 hours) by default
  • Admin login rate limit: 10/minute by default

Error format

{ "detail": "message" }

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)
curl https://face-api.xylolabs.com/health
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 UUID
  • image: input image metadata
  • detections[]: bbox, landmarks, size, area, center, relative position
  • summary: face count and confidence aggregates
  • processing: model + threshold/timing summary
  • profiling: 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, or image/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
{ "username": "admin", "password": "secret" }
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)
curl -u admin:password https://face-api.xylolabs.com/api/v1/admin/auth/session
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());
{
  "authenticated": true,
  "username": "admin"
}

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)
curl -X POST https://face-api.xylolabs.com/api/v1/admin/auth/logout -b cookies.txt -i
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.

{ "name": "Production App" }

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.

{ "name": "Renamed App", "is_active": false }
  • 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

{
  "items": [],
  "total": 0,
  "page": 1,
  "per_page": 20,
  "pages": 1
}

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);