diff --git a/stdnum/in_/ifsc.py b/stdnum/in_/ifsc.py
new file mode 100644
index 00000000..1254909e
--- /dev/null
+++ b/stdnum/in_/ifsc.py
@@ -0,0 +1,102 @@
+# ifsc.py - functions for handling Indian IFSC bank branch codes
+#
+# Copyright (C) 2026 Yash Budhia
+#
+# 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 .
+
+"""IFSC (Indian Financial System Code).
+
+The Indian Financial System Code (IFSC) is an 11 character alphanumeric code
+that the Reserve Bank of India (RBI) assigns to every bank branch that takes
+part in the NEFT, RTGS and IMPS electronic funds transfer systems. It is
+required for every domestic electronic transfer in India and is also used to
+route UPI payments.
+
+The code consists of a 4 letter bank code, a fifth character that is reserved
+and always 0, and a 6 character branch code. The branch code is often numeric
+but may contain letters, which is common for cooperative and other sub-member
+banks that transact through a sponsor bank. There is no check digit.
+
+More information:
+
+* https://en.wikipedia.org/wiki/Indian_Financial_System_Code
+* https://www.rbi.org.in/Scripts/IFSCMICRDetails.aspx
+* https://github.com/razorpay/ifsc
+
+>>> validate('SBIN0000001')
+'SBIN0000001'
+>>> validate('hdfc0cabblm')
+'HDFC0CABBLM'
+>>> validate('SBIN000001')
+Traceback (most recent call last):
+ ...
+InvalidLength: ...
+>>> validate('SB1N0000001') # bank code must be letters
+Traceback (most recent call last):
+ ...
+InvalidFormat: ...
+>>> validate('SBIN1000001') # fifth character is reserved and must be 0
+Traceback (most recent call last):
+ ...
+InvalidComponent: ...
+>>> info('ICIC0000001')['bank_code']
+'ICIC'
+"""
+
+from __future__ import annotations
+
+import re
+
+from stdnum.exceptions import *
+from stdnum.util import clean
+
+
+_ifsc_re = re.compile(r'^[A-Z]{4}[A-Z0-9]{7}$')
+
+
+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, ' -').upper().strip()
+
+
+def validate(number: str) -> str:
+ """Check if the number provided is a valid IFSC. This checks the length
+ and formatting."""
+ number = compact(number)
+ if len(number) != 11:
+ raise InvalidLength()
+ if not _ifsc_re.match(number):
+ raise InvalidFormat()
+ if number[4] != '0':
+ raise InvalidComponent()
+ return number
+
+
+def is_valid(number: str) -> bool:
+ """Check if the number provided is a valid IFSC. This checks the length
+ and formatting."""
+ try:
+ return bool(validate(number))
+ except ValidationError:
+ return False
+
+
+def info(number: str) -> dict[str, str]:
+ """Provide information that can be decoded from the IFSC."""
+ number = compact(number)
+ return {
+ 'bank_code': number[:4],
+ 'branch_code': number[5:],
+ }
diff --git a/tests/test_in_ifsc.doctest b/tests/test_in_ifsc.doctest
new file mode 100644
index 00000000..36d9a41f
--- /dev/null
+++ b/tests/test_in_ifsc.doctest
@@ -0,0 +1,148 @@
+test_in_ifsc.doctest - more detailed doctests for stdnum.in_.ifsc module
+
+Copyright (C) 2026 Yash Budhia
+
+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.in_.ifsc module. It
+tries to cover more corner cases and detailed functionality that is not
+really useful as module documentation.
+
+>>> from stdnum.in_ import ifsc
+>>> from stdnum.exceptions import *
+
+These codes come from the openly maintained Razorpay IFSC dataset, which is
+derived from the list the RBI publishes, and should all be valid. They were
+picked to cover many different banks and both numeric and alphanumeric branch
+codes.
+
+>>> numbers = '''
+...
+... SBIN0000001
+... HDFC0000001
+... ICIC0000001
+... UTIB0000001
+... PUNB0000100
+... ABHY0065001
+... ADCC0000001
+... AIRP00000SC
+... AKJB0000001
+... ALLA0888888
+... AMCB0RTGS4S
+... ANDB0TRESUY
+... APBL0000001
+... APGB0000001
+... APGV0000001
+... APMC0000001
+... ASBL0000001
+... AUBL0000001
+... AUCB0000001
+... BACB0000001
+... BARB0BGGBXX
+... BARC0INBB01
+... BBKM0000001
+... BCBM0000001
+... BCEY0CHEN01
+... BDBL0000001
+... BKDN0RTGSSC
+... BKID000DCBL
+... BNPA0RTGSMI
+... BOFA0MM6205
+... BOTM0003611
+... CBIN0281102
+... CCBL0209003
+... CHAS0INBX01
+... CITI0RTGSMI
+... CIUB0000082
+... CLBL0000001
+... CNRB0RTGS01
+... COAS0000001
+... CORP0000920
+... COSB0000025
+... CRLY0000001
+... CRUB0000001
+... CSBK0000001
+... CSBX0CSB001
+... DBSS0IN0811
+... DCBL0000037
+... DEOB0000001
+... DEUT0000PBC
+... DLSC0000001
+... DLXB0000146
+... DMKJ0000001
+... DNSB0000001
+... DOHB0000001
+... EBIL0000001
+... ESFB0000001
+... ESMF0000001
+... FDRL0000379
+... FINO0000001
+... FSFB0000001
+...
+... '''
+>>> [x for x in numbers.splitlines() if x and not ifsc.is_valid(x)]
+[]
+
+
+The fifth character is reserved and must always be 0.
+
+>>> ifsc.validate('SBIN1000001')
+Traceback (most recent call last):
+ ...
+InvalidComponent: ...
+>>> ifsc.is_valid('SBINA000001')
+False
+
+
+The bank code is four letters and the branch code is six letters or digits.
+
+>>> ifsc.validate('SB1N0000001')
+Traceback (most recent call last):
+ ...
+InvalidFormat: ...
+>>> ifsc.validate('SBIN00000#1')
+Traceback (most recent call last):
+ ...
+InvalidFormat: ...
+
+
+The code is always exactly 11 characters long.
+
+>>> ifsc.validate('SBIN000001')
+Traceback (most recent call last):
+ ...
+InvalidLength: ...
+>>> ifsc.validate('SBIN00000012')
+Traceback (most recent call last):
+ ...
+InvalidLength: ...
+
+
+Lower case, surrounding whitespace and separators are normalised away.
+
+>>> ifsc.compact(' hdfc0cabblm ')
+'HDFC0CABBLM'
+>>> ifsc.validate('icic0000001')
+'ICIC0000001'
+>>> ifsc.validate('UTIB 0000001')
+'UTIB0000001'
+
+
+The code splits into a bank code and a branch code.
+
+>>> ifsc.info('UTIB0000001') == {'bank_code': 'UTIB', 'branch_code': '000001'}
+True
+>>> ifsc.info('HDFC0CABBLM')['branch_code']
+'CABBLM'