{
    "openapi": "3.1.0",
    "info": {
        "title": "Trace API",
        "version": "1.0.0",
        "summary": "Reverse face search over the public web.",
        "description": "Upload a face photo and get back the public pages it appears on.\n\nEvery search costs one credit. A request from an account with no credits creates the scan but does not search: it comes back `locked: true` with no matches. Call the reveal endpoint to spend a credit and run the real search. (The panel shows a static sample result to a signed-in visitor with no credits; the API never returns it, because a placeholder is something a script would build on.)\n\nOnly upload photos you have the right to use, and follow the law where you are."
    },
    "servers": [
        {
            "url": "https://traceaifacescan.app/api/v1"
        }
    ],
    "security": [
        {
            "bearerAuth": []
        }
    ],
    "components": {
        "securitySchemes": {
            "bearerAuth": {
                "type": "http",
                "scheme": "bearer",
                "description": "An API key from the panel, sent as `Authorization: Bearer trk_live_\u2026`."
            }
        },
        "schemas": {
            "Match": {
                "type": "object",
                "properties": {
                    "score": {
                        "type": [
                            "integer",
                            "null"
                        ],
                        "minimum": 0,
                        "maximum": 100,
                        "description": "Never null on a revealed scan. The field is nullable only because a locked scan returns no matches at all."
                    },
                    "tier": {
                        "type": [
                            "string",
                            "null"
                        ],
                        "enum": [
                            "possible",
                            "strong",
                            "near_certain"
                        ],
                        "description": "Trace's own confidence bands: possible from 70, strong from 80, near_certain from 90."
                    },
                    "platform": {
                        "type": [
                            "string",
                            "null"
                        ],
                        "description": "The platform the URL belongs to, or `web` for anything else."
                    },
                    "handle": {
                        "type": [
                            "string",
                            "null"
                        ],
                        "description": "null when the URL points at a piece of content rather than an account."
                    },
                    "url": {
                        "type": [
                            "string",
                            "null"
                        ],
                        "format": "uri"
                    },
                    "preview_url": {
                        "type": [
                            "string",
                            "null"
                        ],
                        "format": "uri",
                        "description": "Needs the same Authorization header. Not a public link."
                    }
                }
            },
            "Scan": {
                "type": "object",
                "properties": {
                    "id": {
                        "type": "string"
                    },
                    "number": {
                        "type": "integer",
                        "description": "Sequential within your account."
                    },
                    "status": {
                        "type": "string",
                        "enum": [
                            "queued",
                            "running",
                            "done",
                            "failed"
                        ]
                    },
                    "progress": {
                        "type": "integer",
                        "minimum": 0,
                        "maximum": 100
                    },
                    "locked": {
                        "type": "boolean",
                        "description": "true when no credit has been spent yet. A locked scan carries no matches \u2014 call reveal to spend a credit and run the search."
                    },
                    "match_count": {
                        "type": "integer"
                    },
                    "error_code": {
                        "type": [
                            "string",
                            "null"
                        ],
                        "enum": [
                            "no_face",
                            "provider_unavailable",
                            "not_authorized",
                            "rate_limited",
                            "timeout",
                            "upload_failed",
                            "network",
                            "unexpected",
                            null
                        ],
                        "description": "Set when status is failed. A credit reserved for a failed search is always returned."
                    },
                    "created_at": {
                        "type": "string",
                        "format": "date-time"
                    },
                    "expires_at": {
                        "type": [
                            "string",
                            "null"
                        ],
                        "format": "date-time",
                        "description": "The image and results are deleted after 30 days."
                    },
                    "matches": {
                        "type": "array",
                        "items": {
                            "$ref": "#/components/schemas/Match"
                        }
                    }
                }
            },
            "Problem": {
                "type": "object",
                "description": "RFC 9457. Branch on `code`; `detail` is for humans and may change.",
                "properties": {
                    "type": {
                        "type": "string",
                        "format": "uri"
                    },
                    "title": {
                        "type": "string"
                    },
                    "status": {
                        "type": "integer"
                    },
                    "code": {
                        "type": "string"
                    },
                    "detail": {
                        "type": "string"
                    }
                }
            }
        },
        "responses": {
            "Unauthenticated": {
                "description": "Missing, malformed, revoked or invalid key (`unauthenticated`).",
                "content": {
                    "application/problem+json": {
                        "schema": {
                            "$ref": "#/components/schemas/Problem"
                        }
                    }
                }
            },
            "InsufficientCredits": {
                "description": "Not enough credits (`insufficient_credits`).",
                "content": {
                    "application/problem+json": {
                        "schema": {
                            "$ref": "#/components/schemas/Problem"
                        }
                    }
                }
            },
            "NotFound": {
                "description": "No such scan (`not_found`).",
                "content": {
                    "application/problem+json": {
                        "schema": {
                            "$ref": "#/components/schemas/Problem"
                        }
                    }
                }
            },
            "InvalidImage": {
                "description": "The image was rejected (`invalid_image`).",
                "content": {
                    "application/problem+json": {
                        "schema": {
                            "$ref": "#/components/schemas/Problem"
                        }
                    }
                }
            },
            "RateLimited": {
                "description": "Rate limited (`rate_limited`). There is no request rate limit by default; this is returned after repeated failed authentication, or if a per-key limit has been set. See `Retry-After`.",
                "content": {
                    "application/problem+json": {
                        "schema": {
                            "$ref": "#/components/schemas/Problem"
                        }
                    }
                }
            }
        }
    },
    "paths": {
        "/account": {
            "get": {
                "summary": "Your account and the key you are using",
                "operationId": "getAccount",
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object"
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthenticated"
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    }
                }
            }
        },
        "/scans": {
            "get": {
                "summary": "List your scans, newest first",
                "operationId": "listScans",
                "description": "Matches are omitted from the list \u2014 fetch a single scan for those.",
                "parameters": [
                    {
                        "name": "page",
                        "in": "query",
                        "schema": {
                            "type": "integer",
                            "minimum": 1
                        }
                    },
                    {
                        "name": "per_page",
                        "in": "query",
                        "schema": {
                            "type": "integer",
                            "minimum": 1,
                            "maximum": 100,
                            "default": 25
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthenticated"
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    }
                }
            },
            "post": {
                "summary": "Start a scan",
                "operationId": "createScan",
                "description": "Returns 202: the search runs in the background and takes 25-45 seconds. Poll the scan until `status` is `done` or `failed`.\n\nThere is no `image_url` parameter and there will not be one in v1 \u2014 fetching an arbitrary address on your behalf would make this endpoint an SSRF proxy.\n\nSend an `Idempotency-Key` header to make retries safe: the same key within 24 hours returns the original scan instead of starting a second one.",
                "parameters": [
                    {
                        "name": "Idempotency-Key",
                        "in": "header",
                        "required": false,
                        "schema": {
                            "type": "string",
                            "maxLength": 64
                        }
                    }
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "multipart/form-data": {
                            "schema": {
                                "type": "object",
                                "required": [
                                    "image"
                                ],
                                "properties": {
                                    "image": {
                                        "type": "string",
                                        "format": "binary",
                                        "description": "jpeg, png or webp. At most 8 MB, at least 200x200 px. Crop to the face for the best results."
                                    }
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "202": {
                        "description": "Queued"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthenticated"
                    },
                    "422": {
                        "$ref": "#/components/responses/InvalidImage"
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    }
                }
            }
        },
        "/scans/{scan}": {
            "get": {
                "summary": "One scan, with its matches",
                "operationId": "getScan",
                "parameters": [
                    {
                        "name": "scan",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Scan"
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthenticated"
                    },
                    "404": {
                        "$ref": "#/components/responses/NotFound"
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    }
                }
            },
            "delete": {
                "summary": "Delete a scan, its image and its previews",
                "operationId": "deleteScan",
                "parameters": [
                    {
                        "name": "scan",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Deleted"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthenticated"
                    },
                    "404": {
                        "$ref": "#/components/responses/NotFound"
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    }
                }
            }
        },
        "/scans/{scan}/reveal": {
            "post": {
                "summary": "Spend a credit to reveal the sources",
                "operationId": "revealScan",
                "description": "Runs the full search on the same scan and returns 202. Already-revealed scans return 200 unchanged and cost nothing.",
                "parameters": [
                    {
                        "name": "scan",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        }
                    }
                ],
                "responses": {
                    "202": {
                        "description": "Queued"
                    },
                    "200": {
                        "description": "Already revealed"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthenticated"
                    },
                    "402": {
                        "$ref": "#/components/responses/InsufficientCredits"
                    },
                    "404": {
                        "$ref": "#/components/responses/NotFound"
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    }
                }
            }
        },
        "/scans/{scan}/previews/{index}": {
            "get": {
                "summary": "The preview thumbnail of one match",
                "operationId": "getPreview",
                "description": "Needs the same Authorization header \u2014 this is not a public link.",
                "parameters": [
                    {
                        "name": "scan",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        }
                    },
                    {
                        "name": "index",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "integer",
                            "minimum": 0
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "The image",
                        "content": {
                            "image/webp": [],
                            "image/jpeg": [],
                            "image/png": []
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthenticated"
                    },
                    "404": {
                        "$ref": "#/components/responses/NotFound"
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    }
                }
            }
        }
    }
}