diff --git a/stdnum/kz/__init__.py b/stdnum/kz/__init__.py
new file mode 100644
index 00000000..aa1ca58c
--- /dev/null
+++ b/stdnum/kz/__init__.py
@@ -0,0 +1,22 @@
+# __init__.py - collection of Kazakhstan numbers
+# coding: utf-8
+#
+# Copyright (C) 2022 Leandro Regueiro
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, see .
+
+"""Collection of Kazakhstan numbers."""
+
+# provide aliases
+from stdnum.kz import bin as vat # noqa: F401
diff --git a/stdnum/kz/bin.py b/stdnum/kz/bin.py
new file mode 100644
index 00000000..b1c3e301
--- /dev/null
+++ b/stdnum/kz/bin.py
@@ -0,0 +1,120 @@
+# bin.py - functions for handling Kazakhstan BIN numbers
+# coding: utf-8
+#
+# Copyright (C) 2022 Leandro Regueiro
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, see .
+
+"""BIN or "БСН" (Business Identification Number, "бизнес-сәйкестендіру нөмірі",
+Kazakhstan tax number).
+
+This number consists of 12 digits and consists of five parts:
+
+* First part: consists of 4 digits representing the year (the last two digits)
+ and month of registration or re-registration.
+* Second part: consists of 1 digit representing the type of legal entity or
+ individual entrepreneur (C):
+ 4 - for resident legal entities;
+ 5 - for non-resident legal entities;
+ 6 - for IP (C).
+* Third part: consists of 1 digit providing additional information:
+ 0 - head unit of a legal entity or individual entrepreneur (S);
+ 1 - branch of a legal entity or individual entrepreneur (S);
+ 2 - representation of a legal entity or individual entrepreneur (S);
+ 3 - a peasant (farm) farm operating on the basis of joint entrepreneurship.
+* Fourth part: consists of 5 digits and is a serial number.
+* Fifth part: consists of 1 check digit.
+
+More information:
+
+* https://www.oecd.org/tax/automatic-exchange/crs-implementation-and-assistance/tax-identification-numbers/Kazakhstan-TIN.pdf
+
+>>> validate('940140000385')
+'940140000385'
+>>> validate('990 140 004 654')
+'990140004654'
+>>> validate('12345')
+Traceback (most recent call last):
+ ...
+InvalidLength: ...
+>>> validate('12345678901X')
+Traceback (most recent call last):
+ ...
+InvalidFormat: ...
+>>> validate('949940000385')
+Traceback (most recent call last):
+ ...
+InvalidComponent: ...
+>>> validate('940190000385')
+Traceback (most recent call last):
+ ...
+InvalidComponent: ...
+>>> validate('940149000385')
+Traceback (most recent call last):
+ ...
+InvalidComponent: ...
+>>> validate('940140000386')
+Traceback (most recent call last):
+ ...
+InvalidChecksum: ...
+>>> format('990 140 004 654')
+'990140004654'
+""" # noqa: E501
+
+from stdnum.exceptions import *
+from stdnum.kz.iin import calc_check_digit
+from stdnum.util import clean, isdigits
+
+
+def compact(number: str) -> str:
+ """Convert the number to the minimal representation.
+
+ This strips the number of any valid separators and removes surrounding
+ whitespace.
+ """
+ return clean(number, ' ')
+
+
+def validate(number: str) -> str:
+ """Check if the number is a valid Kazakhstan BIN number.
+
+ This checks the length and formatting.
+ """
+ number = compact(number)
+ if len(number) != 12:
+ raise InvalidLength()
+ if not isdigits(number):
+ raise InvalidFormat()
+ if int(number[2:4]) not in (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12):
+ raise InvalidComponent()
+ if number[4] not in ('4', '5', '6'):
+ raise InvalidComponent()
+ if number[5] not in ('0', '1', '2', '3'):
+ raise InvalidComponent()
+ if calc_check_digit(number[:-1]) != number[-1]:
+ raise InvalidChecksum()
+ return number
+
+
+def is_valid(number: str) -> bool:
+ """Check if the number is a valid Kazakhstan BIN number."""
+ try:
+ return bool(validate(number))
+ except ValidationError:
+ return False
+
+
+def format(number: str) -> str:
+ """Reformat the number to the standard presentation format."""
+ return compact(number)
diff --git a/stdnum/kz/iin.py b/stdnum/kz/iin.py
new file mode 100644
index 00000000..319f80e8
--- /dev/null
+++ b/stdnum/kz/iin.py
@@ -0,0 +1,136 @@
+# iin.py - functions for handling Kazakhstani individual identification numbers
+# coding: utf-8
+#
+# Copyright (C) 2026 Luca Sicurello
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, see .
+
+"""IIN (ЖСН, Жеке сәйкестендіру нөмірі, Kazakhstani Individual identification number).
+
+It is a 12-digit number of which the first 6 digits denote the person's
+birth date in the format YYMMDD, the next digit encodes the gender and
+century of birth, the next four digits represent a birth serial number
+and the last digit is a check digit.
+
+* https://www.gov.kz/article/648?lang=kk
+* https://ru.wikipedia.org/wiki/Индивидуальный_идентификационный_номер
+
+
+>>> compact('921029 35085 1')
+'921029350851'
+>>> validate('921029350851')
+'921029350851'
+>>> get_birth_date('921029350851')
+datetime.date(1992, 10, 29)
+>>> validate('92102B50851') # invalid digit
+Traceback (most recent call last):
+ ...
+InvalidFormat: ...
+>>> validate('921529350859') # invalid date
+Traceback (most recent call last):
+ ...
+InvalidComponent: ...
+"""
+
+from __future__ import annotations
+
+import datetime
+
+from stdnum.exceptions import *
+from stdnum.util import clean, isdigits
+
+
+def compact(number: str) -> str:
+ """Convert the number to the minimal representation.
+
+ This strips the number of any valid separators and removes surrounding
+ whitespace.
+ """
+ return clean(number, ' ')
+
+
+def calc_check_digit(number: str) -> str:
+ """Calculate the check digit. The number passed should not have
+ the check digit included."""
+ s = sum(w * int(n) for w, n in zip(range(1, 12), number[:11])) % 11
+ if s != 10:
+ return str(s)
+
+ s = sum(w * int(n) for w, n in zip(range(3, 14), number[:11])) % 11
+ if s != 10:
+ return str(s)
+
+ raise InvalidComponent()
+
+
+def get_birth_date(number: str) -> datetime.date:
+ """Get the birth date from the person's Individual identification number."""
+ number = compact(number)
+ year = int(number[0:2])
+ month = int(number[2:4])
+ day = int(number[4:6])
+
+ if number[6] == '0': # no century info for foreign nationals
+ today = datetime.date.today()
+ year += (today.year // 100) * 100
+ if year >= today.year:
+ year -= 100
+
+ elif number[6] in '12':
+ year += 1800
+ elif number[6] in '34':
+ year += 1900
+ elif number[6] in '56':
+ year += 2000
+ else:
+ raise InvalidComponent()
+
+ try:
+ return datetime.date(year, month, day)
+ except ValueError:
+ raise InvalidComponent()
+
+
+def get_gender(number: str) -> str | None:
+ """Get the gender of the person's Individual identification number."""
+ number = compact(number)
+ if number[6] == '0':
+ return None
+ elif int(number[6]) % 2:
+ return 'M'
+ else:
+ return 'F'
+
+
+def validate(number: str) -> str:
+ """Check if the given Individual identification number is valid.
+ This checks the length and whether the check digit is correct."""
+ number = compact(number)
+ if not isdigits(number):
+ raise InvalidFormat()
+ if len(number) != 12:
+ raise InvalidLength()
+ if get_birth_date(number) > datetime.date.today():
+ raise InvalidComponent()
+ if calc_check_digit(number[:-1]) != number[-1]:
+ raise InvalidChecksum()
+ return number
+
+
+def is_valid(number: str) -> bool:
+ """Check if the given Individual identification number is valid."""
+ try:
+ return bool(validate(number))
+ except ValidationError:
+ return False
diff --git a/tests/test_kz_bin.doctest b/tests/test_kz_bin.doctest
new file mode 100644
index 00000000..82d1dcaf
--- /dev/null
+++ b/tests/test_kz_bin.doctest
@@ -0,0 +1,337 @@
+test_kz_bin.doctest - more detailed doctests for stdnum.kz.bin module
+
+Copyright (C) 2022 Leandro Regueiro
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, see .
+
+
+This file contains more detailed doctests for the stdnum.kz.bin module. It
+tries to test more corner cases and detailed functionality that is not really
+useful as module documentation.
+
+>>> from stdnum.kz import bin
+
+
+Tests for some corner cases.
+
+>>> bin.validate('940140000385')
+'940140000385'
+>>> bin.validate('990 140 004 654')
+'990140004654'
+>>> bin.format('990 140 004 654')
+'990140004654'
+>>> bin.validate('12345')
+Traceback (most recent call last):
+ ...
+InvalidLength: ...
+>>> bin.validate('12345678901X')
+Traceback (most recent call last):
+ ...
+InvalidFormat: ...
+>>> bin.validate('949940000385')
+Traceback (most recent call last):
+ ...
+InvalidComponent: ...
+>>> bin.validate('940190000385')
+Traceback (most recent call last):
+ ...
+InvalidComponent: ...
+>>> bin.validate('940149000385')
+Traceback (most recent call last):
+ ...
+InvalidComponent: ...
+>>> bin.validate('940140000386')
+Traceback (most recent call last):
+ ...
+InvalidChecksum: ...
+
+
+These have been found online and should all be valid numbers.
+
+>>> numbers = '''
+...
+... 940140000385
+... 171140900016
+... 011140004703
+... 981240000488
+... 031140003104
+... 980740000057
+... 050540004455
+... 011140001654
+... 100240019642
+... 181141900093
+... 990 140 004 654
+... 991240015834
+... 200240900095
+... 210840900029
+... 930440000156
+... 101241001053
+... 061241010270
+... 150440002169
+... 941240000341
+... 011140001666
+... 190740015218
+... 131040003151
+... 140640004517
+... 030840002763
+... 160740009199
+... 930340000261
+... 980340000290
+... 990240003283
+... 140 840 008 587
+... 220140006999
+... 110140001589
+... 960240007379
+... 090440007304
+... 961040000685
+... 910940000091
+... 910940000081
+... 990740001453
+... 960540000294
+... 900740000062
+... 960540000303
+... 990640001380
+... 930640000331
+... 960140000468
+... 081240013779
+... 141040004756
+... 141040030110
+... 981140001105
+... 981140001115
+... 020440002422
+... 180840019420
+... 141140001547
+... 910740000113
+... 910740000044
+... 910740000143
+... 910740000153
+... 910740000133
+... 910740000123
+... 040840002339
+... 091240012910
+... 150340016393
+... 180840002049
+... 051140003851
+... 090440002968
+... 090440002978
+... 090440003074
+... 150740005302
+... 141140001150
+... 940540000528
+... 000140001813
+... 940640000492
+... 050340004626
+... 991240001567
+... 950340000572
+... 961140000232
+... 021140002283
+... 900440000031
+... 910940000071
+... 900440000051
+... 021140002134
+... 961240001086
+... 961240001036
+... 961240001016
+... 980740001213
+... 980740001362
+... 980740001322
+... 090440004151
+... 141140000836
+... 980540000991
+... 970140000975
+... 980540000971
+... 980640000955
+... 970240000890
+... 980540001018
+... 971040000994
+... 970140000985
+... 980640000995
+... 961240000860
+... 980640000975
+... 980640000915
+... 940740000595
+... 141140000549
+... 020740001571
+... 920140000232
+... 020740001600
+... 900340000058
+... 020740001620
+... 900540000065
+... 950140000624
+... 020740001757
+... 940340000273
+... 990640001251
+... 000340001800
+... 020740001551
+... 020740001452
+... 080740008619
+... 990840001338
+... 991140001791
+... 020740001581
+... 090440000545
+... 180840003632
+... 141140002664
+... 950240000648
+... 940740000614
+... 920440000352
+... 961240000992
+... 970240000979
+... 910840000107
+... 931040000104
+... 090440011223
+... 141140001140
+... 990840001328
+... 991040001551
+... 991240001498
+... 991040001563
+... 990840001318
+... 990940001212
+... 991140001781
+... 000840001333
+... 980340001050
+... 991040001526
+... 991240001438
+... 990940001252
+... 990840000944
+... 990340000553
+... 991040001514
+... 991140001800
+... 991140000317
+... 991140001810
+... 990940001202
+... 141140002347
+... 141140001517
+... 940740000624
+... 991140001860
+... 950140000644
+... 931240000449
+... 940140000533
+... 940540000469
+... 950440000615
+... 940540000518
+... 941040000522
+... 950440000625
+... 941240000669
+... 941240000659
+... 090440011898
+... 141140001329
+... 980640000886
+... 950540000400
+... 950140000634
+... 950540000440
+... 941240000649
+... 950540000371
+... 950540000351
+... 981240000953
+... 980940001111
+... 950740000498
+... 900640000099
+... 970140000955
+... 961240000890
+... 900540000055
+... 900540000035
+... 900540000015
+... 900540000045
+... 970140000965
+... 031240002328
+... 141140001775
+... 980740001154
+... 941240000699
+... 961040000467
+... 940640000472
+... 961040000665
+... 961040000615
+... 961040000635
+... 090440021657
+... 141140001229
+... 960740000477
+... 960440000469
+... 960440000489
+... 970840000831
+... 970140001031
+... 960440000439
+... 960440000409
+... 021140002085
+... 960740000453
+... 970140001100
+... 980840001306
+... 021140001999
+... 960440000419
+... 960440000429
+... 961040000576
+... 970440000430
+... 970840000811
+... 941240000679
+... 961240000979
+... 961240001006
+... 900340000028
+... 990640001291
+... 990640001300
+... 990640001281
+... 990640001271
+... 120940010470
+... 990640001231
+... 070940008327
+... 040140002670
+... 141140000456
+... 021140001929
+... 980940001161
+... 980940001171
+... 980940001131
+... 980940001240
+... 980940001191
+... 980940001210
+... 980940001200
+... 980940001151
+... 980940001181
+... 980940001141
+... 980940001220
+... 980940001230
+... 141140001051
+... 980740001203
+... 980740001134
+... 980740001312
+... 980740001144
+... 980740001114
+... 980740001174
+... 980740001233
+... 980740000364
+... 120940007261
+... 980740001302
+... 980740001223
+... 980740001243
+... 980740001263
+... 980740001293
+... 141140001205
+... 021140001897
+... 021040001777
+... 021140002055
+... 021140002075
+... 990540001199
+... 021240002336
+... 021140001959
+... 021140001979
+... 021140001909
+... 021140001919
+... 021140001969
+... 021140001873
+... 020840001515
+... 021040001787
+... 180840015606
+... 180840013401
+... 210740028187
+...
+... '''
+>>> [x for x in numbers.splitlines() if x and not bin.is_valid(x)]
+[]
diff --git a/tests/test_kz_iin.doctest b/tests/test_kz_iin.doctest
new file mode 100644
index 00000000..1afac40a
--- /dev/null
+++ b/tests/test_kz_iin.doctest
@@ -0,0 +1,119 @@
+test_kz_iin.doctest - more detailed doctests for the stdnum.kz.iin module
+
+Copyright (C) 2026 Luca Sicurello
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, see .
+
+
+This file contains more detailed doctests for the stdnum.kz.iin module. It
+tries to validate a number of numbers that have been found online.
+
+>>> from stdnum.kz import iin
+>>> from stdnum.exceptions import *
+
+
+Tests for some corner cases.
+
+>>> iin.validate('921029 35085 1')
+'921029350851'
+>>> iin.is_valid('921029350851')
+True
+>>> iin.is_valid('921029350852')
+False
+>>> iin.validate('921029350852')
+Traceback (most recent call last):
+ ...
+InvalidChecksum: ...
+>>> iin.validate('921529350859') # wrong date
+Traceback (most recent call last):
+ ...
+InvalidComponent: ...
+>>> iin.validate('921029950850') # wrong gender code
+Traceback (most recent call last):
+ ...
+InvalidComponent: ...
+>>> iin.validate('921029351840') # wrong serial registration number
+Traceback (most recent call last):
+ ...
+InvalidComponent: ...
+>>> iin.validate('991231623804') # future date
+Traceback (most recent call last):
+ ...
+InvalidComponent: ...
+>>> iin.validate('123')
+Traceback (most recent call last):
+ ...
+InvalidLength: ...
+>>> iin.validate('92102935085A')
+Traceback (most recent call last):
+ ...
+InvalidFormat: ...
+>>> iin.get_gender('840621350384')
+'M'
+>>> iin.get_gender('850830402160')
+'F'
+>>> print(iin.get_gender('960206001028'))
+None
+>>> iin.get_birth_date('960206101024')
+datetime.date(1896, 2, 6)
+>>> iin.get_birth_date('960206001028')
+datetime.date(1996, 2, 6)
+>>> iin.get_birth_date('000206001029')
+datetime.date(2000, 2, 6)
+
+
+These have been found online and should all be valid numbers.
+
+>>> numbers = '''
+...
+... 790327250010
+... 660811300838
+... 750911301086
+... 780911400109
+... 821119301118
+... 840621350384
+... 841204301193
+... 850830402160
+... 870822400401
+... 871218302400
+... 901026350693
+... 910918301524
+... 921029350851
+... 940609350101
+... 950512300467
+... 960206301027
+... 960417300680
+... 970605350412
+... 970629300437
+... 980415451416
+... 980424301155
+... 990503301265
+... 000117551201
+... 000302501528
+... 000601500307
+... 001026550291
+... 010321501098
+... 011231601533
+... 020105500969
+... 020118551052
+... 030326651468
+... 040120651549
+... 040301501193
+... 040412500083
+... 050305501840
+... 050401650966
+...
+... '''
+>>> [x for x in numbers.splitlines() if x and not iin.is_valid(x)]
+[]