Thai National ID Card OCR
Skip the camera work — our free open-source eKYC SDK (Web, Flutter, iOS, Android & React Native) auto-captures documents and faces and calls this API for you. View on GitHub.
The Thai National ID Card OCR API extracts structured data from both faces of the Thai national ID card: the 13-digit ID number, names in Thai and English, address, dates, religion, and gender from the front, and the laser code from the back — with per-field confidence scores and optional bounding boxes, cropped images, and raw values. The production service processes a card in 1.4 seconds (median) and sustains 5,000 cards per hour. Measured field-level accuracy is 98.0 percent against human-verified ground truth, with the ID number at 100 percent; see Accuracy.
Live Demo
Upload a Thai national ID card, or use the synthetic specimens below.
Try Our AI Demo
Login or create a free account to use this AI service demo and explore our powerful APIs.
Get 100 Free Credits (IC) when you sign up!
Offer ends December 31, 2025
Try the SDK (Live Camera)
Prefer not to upload a file? The free open-source iApp eKYC Web SDK captures the card automatically from your camera — it detects the card boundary, waits for a sharp stable frame, perspective-corrects the image, and submits it to this API. See more flows on the full SDK live demo page.
Loading live demo…
Loading live demo…
Quick Start
You need an API key from the API Key Management page. Send the image as multipart/form-data:
curl -X POST https://api.iapp.co.th/v3/store/ekyc/thai-national-id-card/front \
-H "apikey: YOUR_API_KEY" \
-F "file=@idcard.jpg"
{
"id_number": "3411700830334",
"th_name": "นาง บุญยัง โลเปช",
"en_name": "Mrs. Bunyang Lopez",
"en_dob": "22 Mar. 1957",
"address": "33/349 หมู่ที่ 7 ต.หนองปรือ อ.บางละมุง จ.ชลบุรี",
"province": "ชลบุรี",
"detection_score": 0.979
}
The full response schemas, processing options, and error codes are in the Technical Reference.
Endpoints and Pricing
| Endpoint | Output | Price |
|---|---|---|
POST /v3/store/ekyc/thai-national-id-card/front | JSON with all front-side fields, per-field confidence, and optional bounding boxes and images | 1.25 IC per page |
POST /v3/store/ekyc/thai-national-id-card/back | JSON with the back-side laser code | 0.75 IC per page |
The legacy paths /thai-national-id-card/v3.5/front and /thai-national-id-card/v3.5.1/back remain supported at the same prices. For photographed photocopies, use Photographed Thai National ID Card With Signature Detection. For on-premise deployment, see Data Security.
Performance
Measured on the production service, August 2026.
| Metric | Value |
|---|---|
| Median processing time | 1.4 s per card (front); 1.3 s (back) |
| Sustained throughput | 1.4 cards per second (5,000 cards per hour) |
| Maximum file size | 10 MB |
| Supported input formats | JPEG, JPG, PNG, HEIC, HEIF, PDF |
Accuracy
Accuracy is measured against human-verified ground truth: 60 labeled Thai national ID cards, 1,380 individual field observations. The ground-truth dataset contains real ID cards and is kept private for privacy reasons; the methodology is fully specified in the whitepaper below.
| Field | Exact match |
|---|---|
| ID number (13 digits) | 100% |
| Surname (English), name titles, gender, religion | 100% |
| Sub-district, district, postal code | 100% |
| Province, dates of birth and expiry | 100% |
| Address (full) | 98.3% |
| Full name (Thai) | 93.3% |
| All fields (overall) | 98.0% |
The recognition engine was upgraded in August 2026, raising overall field accuracy from 97.8% to 98.0%, with the largest gains on addresses, province, and expiry dates. One honest caveat: Thai given/family-name fields moved slightly the other way (full Thai name 93.3%, versus 96.7% before) — if your integration keys specifically on Thai names rather than the ID number or address, validate them against the per-field confidence scores. Full methodology, per-field results, and comparison against a leading global cloud OCR service are published in the benchmark whitepaper:
Download the benchmark whitepaper (PDF)
Data Security and Compliance
- The service is GDPR and PDPA compliant.
- Uploaded images are processed in memory and are not retained after the response is returned.
- A fully self-contained on-premise deployment is available, in which no card data leaves your infrastructure. Contact us for details.
Technical Reference
Thai National ID Card Endpoints
1. Thai National ID Card - Front Side
POST /v3/store/ekyc/thai-national-id-card/front
Extracts detailed information from the front side of a Thai national ID card.
Request Headers
| Name | Type | Required | Description |
|---|---|---|---|
| apikey | string | Yes | Your API key |
Request Body (multipart/form-data)
| Parameter | Type | Required | Description |
|---|---|---|---|
| file | File | Yes | The binary data of the image (front side) |
| fields | String | No | Option to choose fields in output |
| options | String | No | Option to choose processing tools and settings |
2. Thai National ID Card - Back Side
POST /v3/store/ekyc/thai-national-id-card/back
Extracts information from the back side of a Thai national ID card.
Request Headers
| Name | Type | Required | Description |
|---|---|---|---|
| apikey | string | Yes | Your API key |
Request Body (multipart/form-data)
| Parameter | Type | Required | Description |
|---|---|---|---|
| file | File | Yes | The binary data of the image (back side) |
| options | String | No | Option to choose processing tools and settings |
Available Options
not_crop_card- Skip card croppingnot_rotate_card- Skip auto-rotationget_bbox- Return bounding box coordinatesget_image- Return processed imageget_original- Return original image
Response Format for Available Options
When using the available options, the response JSON will include additional fields based on the options selected:
Response with get_bbox Option
Returns bounding box coordinates for each detected field in the format [x1, y1, x2, y2] with confidence score:
{
"bbox": {
"address": [
[[119, 292, 376, 334], 0.991],
[[75, 330, 255, 362], 0.999]
],
"card": [[[0, 0, 713, 462], 0.99]],
"en_dob": [[[370, 235, 526, 266], 0.999]],
"face": [[[544, 218, 708, 407], 0.999]]
// Other field coordinates...
}
}
Response with get_image Option
Returns base64 encoded cropped images for each detected field:
{
"image": {
"address": ["/9j/4AAQSk...", "/9j/4AAQSk..."],
"card": ["/9j/4AAQSk..."],
"face": ["/9j/4AAQSk..."],
"id_number": ["/9j/4AAQSk..."]
// Other field images...
}
}
Response with get_original Option
Returns the original values before the autocorrect step (which occured in the post processing step) detected from the card:
{
"original": {
"address": "XXXXX XXXXX X ต.หนองปรือ อ.บางละมุง จ.ชลบุรี",
"en_dob": "XX Mar. 1957",
"en_expire": "XX Mar. 2025",
"th_name": "นาง XXXXXX โลเปซ"
// Other original field values...
}
}
The response will always include the standard fields like detection_score, error_message, and process_time regardless of options used.
Responses
Front side
Request:
curl -X POST https://api.iapp.co.th/v3/store/ekyc/thai-national-id-card/front
-H "apikey: YOUR_API_KEY"
-F "file=@/path/to/idcard.jpg"
Response:
{
"address": "XXXXXX XXXX X ต.หนองปรือ อ.บางละมุง จ.ชลบุรี",
"alley": "",
"detection_score": 0.981011797984441,
"district": "บางละมุง",
"en_dob": "XX Mar. 1957",
"en_expire": "XX Mar. 2025",
"en_fname": "XXXXXX",
"en_init": "Mrs.",
"en_issue": "26 Jul. 2016",
"en_lname": "Lopez",
"en_name": "Mrs. XXXXXX Lopez",
"error_message": "",
"face": "/9j/4AAQ...UAFFFFAH/9k=", # Base64 of photo's image
"gender": "Female",
"home_address": "XXXXX XXX",
"house_no": "XXXXXX",
"id_number": "34117XXXXXX334",
"id_number_status": 1,
"lane": "",
"postal_code": "20150", # Automatic Retrieve from Database
"process_time": 1.3018648624420166, # Seconds
"province": "ชลบุรี",
"religion": "",
"request_id": null,
"road": "",
"sub_district": "หนองปรือ",
"th_dob": "XX มี.ค. 2500",
"th_expire": "XX มี.ค. 2568",
"th_fname": "XXXXX",
"th_init": "นาง",
"th_issue": "XX ก.ค. 2559",
"th_lname": "โลเปซ",
"th_name": "นาง XXXXX โลเปซ",
"village": "",
"village_no": "7",
"confidence": {
"id_number": 0.95,
"th_name": 0.95,
"th_fname": 0.95,
"th_lname": 0.95,
"en_name": 0.95,
"en_fname": 0.95,
"en_lname": 0.95,
"th_dob": 0.9,
"en_dob": 0.9,
"th_issue": 0.9,
"en_issue": 0.9,
"th_expire": 0.9,
"en_expire": 0.9,
"address": 0.95,
"gender": 0.95,
"religion": 0.9
}
}
Backside
Request:
curl --location --request POST 'https://api.iapp.co.th/v3/store/ekyc/thai-national-id-card/back' \
--header 'apikey: {Your API Key}' \
--form 'file=@"path/to/id-card-back.jpg"'
Response:
{
"back_number": "JT0-XXXXXXX-05",
"detection_score": 0.99846746524175,
"process_time": 1.370201416015625
}
Supported Fields
Front Side
| Field | Type | Description |
|---|---|---|
| address | String | Address on the ID card |
| detection_score | float | Detection score of a related field |
| district | String | District name on the ID card |
| en_dob | String | Date of birth, in English |
| en_expire | String | Date of expiry, in English |
| en_fname | String | English given name |
| en_init | String | Name title, in English |
| en_issue | String | Date of issue, in Thai |
| en_lname | String | English surname |
| en_name | String | English given name and surname |
| error_message | String | Error message |
| face | String | Base64 character string converted from the image |
| gender | String | Gender |
| home_address | String | Home address on the ID card |
| id_number | String | National ID number |
| postal_code | String | Postal Code |
| process_time | String | Processing time (Sec.) |
| province | String | Province name on the ID card |
| religion | String | Religion on the ID card |
| sub_district | String | Sub district name on the ID card |
| th_dob | String | Date of birth, in Thai |
| th_expire | String | Date of expiry, in Thai |
| th_fname | String | Thai given name |
| th_init | String | Name title, in English |
| th_issue | String | Date of issue |
| th_lname | String | Thai surname |
| th_name | String | Thai given name and surname |
| confidence | Object | Confidence scores (0-1) for each extracted field |
Back side
| Field | Type | Description |
|---|---|---|
| back_number | String | Laser number on back of ID card |
Common Response Fields
| Field | Type | Description |
|---|---|---|
| detection_score | Float | Confidence score (0-1) |
| error_message | String | Error message if request fails |
| process_time | Float | Processing time in seconds |
Error Code
Specific Error Messages
Thai National ID Card Front Side
| Status Code | Status Message | Description |
|---|---|---|
| 420 | NO_ID_CARD_FOUND | The image file is not the ID card |
| 421 | IMAGE_ERROR_UNSUPPORTED_FORMAT | The image cannot be resolved. The file format may not be supported or the file is damaged |
| 422 | INVALID_IMAGE_SIZE | The size of the uploaded image does not meet the requirement |
| 424 | PLEASE_VERIFY_ID_CARD_NUMBER | The National ID Number has 13 digits but is incorrect. Response code 424 will show when using id_check option |
| 425 | CANNOT_READ_ID_CARD_NUMBER_CLEARLY | The National ID Number has less than 13 digits. Response code 425 will show when using id_check option |
| 426 | IMAGE_ERROR_UNSUPPORTED_BLACK_WHITE_IMAGE | Used gray_check option to check black-white and gray images |
Thai National ID Card Back Side
| Status Code | Status Message | Description |
|---|---|---|
| 430 | NO_ID_CARD_FOUND | The image file is not the ID Card |
| 431 | IMAGE_ERROR_UNSUPPORTED_FORMAT | The image cannot be resolved. The file format may not be supported or the file is damaged |
| 432 | INVALID_IMAGE_SIZE | The size of the uploaded image does not meet the requirement |
| 434 | LASER_NUMBER_NOT_FOUND | Cannot detect the laser number on back ID Card |
| 435 | CANNOT_READ_LASER_NUMBER_CLEARLY | The laser number has less than 12 digits. Response code 435 will show when using id_check option |
| 436 | IMAGE_ERROR_UNSUPPORTED_BLACK_WHITE_IMAGE | Used gray_check option to check black-white and gray images |
Common Error Messages
| Status Code | Status Message | Description |
|---|---|---|
| 404 | REQUESTED_URL_NOT_FOUND | API Route not found in request |
| 405 | METHOD_NOT_ALLOWED | Method name (GET, POST) in the URL is incorrect |
| 413 | FILE_IS_TOO_LARGE: (more than 2 MB) | The file size is too large |
| 427 | LONG_TIME_TO_PROCESS | The server is processed for a long time |
| 428 | LONG_TIME_TO_REQUEST | The client is waiting in the queue for a long time |
| 461 | NO_FILE_ATTACHED | No file attached |
| 560 | SERVER_IS_BUSY: (Please try again in a few seconds.) | Server is working on many requests |
| 563 | ID_CARD_API_NOT_SUPPORT_THIS_IMAGE | The API has an error in the process |
Code Examples
- cURL
- Python
- JavaScript
- PHP
- Swift
- Kotlin
- Java
- Dart
curl -X POST https://api.iapp.co.th/v3/store/ekyc/thai-national-id-card/front \
-H "apikey: YOUR_API_KEY" \
-F "file=@idcard.jpg"
import requests
url = "https://api.iapp.co.th/v3/store/ekyc/thai-national-id-card/front"
headers = {"apikey": "YOUR_API_KEY"}
files = {"file": open("idcard.jpg", "rb")}
response = requests.post(url, headers=headers, files=files)
print(response.json())
const axios = require("axios");
const FormData = require("form-data");
const fs = require("fs");
const data = new FormData();
data.append("file", fs.createReadStream("idcard.jpg"));
axios.post("https://api.iapp.co.th/v3/store/ekyc/thai-national-id-card/front", data, {
headers: { apikey: "YOUR_API_KEY", ...data.getHeaders() },
})
.then((response) => console.log(response.data))
.catch((error) => console.log(error));
<?php
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_URL => 'https://api.iapp.co.th/v3/store/ekyc/thai-national-id-card/front',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => array(
'file' => new CURLFILE('idcard.jpg')
),
CURLOPT_HTTPHEADER => array(
'apikey: YOUR_API_KEY'
),
));
$response = curl_exec($curl);
curl_close($curl);
echo $response;
?>
import Foundation
let url = URL(string: "https://api.iapp.co.th/v3/store/ekyc/thai-national-id-card/front")!
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.addValue("YOUR_API_KEY", forHTTPHeaderField: "apikey")
let boundary = "Boundary-\(UUID().uuidString)"
request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
var body = Data()
body.append("--\(boundary)\r\n".data(using: .utf8)!)
body.append("Content-Disposition: form-data; name=\"file\"; filename=\"idcard.jpg\"\r\n".data(using: .utf8)!)
body.append("Content-Type: image/jpeg\r\n\r\n".data(using: .utf8)!)
if let fileData = try? Data(contentsOf: URL(fileURLWithPath: "idcard.jpg")) {
body.append(fileData)
}
body.append("\r\n--\(boundary)--\r\n".data(using: .utf8)!)
request.httpBody = body
URLSession.shared.dataTask(with: request) { data, response, error in
if let data = data {
print(String(data: data, encoding: .utf8)!)
}
}.resume()
import okhttp3.*
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.RequestBody.Companion.asRequestBody
import java.io.File
val client = OkHttpClient()
val file = File("idcard.jpg")
val requestBody = MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("file", file.name, file.asRequestBody("image/jpeg".toMediaTypeOrNull()))
.build()
val request = Request.Builder()
.url("https://api.iapp.co.th/v3/store/ekyc/thai-national-id-card/front")
.addHeader("apikey", "YOUR_API_KEY")
.post(requestBody)
.build()
client.newCall(request).execute().use { response ->
println(response.body?.string())
}
import okhttp3.*;
import java.io.File;
OkHttpClient client = new OkHttpClient();
File file = new File("idcard.jpg");
RequestBody requestBody = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("file", file.getName(),
RequestBody.create(MediaType.parse("image/jpeg"), file))
.build();
Request request = new Request.Builder()
.url("https://api.iapp.co.th/v3/store/ekyc/thai-national-id-card/front")
.addHeader("apikey", "YOUR_API_KEY")
.post(requestBody)
.build();
try (Response response = client.newCall(request).execute()) {
System.out.println(response.body().string());
}
import 'package:http/http.dart' as http;
var request = http.MultipartRequest(
'POST',
Uri.parse('https://api.iapp.co.th/v3/store/ekyc/thai-national-id-card/front'),
);
request.files.add(await http.MultipartFile.fromPath('file', 'idcard.jpg'));
request.headers.addAll({'apikey': 'YOUR_API_KEY'});
var response = await request.send();
print(await response.stream.bytesToString());
Limitations and Best Practices
Limitations
- Maximum file size: 10MB
- Supported formats: JPEG, JPG, PNG, HEIC, HEIF, PDF
Best Practices
- Ensure good image quality
- Check detection_score in response
- Handle error messages appropriately
- Validate ID card number format
- Consider using options parameter for specific needs
Thai National ID Card Specifications
Understanding the physical specifications of Thai National ID cards helps ensure optimal image capture for OCR processing.
Card Dimensions (ขนาดบัตรประชาชน)
| Specification | Metric | Imperial (inches) |
|---|---|---|
| Width | 85.60 mm | 3.370 inches (3⅜") |
| Height | 53.98 mm | 2.125 inches (2⅛") |
| Thickness | 0.76 mm | 0.030 inches |
| Corner Radius | 3.18 mm | 0.125 inches |
บัตรประชาชนไทยมีขนาด 3.370 × 2.125 นิ้ว (85.60 × 53.98 มม.) ตามมาตรฐาน ISO/IEC 7810 ID-1 ซึ่งเป็นขนาดเดียวกับบัตรเครดิตทั่วโลก
Card Format Standards
| Standard | Description |
|---|---|
| Format | ISO/IEC 7810 ID-1 (Credit card size) |
| Material | Polycarbonate (PC) plastic |
| Chip Type | Contact smart card chip (since 2005) |
| Data Storage | Biometric data, digital certificate, fingerprint templates |
Card Features
- Front Side: Photo, name (Thai/English), ID number, date of birth, address, issue/expiry dates, barcode
- Back Side: Laser code (12-character alphanumeric)
- Security Features: Hologram, UV printing, microtext, laser engraving
- Smart Card Chip: Contains encrypted biometric data and digital signature
Image Requirements for OCR
For optimal OCR accuracy, ensure your captured images meet these requirements:
| Requirement | Recommended | Minimum |
|---|---|---|
| Resolution | 600 DPI | 300 DPI |
| Image Width | 1000+ pixels | 600 pixels |
| Image Height | 630+ pixels | 400 pixels |
| File Size | < 5 MB | < 10 MB |
| Format | JPEG, PNG | JPEG, PNG, HEIC, PDF |
Changelog
| Version | Date | Accuracy | Changes |
|---|---|---|---|
| v3.6.0-20260824 | 24 Aug 2026 | 98.0% field-level | English-format dates (en_dob, en_issue, en_expire) now match the card's printed format: abbreviated month names carry a dot (23 Apr. 1997); May, printed unabbreviated, has none. Verified on 60 cards — only these three fields changed and every Thai field is identical. Update client-side date parsing to accept the dotted form. |
| v3.5.3-20260824 | 24 Aug 2026 | 98.0% field-level | Upgraded recognition engine: overall field accuracy 97.8% to 98.0% against human-verified ground truth, ID number at 100%; largest gains on addresses, province, and expiry dates. Median processing 1.4 s per card. Fully self-contained on-premise deployment available. |
| 3.5 | Nov 2022 | 98.13% character-level | New OCR engine, faster processing. |
| 3.4 | Jul 2022 | 93.25% | Added Thai military ranks. |
| 3.0 | Feb 2021 | 91.75% | Major accuracy improvements. |
| 2.0 | Feb 2020 | 85% | Added back-side support. |
| 1.0 | Nov 2019 | 76% | Initial release. |
