Liveness Detection API Reference
This is the complete endpoint specification for the Liveness Detection (anti-spoofing) API (Liveness Check API). If you are using the API for the first time, please read the Quickstart Guide first.
Basic Information
| Item | Value |
|---|---|
| API base URL | https://liveness.api.pas-ta.io |
| Customer dashboard | https://liveness.api.pas-ta.io/console/dashboard |
| Content type | application/json |
| Character encoding | UTF-8 |
| API version (URL path) | v1 |
Authentication
This API supports two authentication methods.
| Target | Method | Purpose |
|---|---|---|
Detection API (/v1/*) |
X-API-Key header |
Send detection requests from the Customer’s servers or applications |
Customer dashboard (/console/*) |
Cookie session (browser only) | Manage your account, keys, and plan on the dashboard screens (not intended for direct programmatic calls) |
For a detection request, include the API key obtained from the dashboard (a string beginning with lc_) in the X-API-Key header.
X-API-Key: lc_abc12xxxxxxxxxxxxxxxxxxxxxxxx
The
/console/*endpoints are a screen-only backend authenticated with HttpOnly cookies issued when you log in to the dashboard. They are not designed to be called directly from your programs. See the Customer Console Guide for what you can do on the screens, including sign-in, API key management, and plan settings.
Common Response Structure
All endpoints except /v1/health return a common structure consisting of metadata and data (or error).
On success:
{
"metadata": {
"date": "2026-05-04T12:34:56.789Z",
"ver": "1.0.1",
"customer_id": "550e8400-e29b-41d4-a716-446655440000",
"remaining_calls": 3850,
"over_limit_calls": 0,
"status": "success"
},
"data": { }
}
On error:
{
"metadata": { "...": "...", "status": "error" },
"error": {
"code": "invalid_image",
"message": "No face was detected"
}
}
metadata Fields
| Field | Type | Description |
|---|---|---|
date |
string | Response generation timestamp (ISO 8601, UTC, millisecond precision) |
ver |
string | The applied API version (semantic version, e.g., "1.0.1") |
customer_id |
string | null | Customer UUID (null when authentication fails) |
remaining_calls |
integer | null | Remaining credits for the current month (never goes below 0; clipped at 0) |
over_limit_calls |
integer | null | Number of overage calls for the current month (always 0 on the free plan or when the hard cap is ON) |
status |
string | "success" or "error" |
The monthly boundary is based on UTC. The current-month tally begins at 00:00:00 UTC on the 1st of each month, and credits expire at 23:59:59 UTC at the end of the month (consistent with the AWS Marketplace billing-month boundary).
Billing (Call Counting)
Only successful responses (status: "success") of POST /v1/check/liveness consume your monthly credits (i.e., are counted as calls). Everything else is free.
| Endpoint | Billing |
|---|---|
POST /v1/check/liveness — successful response |
Consumes 1 call (billable) |
POST /v1/check/liveness — error response (4xx / 5xx) |
Free (not counted) |
GET /v1/usage |
Free |
GET /v1/health |
Free |
/console/* (all dashboard-related) |
Free |
Error responses such as
invalid_image,429, and5xxare not counted toward your monthly calls. Only successful responses that return a detection result are billable.
Detection API (/v1/*)
POST /v1/check/liveness — Liveness Detection
Send a Base64-encoded face image to determine whether it is a photo or a spoofing attack.
Authentication: X-API-Key header required
Billing: Only successful responses consume 1 call (error responses are not counted)
Request
{
"image": "<Base64-encoded image>",
"image_format": "jpeg"
}
| Field | Type | Required | Description |
|---|---|---|---|
image |
string | ○ | Base64-encoded image data (maximum 10 MB) |
image_format |
string | ○ | "jpeg" or "png" |
Response (200 OK)
{
"metadata": {
"date": "2026-05-04T12:34:56.789Z",
"ver": "1.0.1",
"customer_id": "550e8400-e29b-41d4-a716-446655440000",
"remaining_calls": 3850,
"over_limit_calls": 0,
"status": "success"
},
"data": {
"isFakeFace": false,
"isFakeLikelihood": 0.987,
"isValidFace": true,
"faceBrightness": 142.3,
"faceRectArea": {"x": 120, "y": 80, "width": 200, "height": 200},
"vFaceRect": [
{"x": 120, "y": 80, "width": 200, "height": 200}
],
"msg": "",
"latency_ms": 120
}
}
data field |
Type | Description |
|---|---|---|
isFakeFace |
boolean | true = suspected photo or spoofing; false = live subject. Becomes true when isFakeLikelihood exceeds the default threshold of 0.15 |
isFakeLikelihood |
float | Spoofing likelihood (detection score from 0.0 to 1.0) |
isValidFace |
boolean | Whether the face can be treated as a valid detection target |
faceBrightness |
float | Average brightness of the face region |
faceRectArea |
object | The adopted face rectangle {x, y, width, height} |
vFaceRect |
array | List of all detected face rectangles |
msg |
string | Supplementary message |
latency_ms |
integer | Inference latency (milliseconds) |
How to interpret the Processing Result:
isFakeFaceis a reference verdict that becomestruewhenisFakeLikelihoodexceeds the default threshold of 0.15 (the threshold may change as detection accuracy improves; changes will be announced in the Changelog). For stricter operation, evaluateisFakeLikelihood(0.0–1.0) against a threshold on the Customer’s side. By adjusting the threshold to suit your use case, you can optimize the balance between false positives and false negatives.
Errors
| HTTP | error.code |
Condition |
|---|---|---|
| 400 | invalid_request |
Validation error in the request body |
| 400 | invalid_image |
Image decode failure or no face detected |
| 401 | invalid_api_key |
Invalid, deactivated, or expired key |
| 429 | quota_exceeded |
Limit reached and in API-stop mode (free plan, or a paid plan with the hard cap ON) |
| 500 | internal_error |
Internal server error |
| 503 | service_unavailable |
Temporary service unavailability |
GET /v1/usage — Check Current-Month Usage
Authentication: X-API-Key header required
Billing: Free (not counted toward your monthly calls)
Request
curl https://liveness.api.pas-ta.io/v1/usage \
-H "X-API-Key: {your API key}"
Response (200 OK)
{
"metadata": { "...": "...", "status": "success" },
"data": {
"year_month": "2026-05",
"total_calls": 1150,
"monthly_limit": 5000,
"remaining_calls": 3850,
"over_limit_calls": 0,
"plan": "standard",
"hard_cap_enabled": false
}
}
data field |
Description |
|---|---|
year_month |
Target year and month (YYYY-MM, UTC-based) |
total_calls |
Cumulative number of calls for the current month |
monthly_limit |
Monthly credit count (plan limit) |
remaining_calls |
Remaining credit count |
over_limit_calls |
Number of overage calls (always 0 when the hard cap is ON or on the free plan) |
plan |
The plan currently in effect |
hard_cap_enabled |
Whether the hard cap setting is ON/OFF |
GET /v1/health — Health Check
Authentication: Not required (no metadata is included)
Billing: Free (not counted toward your monthly calls)
Request
curl https://liveness.api.pas-ta.io/v1/health
Response (200 OK)
{"status": "ok"}
In cases such as a DB connection failure, it returns 503 with {"status": "error", "message": "database unavailable"}.
Error Code List
| HTTP | error.code |
Condition |
|---|---|---|
| 400 | invalid_image |
Face detection failure or corrupted image |
| 400 | invalid_request |
Request validation error |
| 401 | invalid_api_key |
Invalid, deactivated, or expired key |
| 403 | forbidden |
Failure to verify the authentication credentials |
| 404 | not_found |
Resource does not exist (scheduled change, key ID, etc.) |
| 429 | quota_exceeded |
Monthly call limit exceeded (in API-stop mode) |
| 500 | internal_error |
Internal server error |
| 503 | service_unavailable |
DB connection failure or temporary unavailability |
About rate limiting: In addition to the monthly credit limit per plan, there is overall throttling (
429) for fair use. This is not an individual guarantee of instantaneous rate. If you require stable high throughput, please contact Support.
Sample Code
Examples of calling POST /v1/check/liveness from each language.
curl
There are two ways to write this with curl. For small images, ① is all you need. As the image grows, ① hits a shell limit, so use ② instead.
① Put the body directly in -d (basic form, for small images)
This is the most readable form — the shape of the request is visible at a glance.
curl -X POST https://liveness.api.pas-ta.io/v1/check/liveness \
-H "X-API-Key: {your API key}" \
-H "Content-Type: application/json" \
-d '{"image": "{Base64-encoded image}", "image_format": "jpeg"}'
② Pass the body via stdin (for large images, up to 10 MB)
# macOS: base64 -i / Linux: base64 -w0
{ printf '{"image": "'; base64 -i face.jpg | tr -d '\n'; printf '", "image_format": "jpeg"}'; } \
| curl -X POST https://liveness.api.pas-ta.io/v1/check/liveness \
-H "X-API-Key: {your API key}" \
-H "Content-Type: application/json" \
--data-binary @-
Choosing between ① and ②: Expanding the Base64 string directly into a command-line argument (as in
-d "{\"image\": \"$(base64 -i face.jpg)\", ...}") hits the shell’s argument-length limit (ARG_MAX), and the command fails withargument list too longbefore it even runs. The limit depends on your environment, but as a rule of thumb this starts happening once the source image exceeds a few hundred KB. The stdin approach in ② works up to the API’s 10 MB limit.tr -d '\n'is required to strip the trailing newline from the Base64 output (it would otherwise break the JSON).
Python
import base64
import requests
with open("face.jpg", "rb") as f:
image_b64 = base64.b64encode(f.read()).decode("ascii")
resp = requests.post(
"https://liveness.api.pas-ta.io/v1/check/liveness",
headers={"X-API-Key": "lc_abc12xxxxxxxxxxxxxxxxxxxxxxxx"},
json={"image": image_b64, "image_format": "jpeg"},
timeout=30,
)
resp.raise_for_status()
result = resp.json()["data"]
print("fake?", result["isFakeFace"], "likelihood:", result["isFakeLikelihood"])
Node.js
import { readFileSync } from "node:fs";
const imageB64 = readFileSync("face.jpg").toString("base64");
const resp = await fetch("https://liveness.api.pas-ta.io/v1/check/liveness", {
method: "POST",
headers: {
"X-API-Key": "lc_abc12xxxxxxxxxxxxxxxxxxxxxxxx",
"Content-Type": "application/json",
},
body: JSON.stringify({ image: imageB64, image_format: "jpeg" }),
});
const { data } = await resp.json();
console.log("fake?", data.isFakeFace, "likelihood:", data.isFakeLikelihood);
Plans and Usage Limits
About pricing: The table below shows AWS Marketplace public prices (USD). Private Offers are available for customers in Japan. Please contact Support if interested.
| Plan | Monthly calls | Monthly fee | Overage unit price / call |
|---|---|---|---|
| Free (evaluation) | 50 | $0 | Not available (hard cap always on) |
| Starter | 1,000 | $180 | $0.18 |
| Standard | 5,000 | $750 | $0.15 |
| Professional | 10,000 | $1,200 | $0.12 |
| Business | 30,000 | $2,400 | $0.08 |
| Enterprise | 100,000+ (custom) | $5,000+ | $0.05 |
- Credit system: At the start of each month, credits equal to the monthly call count are granted and expire at the end of that month (no carryover to the next month).
- Overage metering (default): Usage beyond the limit is billed on a metered basis at the overage unit price.
- Hard cap (optional, ON): When the limit is reached, the API is stopped (
429) and no overage charges are incurred. You can toggle this from the dashboard. - Plan changes: Upgrades take effect immediately; downgrades and cancellations take effect the next month / at the end of the month. For the procedure in AWS Marketplace, see Plan changes and cancellation.
- Enterprise: The limit is set on a custom basis under an individual contract (private offer).
- Billing: Only successful detection responses are counted (see Billing (Call Counting)).
Versioning
You can check the API version in the response’s metadata.ver (semantic version, e.g., 1.0.1).
| Change level | Example | Handling |
|---|---|---|
| Major (breaking change) | Response structure change, field removal, type change | Introduce a new URL path (/v1/ → /v2/) |
| Minor (backward compatible) | Field addition, new endpoint addition | Keep the URL path; update the minor of ver |
| Revision (internal change) | Bug fix, performance improvement | Keep the URL path; update the revision of ver |
- Even after introducing
/v2/, we will operate/v1/in parallel for the time being (a deprecation notice will be given at least 6 months in advance). - For the change history, please see the Changelog.