> ## Documentation Index
> Fetch the complete documentation index at: https://docs.didit.me/llms.txt
> Use this file to discover all available pages before exploring further.

# Face Search

> Search a face image (1:N) against your application's face search index — faces enrolled from verification sessions, saved standalone API calls, direct user-profile face uploads, and your block/allow lists — and get back ranked similarity matches plus blocklist and duplicate warnings.

**How it works.** The largest detected face in `user_image` is embedded and searched against the index, scoped to your application. Up to **5 matches** above the similarity floor are returned in `matches`, each with `similarity_percentage`, the originating session (`session_id`, `session_number`, `status`, `vendor_data`), `user_details` extracted during that session's document verification, `is_blocklisted` / `is_allowlisted` flags, and a `source` discriminator (`session`, `imported`, or `list_entry`).

- `search_type=most_similar` (default) ranks every enrolled face by similarity — use it for deduplication and fraud-ring investigation.
- `search_type=blocklisted_or_approved` restricts candidates to blocklisted faces, allowlisted faces, faces from approved sessions, and imported user-profile faces, ranking blocklisted entries first — use it when the primary goal is blocklist screening.

`status` is `Declined` only when a `FACE_IN_BLOCKLIST` or `POSSIBLE_FACE_IN_BLOCKLIST` warning fires; `DUPLICATED_FACE` / `POSSIBLE_DUPLICATED_FACE` (`information`) and `MULTIPLE_FACES_DETECTED` (`warning`) never decline. Unlike Passive Liveness, this endpoint does **not** exclude prior sessions with the same `vendor_data` from matching. A passive-liveness analysis also runs internally and is stored with the persisted session, but its result is not part of this response.

**Billing.** Each `200` response consumes one Face Search API credit (standalone APIs have no free tier). Insufficient balance returns `403` before any image processing.

**Session persistence and face enrollment (`save_api_request`, default `true`).** When `true`, the call is persisted as an API-type session (Business Console, `GET /v3/session/{sessionId}/decision/` via the returned `request_id`, `status.updated` webhook) and the searched face is enrolled into your face search index. Faces enrolled by Face Search calls are excluded from future Face Search results, so repeated searches never match each other. When `false`, the call is one-shot: nothing is stored, the face is not enrolled, and `match_image_url` values are internal storage paths rather than downloadable URLs.

**Sandbox.** Sandbox API keys skip all processing and billing: after request validation (malformed input still returns `400`), the endpoint returns a static `Approved` mock payload, no session is persisted, and no credits are consumed.

**Authentication.** Send your application's API key in the `x-api-key` header. Missing or invalid credentials return `403` (`{"detail": "You do not have permission to perform this action."}`) — this API never returns `401`.

**Rate limit.** Shared write budget of 300 requests/min per API key across all POST/PATCH/DELETE endpoints; exceeding it returns `429`.



## OpenAPI

````yaml POST /v3/face-search/
openapi: 3.0.0
info:
  version: 3.0.0
  title: Didit Verification API
  description: Identity verification API. Authenticate with x-api-key header.
servers:
  - url: https://verification.didit.me
security: []
tags: []
paths:
  /v3/face-search/:
    post:
      tags:
        - Standalone APIs
      summary: Face Search
      description: >-
        Search a face image (1:N) against your application's face search index —
        faces enrolled from verification sessions, saved standalone API calls,
        direct user-profile face uploads, and your block/allow lists — and get
        back ranked similarity matches plus blocklist and duplicate warnings.


        **How it works.** The largest detected face in `user_image` is embedded
        and searched against the index, scoped to your application. Up to **5
        matches** above the similarity floor are returned in `matches`, each
        with `similarity_percentage`, the originating session (`session_id`,
        `session_number`, `status`, `vendor_data`), `user_details` extracted
        during that session's document verification, `is_blocklisted` /
        `is_allowlisted` flags, and a `source` discriminator (`session`,
        `imported`, or `list_entry`).


        - `search_type=most_similar` (default) ranks every enrolled face by
        similarity — use it for deduplication and fraud-ring investigation.

        - `search_type=blocklisted_or_approved` restricts candidates to
        blocklisted faces, allowlisted faces, faces from approved sessions, and
        imported user-profile faces, ranking blocklisted entries first — use it
        when the primary goal is blocklist screening.


        `status` is `Declined` only when a `FACE_IN_BLOCKLIST` or
        `POSSIBLE_FACE_IN_BLOCKLIST` warning fires; `DUPLICATED_FACE` /
        `POSSIBLE_DUPLICATED_FACE` (`information`) and `MULTIPLE_FACES_DETECTED`
        (`warning`) never decline. Unlike Passive Liveness, this endpoint does
        **not** exclude prior sessions with the same `vendor_data` from
        matching. A passive-liveness analysis also runs internally and is stored
        with the persisted session, but its result is not part of this response.


        **Billing.** Each `200` response consumes one Face Search API credit
        (standalone APIs have no free tier). Insufficient balance returns `403`
        before any image processing.


        **Session persistence and face enrollment (`save_api_request`, default
        `true`).** When `true`, the call is persisted as an API-type session
        (Business Console, `GET /v3/session/{sessionId}/decision/` via the
        returned `request_id`, `status.updated` webhook) and the searched face
        is enrolled into your face search index. Faces enrolled by Face Search
        calls are excluded from future Face Search results, so repeated searches
        never match each other. When `false`, the call is one-shot: nothing is
        stored, the face is not enrolled, and `match_image_url` values are
        internal storage paths rather than downloadable URLs.


        **Sandbox.** Sandbox API keys skip all processing and billing: after
        request validation (malformed input still returns `400`), the endpoint
        returns a static `Approved` mock payload, no session is persisted, and
        no credits are consumed.


        **Authentication.** Send your application's API key in the `x-api-key`
        header. Missing or invalid credentials return `403` (`{"detail": "You do
        not have permission to perform this action."}`) — this API never returns
        `401`.


        **Rate limit.** Shared write budget of 300 requests/min per API key
        across all POST/PATCH/DELETE endpoints; exceeding it returns `429`.
      operationId: post_v3face-search
      parameters: []
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required:
                - user_image
              properties:
                user_image:
                  type: string
                  format: binary
                  description: >-
                    Front-facing face image to search with. Allowed extensions:
                    `tiff`, `jpg`, `jpeg`, `png`, `webp`. Maximum upload size:
                    **5 MB** (larger files are rejected with `400`). Images are
                    automatically compressed to ~0.5 MB before processing, so
                    very high resolutions do not improve accuracy. The image
                    must contain at least one detectable face — otherwise the
                    endpoint returns `400`. When several faces are present, the
                    largest one is searched and a `MULTIPLE_FACES_DETECTED`
                    warning is added.
                search_type:
                  type: string
                  default: most_similar
                  enum:
                    - most_similar
                    - blocklisted_or_approved
                  description: >-
                    Search policy. `most_similar` (default) ranks every enrolled
                    face in your application by similarity.
                    `blocklisted_or_approved` restricts candidates to
                    blocklisted faces, allowlisted faces, faces from approved
                    sessions, and imported user-profile faces — with blocklisted
                    entries ranked first.
                  example: most_similar
                rotate_image:
                  type: boolean
                  default: false
                  description: >-
                    When `true`, the service tries 90-degree rotations of the
                    input and uses the orientation that yields the best face
                    detection. Useful when EXIF orientation is missing. Adds
                    latency.
                  example: false
                save_api_request:
                  type: boolean
                  default: true
                  description: >-
                    When `true` (default), persists the call as an API-type
                    session, emits a `status.updated` webhook, presigns
                    `match_image_url` values, and enrolls the searched face into
                    your face search index (Face Search enrollments are excluded
                    from future searches). When `false`, the search is one-shot
                    — no session is stored and the face is not enrolled.
                  example: true
                vendor_data:
                  type: string
                  description: >-
                    Optional opaque identifier (your user id, email, UUID…)
                    stored on the persisted session and echoed back. It does not
                    exclude same-user faces from the search results.
                  example: user-123
                metadata:
                  type: object
                  additionalProperties: true
                  description: >-
                    Optional JSON object stored with the session (when
                    `save_api_request=true`) and echoed back. In multipart
                    requests, send it as a JSON-encoded string field — it is
                    parsed into an object.
                  example:
                    flow: dedup_check
            example:
              user_image: (binary JPEG/PNG selfie)
              search_type: most_similar
              rotate_image: false
              save_api_request: true
              vendor_data: user-123
              metadata:
                flow: dedup_check
      responses:
        '200':
          description: >-
            Face search completed. `face_search.matches` holds up to 5 similar
            faces ordered by similarity; an empty array means no enrolled face
            exceeded the similarity floor. `status` is `Declined` only on
            blocklist hits — duplicate matches alone return `Approved`, so
            inspect `matches` and `warnings`, not just `status`. When
            `save_api_request=true`, `request_id` is the persisted session id.
          content:
            application/json:
              examples:
                Blocklist hit (Declined):
                  summary: The face matches a blocklisted face
                  value:
                    request_id: a1b2c3d4-e5f6-7890-1234-567890abcdef
                    face_search:
                      status: Declined
                      total_matches: 1
                      matches:
                        - session_id: 882c42d5-8a4d-4d20-8080-a22f57822c86
                          session_number: 323442
                          similarity_percentage: 99.99
                          source: session
                          vendor_data: user-1
                          verification_date: '2025-01-01T00:00:00Z'
                          user_details:
                            full_name: Jane Marie Doe
                            document_type: ID
                            document_number: X1234567
                          match_image_url: https://<media-host>/face/3f6a1c2e/reference.jpg
                          status: Approved
                          is_blocklisted: true
                          is_allowlisted: false
                          api_service: null
                      user_image:
                        entities:
                          - bbox:
                              - 40
                              - 40
                              - 120
                              - 120
                            confidence: 0.732973
                        best_angle: 0
                      warnings:
                        - risk: FACE_IN_BLOCKLIST
                          feature: LIVENESS
                          additional_data:
                            blocklisted_session_id: 882c42d5-8a4d-4d20-8080-a22f57822c86
                            blocklisted_session_number: 323442
                            api_service: null
                          log_type: error
                          short_description: Face in blocklist
                          long_description: >-
                            The system identified a face in the blocklist, which
                            means the face is not allowed to be verified.
                    vendor_data: user-123
                    metadata: null
                    created_at: '2026-06-12T01:04:42.763237+00:00'
                Duplicate found (Approved):
                  summary: >-
                    The face already appeared in another approved session —
                    informational, no decline
                  value:
                    request_id: 5e0c3a1f-7b2d-4c8e-9f10-2a3b4c5d6e7f
                    face_search:
                      status: Approved
                      total_matches: 1
                      matches:
                        - session_id: 1f2e3d4c-5b6a-7980-1122-334455667788
                          session_number: 1024
                          similarity_percentage: 87.42
                          source: session
                          vendor_data: user-9
                          verification_date: '2025-11-20T09:15:00Z'
                          user_details: null
                          match_image_url: https://<media-host>/face/9c8b7a6d/reference.jpg
                          status: Approved
                          is_blocklisted: false
                          is_allowlisted: false
                          api_service: PASSIVE_LIVENESS
                      user_image:
                        entities:
                          - bbox:
                              - 40
                              - 40
                              - 120
                              - 120
                            confidence: 0.732973
                        best_angle: 0
                      warnings:
                        - risk: DUPLICATED_FACE
                          feature: LIVENESS
                          additional_data:
                            duplicated_session_id: 1f2e3d4c-5b6a-7980-1122-334455667788
                            duplicated_session_number: 1024
                            api_service: PASSIVE_LIVENESS
                          log_type: information
                          short_description: Duplicated face from other approved session
                          long_description: >-
                            The system identified a duplicated face from another
                            approved session, requiring further investigation.
                    vendor_data: user-123
                    metadata: null
                    created_at: '2026-06-12T01:04:42.763237+00:00'
                No matches (Approved):
                  summary: No enrolled face exceeded the similarity floor
                  value:
                    request_id: 9d8c7b6a-5f4e-3d2c-1b0a-998877665544
                    face_search:
                      status: Approved
                      total_matches: 0
                      matches: []
                      user_image:
                        entities:
                          - bbox:
                              - 40
                              - 40
                              - 120
                              - 120
                            confidence: 0.732973
                        best_angle: 0
                      warnings: []
                    vendor_data: user-123
                    metadata: null
                    created_at: '2026-06-12T01:04:42.763237+00:00'
              schema:
                type: object
                properties:
                  request_id:
                    type: string
                    format: uuid
                    description: >-
                      Persisted session id when `save_api_request=true` (usable
                      with `GET /v3/session/{sessionId}/decision/`); otherwise a
                      transient correlation UUID.
                  face_search:
                    type: object
                    properties:
                      status:
                        type: string
                        enum:
                          - Approved
                          - Declined
                        description: >-
                          `Declined` only when a `FACE_IN_BLOCKLIST` or
                          `POSSIBLE_FACE_IN_BLOCKLIST` warning fired. Duplicate
                          matches and multiple-face warnings never decline.
                      total_matches:
                        type: integer
                        description: Number of entries in `matches` (0–5).
                        example: 1
                      matches:
                        type: array
                        maxItems: 5
                        description: >-
                          Up to 5 faces from your face search index that exceed
                          the similarity floor, ordered by similarity (with
                          blocklisted entries ranked first when
                          `search_type=blocklisted_or_approved`). Empty when no
                          enrolled face is similar enough.
                        items:
                          type: object
                          properties:
                            session_id:
                              type: string
                              format: uuid
                              nullable: true
                              description: >-
                                Id of the verification session the matched face
                                belongs to. `null` when `source` is `imported`
                                or `list_entry`.
                              example: 882c42d5-8a4d-4d20-8080-a22f57822c86
                            session_number:
                              type: integer
                              nullable: true
                              description: >-
                                Human-friendly session number shown in the
                                Business Console. `null` for non-session
                                matches.
                              example: 323442
                            similarity_percentage:
                              type: number
                              format: float
                              minimum: 0
                              maximum: 100
                              description: >-
                                Similarity between the submitted face and the
                                matched face (0–100).
                              example: 99.99
                            source:
                              type: string
                              enum:
                                - session
                                - imported
                                - list_entry
                              description: >-
                                Where the matched face was enrolled from:
                                `session` — a verification session or a saved
                                standalone API call; `imported` — a face
                                uploaded directly to a user profile via the
                                vendor-user faces upload endpoint; `list_entry`
                                — a face attached to one of your lists.
                            vendor_data:
                              type: string
                              nullable: true
                              description: >-
                                `vendor_data` of the matched session (or of the
                                user profile for imported faces).
                              example: user-1
                            verification_date:
                              type: string
                              format: date-time
                              nullable: true
                              description: >-
                                When the matched face was originally captured
                                (`YYYY-MM-DDThh:mm:ssZ`). `null` for
                                `list_entry` matches.
                              example: '2025-01-01T00:00:00Z'
                            user_details:
                              type: object
                              nullable: true
                              description: >-
                                Identity data extracted during the matched
                                session's document verification. `null` when the
                                matched session has no document data (e.g.
                                liveness-only sessions). For `source: imported`
                                matches it is populated from the vendor-user
                                profile instead: `full_name` set,
                                `document_type`/`document_number` null.
                              properties:
                                full_name:
                                  type: string
                                  nullable: true
                                  example: Jane Marie Doe
                                document_type:
                                  type: string
                                  nullable: true
                                  example: ID
                                document_number:
                                  type: string
                                  nullable: true
                                  example: X1234567
                            match_image_url:
                              type: string
                              nullable: true
                              description: >-
                                Time-limited URL (`https://<media-host>/...`) of
                                the matched face's reference image when
                                `save_api_request=true`. With
                                `save_api_request=false` this is an internal
                                storage path that is not directly downloadable.
                              example: https://<media-host>/face/3f6a1c2e/reference.jpg
                            status:
                              type: string
                              nullable: true
                              description: >-
                                Status of the matched session (`Approved`,
                                `Declined`, `In Review`, …). `null` for
                                `imported`/`list_entry` matches.
                              example: Approved
                            is_blocklisted:
                              type: boolean
                              description: >-
                                `true` when the matched face is on your face
                                blocklist. Any sufficiently similar blocklisted
                                match declines the search.
                            is_allowlisted:
                              type: boolean
                              description: >-
                                `true` when the matched face is on one of your
                                allowlists. Allowlisted matches suppress
                                duplicate warnings.
                            api_service:
                              type: string
                              nullable: true
                              description: >-
                                Set when the matched face was enrolled by a
                                standalone API call (e.g. `PASSIVE_LIVENESS`);
                                `null` for regular verification sessions and
                                imported faces.
                              example: null
                      user_image:
                        type: object
                        description: >-
                          Face-detection results for the submitted image.
                          Entries here carry only `bbox` and `confidence`.
                        properties:
                          entities:
                            type: array
                            description: >-
                              One entry per detected face. The largest face is
                              the one searched.
                            items:
                              type: object
                              properties:
                                bbox:
                                  type: array
                                  items:
                                    type: integer
                                  minItems: 4
                                  maxItems: 4
                                  description: >-
                                    Bounding box `[x_min, y_min, x_max, y_max]`
                                    in pixels.
                                  example:
                                    - 40
                                    - 40
                                    - 120
                                    - 120
                                confidence:
                                  type: number
                                  format: float
                                  minimum: 0
                                  maximum: 1
                                  description: Face-detection confidence (0–1).
                                  example: 0.732973
                          best_angle:
                            type: integer
                            description: >-
                              Rotation (degrees) that produced the best face
                              detection; non-zero only when `rotate_image=true`
                              corrected the orientation. Never null on this
                              endpoint (defaults to 0 when the model omits it).
                            example: 0
                      warnings:
                        type: array
                        description: >-
                          Risk signals. `FACE_IN_BLOCKLIST` /
                          `POSSIBLE_FACE_IN_BLOCKLIST` (`error`) decline the
                          search; `MULTIPLE_FACES_DETECTED` (`warning`) and
                          `DUPLICATED_FACE` / `POSSIBLE_DUPLICATED_FACE`
                          (`information`) are advisory.
                        items:
                          type: object
                          properties:
                            risk:
                              type: string
                              enum:
                                - MULTIPLE_FACES_DETECTED
                                - FACE_IN_BLOCKLIST
                                - POSSIBLE_FACE_IN_BLOCKLIST
                                - DUPLICATED_FACE
                                - POSSIBLE_DUPLICATED_FACE
                              description: Machine-readable risk code.
                            feature:
                              type: string
                              enum:
                                - LIVENESS
                              description: >-
                                Feature that raised the warning. Always
                                `LIVENESS` on this endpoint.
                            additional_data:
                              type: object
                              nullable: true
                              additionalProperties: true
                              description: >-
                                `null` for `MULTIPLE_FACES_DETECTED`. Blocklist
                                hits carry `{blocklisted_session_id,
                                blocklisted_session_number, api_service}`;
                                duplicate hits carry `{duplicated_session_id,
                                duplicated_session_number, api_service}`
                                pointing at the first matching session.
                            log_type:
                              type: string
                              enum:
                                - error
                                - warning
                                - information
                              description: >-
                                Severity. `error` warnings drive `status` to
                                `Declined`; `warning` and `information` entries
                                are advisory and never decline on their own.
                            short_description:
                              type: string
                              description: Human-readable one-line summary of the risk.
                            long_description:
                              type: string
                              description: Human-readable explanation of the risk.
                  vendor_data:
                    type: string
                    nullable: true
                    description: Echo of the `vendor_data` you sent, or `null`.
                  metadata:
                    type: object
                    nullable: true
                    additionalProperties: true
                    description: Echo of the `metadata` object you sent, or `null`.
                  created_at:
                    type: string
                    format: date-time
                    description: >-
                      ISO 8601 timestamp (UTC) of when the response was
                      generated, e.g. `2026-06-12T01:04:42.763237+00:00`.
        '400':
          description: >-
            Validation error. Returned when `user_image` is missing, exceeds 5
            MB, has an unsupported extension, or when **no face is detected** in
            the submitted image. Field errors use DRF's `{field: [messages]}`
            envelope; face-detection failures use `{"error": ...}`.
          content:
            application/json:
              examples:
                No face detected:
                  summary: The image decodes fine but contains no detectable face
                  value:
                    error: No face detected in the image
                Missing image:
                  summary: '`user_image` not included in the form data'
                  value:
                    user_image:
                      - No file was submitted.
                Unsupported file extension:
                  summary: File extension outside tiff/jpg/jpeg/png/webp
                  value:
                    user_image:
                      - >-
                        File extension “txt” is not allowed. Allowed extensions
                        are: tiff, jpg, jpeg, png, webp.
                File too large:
                  summary: Upload exceeds the 5 MB limit
                  value:
                    user_image:
                      - File size should not exceed 5 MB
        '403':
          description: >-
            Permission denied. Returned when the `x-api-key` header is missing,
            malformed, revoked, or belongs to another environment — and also
            when the calling organization's balance cannot cover the call.
            Authentication failures return `403` with `{"detail": ...}`; this
            API never returns `401`. Credit shortfalls return `403` with
            `{"error": ...}` before any image processing happens.
          content:
            application/json:
              examples:
                Missing or invalid API key:
                  summary: No `x-api-key` header, or the key is invalid/revoked
                  value:
                    detail: You do not have permission to perform this action.
                Not enough credits:
                  summary: Organization balance cannot cover the call
                  value:
                    error: >-
                      You don't have enough credits to perform this request.
                      Please top up at https://business.didit.me
        '429':
          description: >-
            Rate limit exceeded. All POST/PATCH/DELETE endpoints share a budget
            of 300 write requests per minute per API key. The response carries
            `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset`,
            and `Retry-After` headers.
          content:
            application/json:
              examples:
                Write rate limit exceeded:
                  summary: More than 300 write requests in the current minute
                  value:
                    detail: >-
                      Write request rate limit exceeded. You can make up to 300
                      requests per minute.
              schema:
                type: object
                properties:
                  detail:
                    type: string
      security:
        - ApiKeyAuth: []
      x-codeSamples:
        - lang: curl
          label: cURL
          source: |-
            curl -X POST 'https://verification.didit.me/v3/face-search/' \
              -H 'x-api-key: YOUR_API_KEY' \
              -F 'user_image=@./selfie.jpg' \
              -F 'search_type=most_similar' \
              -F 'save_api_request=true' \
              -F 'vendor_data=user-123'
        - lang: python
          label: Python
          source: |-
            import requests

            url = 'https://verification.didit.me/v3/face-search/'
            headers = {'x-api-key': 'YOUR_API_KEY'}

            with open('selfie.jpg', 'rb') as f:
                files = {'user_image': ('selfie.jpg', f, 'image/jpeg')}
                data = {
                    'search_type': 'most_similar',
                    'save_api_request': 'true',
                    'vendor_data': 'user-123',
                }
                resp = requests.post(url, headers=headers, files=files, data=data, timeout=60)

            resp.raise_for_status()
            body = resp.json()
            print('status:', body['face_search']['status'])
            print('matches:', body['face_search']['total_matches'])
            for m in body['face_search']['matches']:
                print(f"  session={m['session_id']} similarity={m['similarity_percentage']} blocklisted={m['is_blocklisted']}")
        - lang: javascript
          label: JavaScript
          source: >-
            import fs from 'node:fs';


            const form = new FormData();

            form.append('user_image', new
            Blob([fs.readFileSync('./selfie.jpg')]), 'selfie.jpg');

            form.append('search_type', 'most_similar');

            form.append('save_api_request', 'true');

            form.append('vendor_data', 'user-123');


            const response = await
            fetch('https://verification.didit.me/v3/face-search/', {
              method: 'POST',
              headers: { 'x-api-key': 'YOUR_API_KEY' },
              body: form,
            });


            if (!response.ok) throw new Error(`Face search failed:
            ${response.status}`);

            const body = await response.json();

            console.log('status:', body.face_search.status, 'matches:',
            body.face_search.total_matches);

            body.face_search.matches.forEach(m =>
              console.log(`  session=${m.session_id} similarity=${m.similarity_percentage} blocklisted=${m.is_blocklisted}`)
            );
components:
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: x-api-key

````