Validating a Turkish TCKN or VKN (and the checksum algorithm behind each)
TCKN (11-digit citizen ID) and VKN (10-digit tax ID) carry different checksums — a length check alone lets garbage through. Both algorithms in plain code, plus the one-call version.
Every Turkish onboarding form, invoice and KYC check eventually asks for a TCKN (T.C. Kimlik No, the 11-digit citizen ID) or a VKN (Vergi Kimlik No, the 10-digit tax ID for companies and sole proprietors). Both look like "just digits", so it is tempting to check only the length. That lets through numbers no real ID or tax office would ever issue — both carry a checksum, and the two checksums are different algorithms.
TCKN: 11 digits, two check digits at the end
Given digits d1..d11:
d1may not be0.- Sum the odd-position digits (1st, 3rd, 5th, 7th, 9th) → call it
A. - Sum the even-position digits (2nd, 4th, 6th, 8th) → call it
B. d10 = (A * 7 - B) mod 10d11 = (sum of the first 10 digits) mod 10
function tcknChecksumOk(raw) {
const s = String(raw).trim();
if (!/^[1-9][0-9]{10}$/.test(s)) return false; // 11 digits, cannot start with 0
const d = [...s].map(Number);
const odd = d[0] + d[2] + d[4] + d[6] + d[8];
const even = d[1] + d[3] + d[5] + d[7];
const d10 = ((odd * 7) - even) % 10;
const d11 = d.slice(0, 10).reduce((a, b) => a + b, 0) % 10;
return d10 === d[9] && d11 === d[10];
}
tcknChecksumOk('10000000146'); // true — used as a public test number throughout this guide
tcknChecksumOk('10000000147'); // false — last digit tampered with
VKN: 10 digits, one check digit
The tax ID uses a different, more involved algorithm on the first 9 digits d1..d9
(0-indexed i = 0..8 below):
function vknChecksumOk(raw) {
const s = String(raw).trim();
if (!/^[0-9]{10}$/.test(s)) return false;
const d = [...s].map(Number);
let sum = 0;
for (let i = 0; i < 9; i++) {
const tmp = (d[i] + 9 - i) % 10;
let v = tmp === 9 ? 9 : (tmp * (2 ** (9 - i))) % 9;
if (tmp !== 0 && v === 0) v = 9;
sum += v;
}
const check = sum % 10 === 0 ? 0 : 10 - (sum % 10);
return check === d[9];
}
vknChecksumOk('1234567890'); // true — public test number
vknChecksumOk('1234567891'); // false — check digit does not match
The two algorithms are unrelated. A validator that runs the TCKN formula on a 10-digit input, or pads a VKN to 11 digits and runs the TCKN check, will silently pass or fail the wrong numbers — this happens more often than it should when a form field is shared between "ID number" and "tax number".
Doing it in one call
The endpoint auto-detects the type from length/shape, or you can pin it with type:
curl "https://api.temsor.com/v1/tr/validate?value=10000000146&type=tckn"
{ "input": "10000000146", "type": "tckn", "valid": true, "reason": null }
curl "https://api.temsor.com/v1/tr/validate?value=10000000147&type=tckn"
{ "input": "10000000147", "type": "tckn", "valid": false, "reason": "Sağlama haneleri tutmuyor." }
curl "https://api.temsor.com/v1/tr/validate?value=1234567891&type=vkn"
{ "input": "1234567891", "type": "vkn", "valid": false, "reason": "Son hane sağlaması tutmuyor." }
curl "https://api.temsor.com/v1/tr/validate?value=10000000146"
{ "input": "10000000146", "type": "tckn", "valid": true, "reason": null }
The last call omits type entirely — an 11-digit value not starting with 0 is
unambiguous, so it is auto-detected as tckn. A 10-digit value is auto-detected as
vkn. The reason field is what you show a user or log for an audit — a bare
false does not tell anyone which rule failed.
Mistakes that pass a length check but fail here
| Input | Why it looks plausible | What actually failed |
|---|---|---|
123 | Digits only | Not 11 digits, or starts with 0 — "11 hane olmalı ve 0 ile başlayamaz." |
05000000542 | Right length | Starts with 0 — no TCKN is ever issued starting with 0, same error as above |
10000000147 | Right length, does not start with 0 | Checksum digits do not match the first 9 |
1234567891 | Right length for a VKN | Check digit does not match — a single wrong final digit is the most common real-world typo |
What this does not tell you
A passing checksum means the number is structurally well-formed — the kind of number the issuing system could plausibly have generated. It does not confirm the number was actually issued, that it belongs to the person or company on your form, or that the associated ID is still active. That confirmation only exists inside the state registries (MERNİS for TCKN, GİB for VKN) and is not something a checksum — or this endpoint — can provide.
Frequently asked
Are the TCKN and VKN checksums the same algorithm?
No. TCKN uses two check digits derived from odd/even digit sums (mod 10 and a ×7 weighting); VKN uses one check digit from a different weighted-sum formula over the first nine digits. Running one algorithm on the other number type will give wrong answers.
Can a TCKN or VKN start with 0?
A TCKN cannot — the first digit must be 1–9. VKN has no such restriction on its first digit.
Does a valid checksum mean the number was actually issued to someone?
No. It only means the number is structurally consistent with how TCKNs/VKNs are constructed. Confirming it was actually issued requires checking the state registry (MERNİS for TCKN, GİB for VKN), which is outside what a checksum can tell you.
Does the endpoint need type to be specified?
No — leaving type as auto detects tckn vs vkn from the digit count and leading digit, since an 11-digit value not starting with 0 and a 10-digit value cannot be confused with each other.