Passport

Comprehensive capture and extraction of Passports.

Verification of Passports is essential for onboarding processes and KYC compliance. It allows you to confirm the identity of an individual, ensuring that they are who they claim to be. This is vital for preventing identity fraud and building trust with your customers.

Introduction

Datakeen's OCR service automatically extracts information from French Passport.

  • Information is captured as structured data and can be extracted as a .json file through our API.
  • MRZ controls are information based checks operated on the MRZ band located on the bottom of the main page of the passport.
  • Authenticity checks generate a compliance score to determine whether the passport is authentic.

French Passport

Extracted informationMRZ controlsAuthenticity checks
- Name
- Surnames
- Spouse name
- Date of birth
- Place of birth
- State of birth
- Country of citizenship
- Gender
- Address
- Delivery date
- Expiry date
- Passport number
- Country code
- MRZ band
- Name on the MRZ
- Surname on the MRZ
- Passport number on the MRZ
- Date of birth on the MRZ
- Validity check
- Delivery date check
- Detection of identity photo
- Detection of hologram
- Public domain presence check

Setting up the API

The synchronous API model extracts data from French Passports in real time. The synchronous API model also performs verification checks in order to control Document Validity, MRZ conformity and Data consistency.

📘

API token is required

In order to perform any call, you will need an API token that can be retrieved with your API credentials. To learn about authentification, please refer to this page

curl --request POST \
     --url https://api.datakeen.co/api/v1/reco/id \
     --header 'accept: application/json' \
     --header 'content-type: application/json'
npm install api --save

const sdk = require('api')('@datakeen/v1.4.0#ax268r1ilnd0liqe');

sdk.postRecoId()
  .then(({ data }) => console.log(data))
  .catch(err => console.error(err));
require 'uri'
require 'net/http'

url = URI("https://api.datakeen.co/api/v1/reco/id")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Post.new(url)
request["accept"] = 'application/json'
request["content-type"] = 'application/json'

response = http.request(request)
puts response.read_body
composer require guzzlehttp/guzzle

<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('POST', 'https://api.datakeen.co/api/v1/reco/id', [
  'headers' => [
    'accept' => 'application/json',
    'content-type' => 'application/json',
  ],
]);

echo $response->getBody();
python -m pip install requests

import requests

url = "https://api.datakeen.co/api/v1/reco/id"

headers = {
    "accept": "application/json",
    "content-type": "application/json"
}

response = requests.post(url, headers=headers)

print(response.text)
CURL *hnd = curl_easy_init();

curl_easy_setopt(hnd, CURLOPT_CUSTOMREQUEST, "POST");
curl_easy_setopt(hnd, CURLOPT_WRITEDATA, stdout);
curl_easy_setopt(hnd, CURLOPT_URL, "https://api.datakeen.co/api/v1/reco/id");

struct curl_slist *headers = NULL;
headers = curl_slist_append(headers, "accept: application/json");
headers = curl_slist_append(headers, "content-type: application/json");
curl_easy_setopt(hnd, CURLOPT_HTTPHEADER, headers);

CURLcode ret = curl_easy_perform(hnd);
dotnet add package RestSharp

using RestSharp;


var options = new RestClientOptions("https://api.datakeen.co/api/v1/reco/id");
var client = new RestClient(options);
var request = new RestRequest("");
request.AddHeader("accept", "application/json");
request.AddHeader("content-type", "application/json");
var response = await client.PostAsync(request);

Console.WriteLine("{0}", response.Content);

CURL *hnd = curl_easy_init();

curl_easy_setopt(hnd, CURLOPT_CUSTOMREQUEST, "POST");
curl_easy_setopt(hnd, CURLOPT_WRITEDATA, stdout);
curl_easy_setopt(hnd, CURLOPT_URL, "https://api.datakeen.co/api/v1/reco/id");

struct curl_slist *headers = NULL;
headers = curl_slist_append(headers, "accept: application/json");
headers = curl_slist_append(headers, "content-type: application/json");
curl_easy_setopt(hnd, CURLOPT_HTTPHEADER, headers);

CURLcode ret = curl_easy_perform(hnd);
(require '[clj-http.client :as client])

(client/post "https://api.datakeen.co/api/v1/reco/id" {:content-type :json
                                                       :accept :json})
package main

import (
	"fmt"
	"net/http"
	"io"
)

func main() {

	url := "https://api.datakeen.co/api/v1/reco/id"

	req, _ := http.NewRequest("POST", url, nil)

	req.Header.Add("accept", "application/json")
	req.Header.Add("content-type", "application/json")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(string(body))

}
POST /api/v1/reco/id HTTP/1.1
Accept: application/json
Content-Type: application/json
Host: api.datakeen.co
OkHttpClient client = new OkHttpClient();

Request request = new Request.Builder()
  .url("https://api.datakeen.co/api/v1/reco/id")
  .post(null)
  .addHeader("accept", "application/json")
  .addHeader("content-type", "application/json")
  .build();

Response response = client.newCall(request).execute();
const options = {
  method: 'POST',
  headers: {accept: 'application/json', 'content-type': 'application/json'}
};

fetch('https://api.datakeen.co/api/v1/reco/id', options)
  .then(response => response.json())
  .then(response => console.log(response))
  .catch(err => console.error(err));
val client = OkHttpClient()

val request = Request.Builder()
  .url("https://api.datakeen.co/api/v1/reco/id")
  .post(null)
  .addHeader("accept", "application/json")
  .addHeader("content-type", "application/json")
  .build()

val response = client.newCall(request).execute()
#import <Foundation/Foundation.h>

NSDictionary *headers = @{ @"accept": @"application/json",
                           @"content-type": @"application/json" };

NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"https://api.datakeen.co/api/v1/reco/id"]
                                                       cachePolicy:NSURLRequestUseProtocolCachePolicy
                                                   timeoutInterval:10.0];
[request setHTTPMethod:@"POST"];
[request setAllHTTPHeaderFields:headers];

NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request
                                            completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
                                                if (error) {
                                                    NSLog(@"%@", error);
                                                } else {
                                                    NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) response;
                                                    NSLog(@"%@", httpResponse);
                                                }
                                            }];
[dataTask resume];
opam install cohttp-lwt-unix cohttp-async

open Cohttp_lwt_unix
open Cohttp
open Lwt

let uri = Uri.of_string "https://api.datakeen.co/api/v1/reco/id" in
let headers = Header.add_list (Header.init ()) [
  ("accept", "application/json");
  ("content-type", "application/json");
] in

Client.call ~headers `POST uri
>>= fun (res, body_stream) ->
  (* Do stuff with the result *)
$headers=@{}
$headers.Add("accept", "application/json")
$headers.Add("content-type", "application/json")
$response = Invoke-WebRequest -Uri 'https://api.datakeen.co/api/v1/reco/id' -Method POST -Headers $headers
library(httr)

url <- "https://api.datakeen.co/api/v1/reco/id"

response <- VERB("POST", url, content_type("application/json"), accept("application/json"))

content(response, "text")
import Foundation

let headers = [
  "accept": "application/json",
  "content-type": "application/json"
]

let request = NSMutableURLRequest(url: NSURL(string: "https://api.datakeen.co/api/v1/reco/id")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()

🚧

Sending multiple documents

Scans, pictures and documents should be sent one by one. If you want to send multiple scans at the same time, please refer to the Document bundle API.

API Response

An instance of the usual response is displayed in the following JSON. You will find the complete information, including extraction and checks. More detailed examples of each extracted field are given below.

{
    "cardSide": "front",
    "cardType": "biometric_passport_fr",
    "controls": {
        "dateValidity": {
            "confidence": 0.7675064531593989,
            "value": true
        },
        "dateValidityDelivery": {
            "confidence": 0.8287575744547411,
            "value": true
        },
        "globalStatus": {
            "confidence": 1,
            "score": 0.7,
            "value": true
        },
        "hologramIsPresent": {
            "confidence": 0.8960498571395874,
            "value": true
        },
        "mrzConformity": {
            "confidence": 0.0,
            "value": true
        },
        "mrzValidity": {
            "confidence": 0.0,
            "value": false
        },
        "photoIsPresent": {
            "confidence": 0.9691312313079834,
            "value": true
        }
    },
    "extractedInformation": {
        "address": {
            "confidence": 0.7903852219955034,
            "value": "74 RUE MONGE 21000 DIJON FRANCE"
        },
        "birthCountry": {
            "confidence": null,
            "value": ""
        },
        "birthDate": {
            "confidence": 0.8385196726772648,
            "value": "09.09.1975"
        },
        "birthDateMRZ": {
            "confidence": 0,
            "value": "09.09.1975"
        },
        "birthDepartment": {
            "confidence": 0.9,
            "value": "75"
        },
        "birthPlace": {
            "confidence": 0.5156590247929245,
            "value": "PARIS 9E ARRONDISSEMENT"
        },
        "countryCode": {
            "confidence": 0.9,
            "value": "FR"
        },
        "deliveryDate": {
            "confidence": 0.8287575744547411,
            "value": "31.01.2014"
        },
        "expiryDate": {
            "confidence": 0.7675064531593989,
            "value": "30.01.2024"
        },
        "firstName": {
            "confidence": 0.7709198893060503,
            "value": "Arnaud Claude Norbert"
        },
        "firstNameMRZ": {
            "confidence": 0,
            "value": "ARNAUD CLAUDE NORBERT"
        },
        "gender": {
            "confidence": null,
            "value": ""
        },
        "idNumber": {
            "confidence": 0.7870982014097763,
            "value": "14AF16156"
        },
        "idNumberMRZ": {
            "confidence": 0,
            "value": "14AF16156"
        },
        "lastName": {
            "confidence": 0.7879640629055729,
            "value": "FINISTRE"
        },
        "lastNameMRZ": {
            "confidence": 0,
            "value": "FINISTRE"
        },
        "mrz": {
            "confidence": 0.7007020735227627,
            "value": "P<FRAFINISTRE<<ARNAUD<CLAUDE<NORBERT<<<<<<<<\n14AF161561FRA7509096M2401302<<<<<<<<<<<<<<06"
        },
        "nationality": {
            "confidence": null,
            "value": ""
        },
        "spouseName": {
            "confidence": null,
            "value": ""
        }
    },
    "message": "",
    "status": 200
}

Document type and side

For each Passport sent, the response will include information about the type and side. The cardType will be biometric_passport_fr andcardSide front.

keyvalue
cardTypestring
cardSidestring
"cardSide": "front",
"cardType": "biometric_passport_fr",

Extracted information format

For each field, the confidence value indicates the degree of certainty of the extraction with regard to the data on the card.

Name

The lastName (key) of the card holder is returned as a string (value).

lastNameValue
valuestring
confidencenumber
"lastName": {
  "confidence": 0.7986501646692794,
  "value": "BERTHIER"
}

Surnames

The firstName (key) of the card holder is returned as a string (value) separated by commas ",".

firstNameValue
valuestring
confidencenumber
"firstName": {
  "confidence": 0.7986501646692794,
  "value": "CORINNE"
}

Spouse name

The spouse (key) of the card holder is returned as a string (value).

spouseValue
valuestring
confidencenumber
"spouse": {
  "confidence": 0.7986501646692794,
  "value": "CORINNE"
}

Date of birth

The birthDate (key) of the card holder is returned as a string (value).

birthDateValue
valuestring
confidencenumber
"birthDate": {
  "confidence": 0.7335174141228151,
  "value": "06.12.1965"
}

Place of birth

The birthPlace (key) of the card holder is returned as a string (value).

birthPlaceValue
valuestring
confidencenumber
"birthPlace": {
  "confidence": 0.7804153092105253,
  "value": "PARIS 1ER ( 75 )"
}

State of birth

The birthDepartement (key) of the card holder is returned as a string (value).

birthDepartementValue
valuestring
confidencenumber
"birthDepartment": {
  "confidence": 0.9,
  "value": "99"
},

Country of citizenship

The nationality (key) of the card holder is returned as a string (value).

nationalityValue
valuestring
confidencenumber
"nationality": {
  "confidence": null,
  "value": "FRA"
}

Gender

The gender or gender (key) of the card holder is returned as a string (value).

genderValue
valuestring
confidencenumber
"gender": {
  "confidence": 0.8408435583114624,
  "value": "F"
}

Address

The address (key) of the card holder is returned as a string (value).

addressValue
valuestring
confidencenumber
"address": {
  "confidence":0.823455583114624,
  "value": "31 avenue de la république, Paris 13"
}

Delivery date

The deliveryDate (key) of the card holder is returned as a string (value).

deliveryDateValue
valuestring
confidencenumber
"deliveryDate": {
  "confidence": 0.8528435583114624,
  "value": "10.05.2005"
}

Expiry date

The expiryDate (key) of the card holder is returned as a string (value).

expiryDateValue
valuestring
confidencenumber
"expiryDate": {
  "confidence": 0.8528435583114624,
  "value": "10.05.2010"
}

Card number

The idNumber (key) of the card holder is returned as a string (value).

idNumberValue
valuestring
confidencenumber
"idNumber": {
  "confidence": 0.7615569009560375,
  "value": "940992310285"
}

MRZ

The mrz (key) of the card holder is returned as a string (value).

value
value

Country code

The countryCode (key) of the card holder is returned as a string (value).

countryCodeValue
valuestring
confidencenumber
"countryCode": {
  "confidence": 0.9,
  "value": "FR"
}

Control results format

The controls applied to each Passport can be extracted in a boolean format. The confidence field indicated the degree of certainty of the control.

Global status

The globalStatus returns, as a boolean (value), whether the Passport is accepted or rejected with regard to our global minimum accepted rates.

globalStatusValue
valueboolean
confidencenumber
"globalStatus": {
  "confidence": 1,
  "score": 0.7,
  "value": true
}

MRZ control

The mrzValidity controls the MRZ information with regard to the previously extracted fields. It is returned as a boolean (value).

mrzValidityValue
valueboolean
confidencenumber
"mrzValidity": {
  "confidence": 0.0,
  "value": false
}

MRZ conformity

The mrzConformity controls the conformity of the MRZ. It is returned as a boolean (value).

mrzConformityValue
valueboolean
confidencenumber
"mrzConformity": {
  "confidence": 0.0,
  "value": true
}

Date validity

The dateValidity controls the current validity of the Passport with regard to the expiry date. It is returned as a boolean (value).

dateValidityValue
valueboolean
confidencenumber
"dateValidity": {
  "confidence": 0.7675064531593989,
  "value": true
}

Date validity delivery

The dateValidityDelivery controls the current validity of the document with regard to the delivery date. It is returned as a boolean (value).

dateValidityDeliveryValue
valueboolean
confidencenumber
"dateValidityDelivery": {
  "confidence": 0.8287575744547411,
  "value": true
}

Detection of photo

The photoIsPresent checks if the identity photo is detected on the document. It is returned as a boolean (value).

photoIsPresentValue
valueboolean
confidencenumber
"photoIsPresent": {
  "confidence": 0.9691312313079834,
  "value": true
}

Detection of photo background

The backgroundPhotoIsPresent checks if the background of the identity photo is detected on the document. It is returned as a boolean (value).

backgroundPhotoIsPresentValue
valueboolean
confidencenumber

Detection of hologram

The hologramIsPresent checks if the hologram is detected on the document. It is returned as a boolean (value).

hologramIsPresentValue
valueboolean
confidencenumber
"hologramIsPresent": {
  "confidence": 0.8960498571395874,
  "value": true
}

Field matching

The use can provided, optionally, the expected information as an input to verify if they correspond to the extracted information. userInput supports the information as shown bellow in the example.

userInputValue
firstNamestring
lastNamestring
birthDatestring
genderstring
photostring (base64 encoded file)
"userInput": {
  "firstName": "",
  "lastName": "",
  "birthDate": "",
  "gender": "",
  "photo": "",

By providing a base64 string encoded photo of the person in userInput, we verify if is the same person present on the card.

Here is the as output example for field matching:

{
  "matchFirstName" : {
    "value": true,
    "confidence": 0.8
  },
  "matchLastName" : {
    "value": true,
    "confidence": 0.8
  },
  "matchbirthDate" : {
    "value": true,
    "confidence": 0.8
  },
  "matchGender" : {
    "value": true,
    "confidence": 0.8
  },
  "matchPhoto" : {
    "value": true,
    "confidence": 0.8
  }
}

Additional information

🚧

Loaded scans must pass prerequisites

To provide a qualitative service and a comprehensive data capture, every picture, scan, or document sent to our API must comply with determined prerequisites which can be found on this page

❗️

API limitations

  • Maximum size : 5 MB
  • Maximum number of calls per minute : 10 calls

What’s Next

Ready to process passports ? See our API Reference for detailed information