KTP Details Extraction
curl --request POST \
--url https://{environment-subdomain}.idmetagroup.com/api/v3/verifications/indonesia/ktp-details-extraction-passthrough \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: multipart/form-data' \
--form documentImage=@example-fileimport requests
url = "https://{environment-subdomain}.idmetagroup.com/api/v3/verifications/indonesia/ktp-details-extraction-passthrough"
files = { "documentImage": ("example-file", open("example-file", "rb")) }
headers = {"Authorization": "Bearer <token>"}
response = requests.post(url, files=files, headers=headers)
print(response.text)const form = new FormData();
form.append('documentImage', '<string>');
const options = {method: 'POST', headers: {Authorization: 'Bearer <token>'}};
options.body = form;
fetch('https://{environment-subdomain}.idmetagroup.com/api/v3/verifications/indonesia/ktp-details-extraction-passthrough', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://{environment-subdomain}.idmetagroup.com/api/v3/verifications/indonesia/ktp-details-extraction-passthrough",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => "-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"documentImage\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001--",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: multipart/form-data"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://{environment-subdomain}.idmetagroup.com/api/v3/verifications/indonesia/ktp-details-extraction-passthrough"
payload := strings.NewReader("-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"documentImage\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001--")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://{environment-subdomain}.idmetagroup.com/api/v3/verifications/indonesia/ktp-details-extraction-passthrough")
.header("Authorization", "Bearer <token>")
.body("-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"documentImage\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001--")
.asString();require 'uri'
require 'net/http'
url = URI("https://{environment-subdomain}.idmetagroup.com/api/v3/verifications/indonesia/ktp-details-extraction-passthrough")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request.body = "-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"documentImage\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001--"
response = http.request(request)
puts response.read_body{
"success": true,
"message": "Indonesia KTP details extraction processed successfully",
"data": {
"outcome": "extracted",
"fields": {
"nik": {
"value": "3174012509870004",
"confidence": 99.4
},
"name": {
"value": "BUDI SANTOSO",
"confidence": 98.9
},
"placeOfBirth": {
"value": "JAKARTA",
"confidence": 99.1
},
"dateOfBirth": {
"value": "1987-09-25",
"confidence": 98.7
},
"gender": {
"value": "LAKI-LAKI",
"confidence": 99.7
},
"bloodType": {
"value": "O",
"confidence": 96.2
},
"address": {
"street": {
"value": "JL. MERPATI RAYA NO. 12",
"confidence": 97.3
},
"rt": {
"value": "003",
"confidence": 99
},
"rw": {
"value": "007",
"confidence": 99
},
"village": {
"value": "KEBAYORAN LAMA UTARA",
"confidence": 97.9
},
"district": {
"value": "KEBAYORAN LAMA",
"confidence": 98.1
}
},
"religion": {
"value": "ISLAM",
"confidence": 99.5
},
"maritalStatus": {
"value": "KAWIN",
"confidence": 98.9
},
"occupation": {
"value": "KARYAWAN SWASTA",
"confidence": 97.4
},
"citizenship": {
"value": "WNI",
"confidence": 99.7
},
"validUntil": {
"value": "SEUMUR HIDUP",
"confidence": 99.2
},
"province": {
"value": "DKI JAKARTA",
"confidence": 99.3
},
"city": {
"value": "JAKARTA SELATAN",
"confidence": 99
},
"issuedDate": {
"value": "2017-03-14",
"confidence": 96.7
},
"signature": null,
"photo": null
},
"missingFields": []
},
"verificationStatus": "VERIFIED",
"verificationStatusCode": 3,
"transactionId": "9f4c1b2e-8a3d-4c7f-b1e6-2d5a9c0f7e31",
"verificationType": "ktp_details_extraction",
"metadata": {
"clientReference": "ORDER-88213"
},
"createdAt": "2026-09-01 04:22:17+0000"
}
Document OCR
KTP Details Extraction
Extract structured fields from an Indonesian KTP without creating a Trust Validation.
POST
/
api
/
v3
/
verifications
/
indonesia
/
ktp-details-extraction-passthrough
KTP Details Extraction
curl --request POST \
--url https://{environment-subdomain}.idmetagroup.com/api/v3/verifications/indonesia/ktp-details-extraction-passthrough \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: multipart/form-data' \
--form documentImage=@example-fileimport requests
url = "https://{environment-subdomain}.idmetagroup.com/api/v3/verifications/indonesia/ktp-details-extraction-passthrough"
files = { "documentImage": ("example-file", open("example-file", "rb")) }
headers = {"Authorization": "Bearer <token>"}
response = requests.post(url, files=files, headers=headers)
print(response.text)const form = new FormData();
form.append('documentImage', '<string>');
const options = {method: 'POST', headers: {Authorization: 'Bearer <token>'}};
options.body = form;
fetch('https://{environment-subdomain}.idmetagroup.com/api/v3/verifications/indonesia/ktp-details-extraction-passthrough', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://{environment-subdomain}.idmetagroup.com/api/v3/verifications/indonesia/ktp-details-extraction-passthrough",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => "-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"documentImage\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001--",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: multipart/form-data"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://{environment-subdomain}.idmetagroup.com/api/v3/verifications/indonesia/ktp-details-extraction-passthrough"
payload := strings.NewReader("-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"documentImage\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001--")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://{environment-subdomain}.idmetagroup.com/api/v3/verifications/indonesia/ktp-details-extraction-passthrough")
.header("Authorization", "Bearer <token>")
.body("-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"documentImage\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001--")
.asString();require 'uri'
require 'net/http'
url = URI("https://{environment-subdomain}.idmetagroup.com/api/v3/verifications/indonesia/ktp-details-extraction-passthrough")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request.body = "-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"documentImage\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001--"
response = http.request(request)
puts response.read_body{
"success": true,
"message": "Indonesia KTP details extraction processed successfully",
"data": {
"outcome": "extracted",
"fields": {
"nik": {
"value": "3174012509870004",
"confidence": 99.4
},
"name": {
"value": "BUDI SANTOSO",
"confidence": 98.9
},
"placeOfBirth": {
"value": "JAKARTA",
"confidence": 99.1
},
"dateOfBirth": {
"value": "1987-09-25",
"confidence": 98.7
},
"gender": {
"value": "LAKI-LAKI",
"confidence": 99.7
},
"bloodType": {
"value": "O",
"confidence": 96.2
},
"address": {
"street": {
"value": "JL. MERPATI RAYA NO. 12",
"confidence": 97.3
},
"rt": {
"value": "003",
"confidence": 99
},
"rw": {
"value": "007",
"confidence": 99
},
"village": {
"value": "KEBAYORAN LAMA UTARA",
"confidence": 97.9
},
"district": {
"value": "KEBAYORAN LAMA",
"confidence": 98.1
}
},
"religion": {
"value": "ISLAM",
"confidence": 99.5
},
"maritalStatus": {
"value": "KAWIN",
"confidence": 98.9
},
"occupation": {
"value": "KARYAWAN SWASTA",
"confidence": 97.4
},
"citizenship": {
"value": "WNI",
"confidence": 99.7
},
"validUntil": {
"value": "SEUMUR HIDUP",
"confidence": 99.2
},
"province": {
"value": "DKI JAKARTA",
"confidence": 99.3
},
"city": {
"value": "JAKARTA SELATAN",
"confidence": 99
},
"issuedDate": {
"value": "2017-03-14",
"confidence": 96.7
},
"signature": null,
"photo": null
},
"missingFields": []
},
"verificationStatus": "VERIFIED",
"verificationStatusCode": 3,
"transactionId": "9f4c1b2e-8a3d-4c7f-b1e6-2d5a9c0f7e31",
"verificationType": "ktp_details_extraction",
"metadata": {
"clientReference": "ORDER-88213"
},
"createdAt": "2026-09-01 04:22:17+0000"
}
Submit one image of the front of an Indonesian KTP as
A blurred image can still produce
documentImage. The endpoint accepts JPG, JPEG, PNG, and PDF files up to 2 MB. The response returns every KTP field with its own confidence score, plus an overall data.outcome.
Image quality guide
The endpoint reads the KTP with OCR, so the quality of the image directly limits what it can extract. A sharp, evenly lit, uncropped image produces complete fields with high confidence. A blurred, dark, or partly covered image produces missing fields, low confidence scores, or values that were misread. A poor image does not usually make the request fail. The engine still returns an answer as HTTP200, and the call is billed. Check the fields described below before you rely on any extracted value.
Success, status, and quality are separate
Read the response in three layers. Each layer answers a different question.| Layer | Where to look | What it tells you |
|---|---|---|
| HTTP and API result | HTTP status and success | Whether the engine produced an answer. 200 with success: true means it did. A 400 or 500 with a code means it did not, and the call is not billed. |
| Verification status | verificationStatus and verificationStatusCode | How the extraction was graded: VERIFIED (3), REVIEW_NEEDED (2), or REJECTED (1). All three are HTTP 200 and billed. FAILED (6) is used only for coded failures. |
| Image quality | data.fields.<field>.confidence, null values, and data.missingFields | How reliable each extracted value is. Quality is judged per field, not for the response as a whole. |
VERIFIED if the NIK and name were readable. A very poor image of a real KTP can produce REJECTED with wrong_document_type. This happens when the engine cannot read enough of the card to recognise it as a KTP.
Recommended capture conditions
For guidance on capturing a clear, readable KTP image, see How to properly capture documents.Authorizations
Use your API token as a Bearer token in the Authorization header.
Headers
Example:
"Bearer {your_api_token}"
Body
multipart/form-data
Response
The document was processed. Check data.outcome and verificationStatus to determine the result.

