Skip to content

Face Match

This endpoint takes two images, locates one face in each, and answers whether the two faces belong to the same person.

The most common use is checking a selfie against the photo printed on an identity document, but the comparison does not require that: any pair of images with a detectable face works — two selfies, two documents, or one of each.

🚧 Face Match is not Face

  • Face Match (this page) — takes two images and answers whether they are the same person.
  • Face — takes one image and only describes the faces that exist in it, comparing nothing.

This endpoint does not perform liveness — it does not tell a person present at the capture apart from a photo of a photo. For that, use Liveness. And it does not query official databases: if you need to check the face against the government's biometrics for a CPF (the Brazilian individual taxpayer number), use Face Match + Datavalid.

TIP

If the selfie comes from a liveness session made with our SDKs, prefer Face Match for Liveness: it reuses the selfie already captured, and you send only the document.

Request

POST /face-match/v2

Headers

http
Authorization: ApiKey <your-api-key>

This endpoint also accepts the JWT token, in the format Authorization: Bearer <accessToken>. See JWT Token.

Parameters

Query string parameter, optional.

ParameterDescriptionRequired
returnsFaceInfoWhen true, each item of resources also carries the face object with the data of the face used in the comparison, and metadata.filesInfo[].data comes back empty. Default: false.No

An unknown query parameter, or one whose value has the wrong type, makes the request fail with 422.

Accepted files

ItemValue
Formatsimage/png, image/jpeg, application/pdf
Files per call2 files
Maximum size (multipart)15 MB per file

The form field names are up to you — they come back in the response, inside resources and metadata.filesInfo, and they are how you tell which image is which. Sending more than two files is rejected with 422.

When the file is a multi-page PDF, each page is analyzed in order and the first page with any face detected is the one that goes into the comparison; the rest are discarded.

Besides multipart/form-data, the files can be sent as JSON, in the base64 field, as described in Sending files.

Example request

bash
curl -i -X POST 'https://api-homolog.nxcd.app/face-match/v2' \
  --header 'Authorization: ApiKey YOUR_API_KEY' \
  --form 'documento=@./cnh.jpg' \
  --form 'selfie=@./selfie.jpg'

Response

The fields below describe the v2 response, the supported version of this endpoint.

FieldDescriptionType
idUnique identifier of the requestString
versionAPI version that served the callString
dataList with the result of the comparison. Carries one entry when the comparison happened, and comes back empty when it did not.Object[]
data[].matchedtrue when the two faces compared belong to the same personBoolean
data[].confidenceConfidence level of the comparison, from 0 to 100Number
data[].resourcesThe two faces used in the comparison, in the order in which they were checkedObject[]
data[].resources[].fieldnameName of the field that file was sent inString
data[].resources[].pagePage of the file where the face was found. Starts at 0; for images it is always 0.Number
data[].resources[].faceData of the face used in the comparison. Only appears with returnsFaceInfo=true.Object
data[].resources[].face.typeSELFIE when the face takes up much of the image, ID when it is a small face, typical of a document photoString
data[].resources[].face.confidenceConfidence with which the detector located the faceNumber
data[].resources[].face.ageEstimated apparent ageNumber
data[].resources[].face.genderObject with value (Male or Female) and confidenceObject
data[].resources[].face.boundingBoxPosition of the face in the image, in proportions between 0 and 1 (top, left, width, height)Object
data[].resources[].face.croppedBase64Crop of the face in base64String
metadataObject with the metadata of the requestObject
metadata.filesInfoList with one entry per file sent, including those where no face was foundObject[]
metadata.filesInfo[].fieldnameName of the field used to send the fileString
metadata.filesInfo[].nameName of the file sentString
metadata.filesInfo[].sizeSize of the file, in bytesNumber
metadata.filesInfo[].pagesNumber of pages of the file. For images, 1.Number
metadata.filesInfo[].mimetypeType of the file sentString
metadata.filesInfo[].encodingEncoding of the file as sentString
metadata.filesInfo[].sha256SHA-256 hash of the file sentString
metadata.filesInfo[].dataFaces found in that file. Comes back empty when returnsFaceInfo=true.Object[]
metadata.filesInfo[].data[].pagePage where the face was found, starting at 0Number
metadata.filesInfo[].data[].faceObject with the boundingBox and croppedBase64 of the faceObject
metadata.timeSpentProcessing time of the request, in millisecondsNumber

🚧 An empty data is not an error

The comparison only happens when two files have some face detected. A file with no face, a single file sent, or no file at all: the response is 200 with "data": [], and metadata.filesInfo still lists everything that was sent.

That means data[0] may not exist. Check the size of data before reading matched — code that assumes data[0].matched gets an error instead of a negative result.

JSON examples

  1. Matching faces
Status Code: 200
json
{
  "id": "13cfec7c-b238-4820-a4d1-5173e4c1418e",
  "version": "v2",
  "data": [
    {
      "matched": true,
      "confidence": 97.81,
      "resources": [
        { "fieldname": "documento", "page": 0 },
        { "fieldname": "selfie", "page": 0 }
      ]
    }
  ],
  "metadata": {
    "filesInfo": [
      {
        "fieldname": "documento",
        "name": "cnh.jpg",
        "size": 567098,
        "pages": 1,
        "mimetype": "image/jpeg",
        "encoding": "7bit",
        "sha256": "41c2ece339abfcf9ba1e7d5a60666162a24ef34ba95adeb41ed1d9721c91c6b0",
        "data": [
          {
            "page": 0,
            "face": {
              "boundingBox": {
                "top": 0.2237228155136108,
                "left": 0.2856607735157013,
                "width": 0.1555942893028259,
                "height": 0.1687679588794708
              },
              "croppedBase64": "iVBORw0KGgoAAAANSUhEUgAAAlgAAAJYCAMAAA...K5CYII="
            }
          }
        ]
      },
      {
        "fieldname": "selfie",
        "name": "selfie.jpg",
        "size": 890098,
        "pages": 1,
        "mimetype": "image/jpeg",
        "encoding": "7bit",
        "sha256": "41c2ece339abfcf9ba1e7d5a60666162a24ef34ba95adeb41ed1d9721c91c6c4",
        "data": [
          {
            "page": 0,
            "face": {
              "boundingBox": {
                "top": 0.1142489537596702,
                "left": 0.2693724930286407,
                "width": 0.4232554733753204,
                "height": 0.6968063116073608
              },
              "croppedBase64": "iVBORw0KGgoAAAANSUhEUgAAAlgAAAJYCAMAAA...SuQmCC"
            }
          }
        ]
      }
    ],
    "timeSpent": 4820
  }
}
  1. Faces of different people
Status Code: 200
json
{
  "id": "6a1d1e8c-4a7d-4a5e-9f2c-1b0f2a9c7e11",
  "version": "v2",
  "data": [
    {
      "matched": false,
      "confidence": 12.4,
      "resources": [
        { "fieldname": "documento", "page": 0 },
        { "fieldname": "selfie", "page": 0 }
      ]
    }
  ],
  "metadata": {
    "filesInfo": [
      {
        "fieldname": "documento",
        "name": "cnh.jpg",
        "size": 567098,
        "pages": 1,
        "mimetype": "image/jpeg",
        "encoding": "7bit",
        "sha256": "41c2ece339abfcf9ba1e7d5a60666162a24ef34ba95adeb41ed1d9721c91c6b0",
        "data": [
          {
            "page": 0,
            "face": {
              "boundingBox": { "top": 0.22, "left": 0.28, "width": 0.15, "height": 0.16 },
              "croppedBase64": "iVBORw0KGgoAAAANSUhEUgAAAlgAAAJYCAMAAA...K5CYII="
            }
          }
        ]
      },
      {
        "fieldname": "selfie",
        "name": "selfie.jpg",
        "size": 890098,
        "pages": 1,
        "mimetype": "image/jpeg",
        "encoding": "7bit",
        "sha256": "41c2ece339abfcf9ba1e7d5a60666162a24ef34ba95adeb41ed1d9721c91c6c4",
        "data": [
          {
            "page": 0,
            "face": {
              "boundingBox": { "top": 0.11, "left": 0.26, "width": 0.42, "height": 0.69 },
              "croppedBase64": "iVBORw0KGgoAAAANSUhEUgAAAlgAAAJYCAMAAA...SuQmCC"
            }
          }
        ]
      }
    ],
    "timeSpent": 4610
  }
}
  1. No face found in one of the files
Status Code: 200
json
{
  "id": "a7f0c3d2-9e51-4b8a-8f31-2d4c6b9e0a77",
  "version": "v2",
  "data": [],
  "metadata": {
    "filesInfo": [
      {
        "fieldname": "documento",
        "name": "cnh.jpg",
        "size": 567098,
        "pages": 1,
        "mimetype": "image/jpeg",
        "encoding": "7bit",
        "sha256": "41c2ece339abfcf9ba1e7d5a60666162a24ef34ba95adeb41ed1d9721c91c6b0",
        "data": [
          {
            "page": 0,
            "face": {
              "boundingBox": { "top": 0.22, "left": 0.28, "width": 0.15, "height": 0.16 },
              "croppedBase64": "iVBORw0KGgoAAAANSUhEUgAAAlgAAAJYCAMAAA...K5CYII="
            }
          }
        ]
      },
      {
        "fieldname": "selfie",
        "name": "borrada.jpg",
        "size": 152064,
        "pages": 1,
        "mimetype": "image/jpeg",
        "encoding": "7bit",
        "sha256": "2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae",
        "data": []
      }
    ],
    "timeSpent": 3120
  }
}
  1. With returnsFaceInfo=true
Status Code: 200
json
{
  "id": "b91e5f44-1c02-4a77-9d3e-58a0f2c4e6bb",
  "version": "v2",
  "data": [
    {
      "matched": true,
      "confidence": 96.32,
      "resources": [
        {
          "fieldname": "documento",
          "page": 0,
          "face": {
            "confidence": 0.98,
            "type": "ID",
            "age": 34,
            "gender": { "value": "Male", "confidence": 99.72 },
            "boundingBox": { "top": 0.22, "left": 0.28, "width": 0.15, "height": 0.16 },
            "croppedBase64": "iVBORw0KGgoAAAANSUhEUgAAAlgAAAJYCAMAAA...K5CYII="
          }
        },
        {
          "fieldname": "selfie",
          "page": 0,
          "face": {
            "confidence": 0.99,
            "type": "SELFIE",
            "age": 33,
            "gender": { "value": "Male", "confidence": 99.15 },
            "boundingBox": { "top": 0.11, "left": 0.26, "width": 0.42, "height": 0.69 },
            "croppedBase64": "iVBORw0KGgoAAAANSUhEUgAAAlgAAAJYCAMAAA...SuQmCC"
          }
        }
      ]
    }
  ],
  "metadata": {
    "filesInfo": [
      {
        "fieldname": "documento",
        "name": "cnh.jpg",
        "size": 567098,
        "pages": 1,
        "mimetype": "image/jpeg",
        "encoding": "7bit",
        "sha256": "41c2ece339abfcf9ba1e7d5a60666162a24ef34ba95adeb41ed1d9721c91c6b0",
        "data": []
      },
      {
        "fieldname": "selfie",
        "name": "selfie.jpg",
        "size": 890098,
        "pages": 1,
        "mimetype": "image/jpeg",
        "encoding": "7bit",
        "sha256": "41c2ece339abfcf9ba1e7d5a60666162a24ef34ba95adeb41ed1d9721c91c6c4",
        "data": []
      }
    ],
    "timeSpent": 4930
  }
}

Response headers

HeaderWhen it appears
Nextid-ReqIdOn every response. Carries the request identifier, the same one as the id field in the body.

Errors

CodeWhen it happens
401 UnauthorizedAPI key or JWT token missing, invalid, or without permission for this endpoint.
404 Not FoundThe version given in the URL does not exist.
406 Not AcceptableThe body was sent as JSON, but without the base64 field.
413 Payload Too LargeOne of the files sent as multipart/form-data is larger than 15 MB.
415 Unsupported Media TypeThe Content-Type header is missing or is not supported.
422 Unprocessable EntityAn unknown query parameter or one with an invalid value, a file format that is not accepted, or more than two files.
500 Internal Server ErrorUnexpected failure during processing.

The error response format is described in HTTP response codes.

Versions

VersionStatusWhat changes
v2Recommendeddata is a list with one comparison, which carries confidence.

v2 is the supported version of this endpoint. It serves both /face-match/v2 and /face-match without a version in the URL.

Nextcode | Identity Verification Solutions