India-first PIN code intelligence library — production-grade, zero dependencies, pure Python.
Why BharatPin?
| Feature | bharatpin | indiapins | pypinindia | pgeocode |
|---|---|---|---|---|
| Pincode → State / District / Area | ✅ | ✅ | ✅ | ✅ |
| All areas / localities for a pincode | ✅ | ❌ | ❌ | ❌ |
| Real GPS coordinates (92.7% coverage) | ✅ | ❌ | ❌ | ✅ |
| Nearby pincodes (radius search) | ✅ | ❌ | ❌ | ✅ |
| Distance between pincodes | ✅ | ❌ | ❌ | ✅ |
| Fuzzy / typo-tolerant search | ✅ | ❌ | ❌ | ❌ |
| Old city names (Bombay, Madras, Calcutta…) | ✅ | ❌ | ❌ | ❌ |
| Address validation + suggestions | ✅ | ❌ | ❌ | ❌ |
| City tier classification (1 / 2 / 3) | ✅ | ❌ | ❌ | ❌ |
| HO / SO / BO office-type priority | ✅ | ❌ | ❌ | ❌ |
| Delivery vs Non-Delivery filter | ✅ | ❌ | ❌ | ❌ |
| CLI tool | ✅ | ❌ | ✅ | ❌ |
| Zero external dependencies | ✅ | ✅ | ❌ | ❌ |
| 2026 updated dataset | ✅ | ❌ | ❌ | ❌ |
Dataset
| Metric | Value |
|---|---|
| Post office records | 1,65,627 |
| Unique pincodes | 19,586 |
| States & Union Territories | 37 |
| GPS coordinate coverage | 92.7% |
| Source | India Post 2026 |
Installation
pip install bharatpin
Quick Start
import bharatpin
# Basic lookups
bharatpin.get_state("360001") # "Gujarat"
bharatpin.get_district("360001") # "Rajkot"
bharatpin.get_area("360001") # "Rajkot"
# All localities covered by a pincode
bharatpin.get_all_areas("360001")
# ['Rajkot', 'Rajkot City', 'Rajkot D H College', 'Rajkot Jn Plot', ...]
# Full details
bharatpin.get_pincode_info("360001")
# Fuzzy search — typos and old city names both work
bharatpin.search_area("bangalor") # finds Bengaluru
bharatpin.search_area("bombay") # finds Mumbai
bharatpin.search_area("calcutta") # finds Kolkata
# Geo
bharatpin.calculate_distance("360001", "110001") # 1077.42 km
bharatpin.find_nearby("360001", radius_km=25)
# Validate an address
bharatpin.validate_address("380001", district="Ahmedabad", state="Gujarat")
# City tier
bharatpin.get_city_tier("110001") # Tier 1 (Metro)
bharatpin.get_city_tier("360001") # Tier 2 (Major City)
API Reference
Core
bharatpin.get_pincode_info("360001") # full info dict + all_offices list
bharatpin.get_state("360001") # "Gujarat"
bharatpin.get_district("360001") # "Rajkot"
bharatpin.get_area("360001") # primary area name (suffix-stripped)
bharatpin.get_all_areas("360001") # every locality under the pincode
bharatpin.get_offices("360001") # all offices sorted HO → SO → BO
bharatpin.get_delivery_offices("360001")# delivery-only offices
bharatpin.get_stats() # dataset statistics
Reverse Lookups
bharatpin.get_pincodes_by_state("Gujarat") # all pincodes in a state
bharatpin.get_pincodes_by_district("Rajkot") # all pincodes in a district
bharatpin.get_pincodes_by_area("Rajkot") # pincodes for an area name
bharatpin.get_districts_by_state("Gujarat") # districts in a state
bharatpin.get_all_states() # all 37 states / UTs
Search
# Fuzzy area search — supports typos, partial names, and old city names
bharatpin.search_area("rajkot")
bharatpin.search_area("bangalor") # typo → Bengaluru
bharatpin.search_area("bombay") # old name → Mumbai
bharatpin.search_area("mumbai", state="Maharashtra", top_n=5)
# Pincode prefix search
bharatpin.search_pincode("3600") # all pincodes starting with 3600
Supported old / alternate city names (60+):
Bombay → Mumbai · Madras → Chennai · Calcutta → Kolkata ·
Bangalore → Bengaluru · Baroda → Vadodara · Poona → Pune ·
Trivandrum → Thiruvananthapuram · Simla → Shimla ·
Allahabad → Prayagraj · Gauhati → Guwahati · Gurgaon → Gurugram …
Geo
bharatpin.get_coordinates("360001")
# {'lat': 22.298, 'lng': 70.7972}
bharatpin.calculate_distance("360001", "110001") # km, Haversine
bharatpin.find_nearby("360001", radius_km=25)
# [{'pincode': '360020', 'area': 'Mavdi', 'distance_km': 3.21, ...}, ...]
bharatpin.find_nearby("360001", radius_km=100, state="Gujarat")
Address Validation
bharatpin.validate_address("380001", district="Ahmedabad", state="Gujarat")
# {'valid': True, 'state_match': True, 'district_match': True,
# 'actual': {'state': 'Gujarat', 'district': 'Ahmedabad', ...}}
bharatpin.validate_address("380001", state="Maharashtra")
# {'valid': False, 'state_match': False,
# 'message': "state mismatch: got 'Maharashtra', expected 'Gujarat'"}
bharatpin.suggest_correction("380001", state="Maharashtra")
# [{'pincode': '380001', 'state': 'Gujarat', 'confidence': 0.5}]
City Tier
bharatpin.get_city_tier("110001")
# {'tier': 1, 'label': 'Tier 1 (Metro)', 'area': 'New Delhi GPO', ...}
# Custom overrides for your business logic
bharatpin.set_custom_tiers({"Surat": 1, "Rajkot": 2})
bharatpin.set_custom_tiers({}) # reset
CLI
bharatpin lookup 360001
bharatpin search rajkot
bharatpin search bombay
bharatpin nearby 360001 --radius 25
bharatpin nearby 360001 --radius 100 --state Gujarat
bharatpin distance 360001 110001
bharatpin validate 380001 --state Gujarat --district Ahmedabad
bharatpin tier 110001
bharatpin stats
Common Use Cases
E-commerce — autofill city/state from pincode
info = bharatpin.get_pincode_info(user_pincode)
# Pre-fill checkout form with info["area"], info["district"], info["state"]
Logistics — serviceability check
dist = bharatpin.calculate_distance(warehouse_pin, customer_pin)
is_serviceable = dist is not None and dist <= 300
Fintech / KYC — address verification
result = bharatpin.validate_address(pin, district=district, state=state)
if not result["valid"]:
corrections = bharatpin.suggest_correction(pin, state=state)
Marketing — tier-based segmentation
tier = bharatpin.get_city_tier(customer_pin)["tier"] # 1, 2, or 3
Package Structure
bharatpin/
├── src/bharatpin/
│ ├── __init__.py # public API
│ ├── _loader.py # lazy singleton loader + Record dataclass
│ ├── core.py # O(1) dict lookups
│ ├── search.py # fuzzy search + alias resolution
│ ├── geo.py # Haversine distance + nearby
│ ├── address.py # validation + correction
│ ├── tier.py # city tier classification
│ ├── cli.py # CLI tool
│ └── data/
│ └── pincodes.csv # 1,65,627 rows — India Post 2026
├── tests/
├── assets/
│ └── bharatpin_logo.svg
├── pyproject.toml
└── README.md
Architecture
- Lazy loading — CSV is parsed once on first call, not at import time
- 5 in-memory indexes — all lookups are O(1) dictionary access
- Bounding-box pre-filter —
find_nearbyrejects out-of-range points before running Haversine - Zero dependencies — Haversine (
math), fuzzy search (trigrams), CSV (csv), CLI (argparse) — all stdlib
Contributing
Pull requests are welcome. For major changes, open an issue first.
git clone https://github.com/jeet308/bharatpin
cd bharatpin
pip install -e ".[dev]"
pytest tests/ -v
License
MIT © 2025 BharatPin Contributors
Metadata
Release files for bharatpin 1.0.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| bharatpin-1.0.1.tar.gz | 3.4 MB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| bharatpin-1.0.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 6.8 MB
Release files / bharatpin-1.0.1.tar.gz
| Download URL | bharatpin-1.0.1.tar.gz |
|---|---|
| Size | 3.4 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
3d74adcedc6260ca4a267387ac4711650240aae2b60484a7e599fd7c7819dd90
|
|
BLAKE2b-256 checksum How to use checksums |
c95d3521ea4def11976aa6d81da9e1477fd3296a65421af28e8e396269031201
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.9.7
|
Release files / bharatpin-1.0.1-py3-none-any.whl
| Download URL | bharatpin-1.0.1-py3-none-any.whl |
|---|---|
| Size | 3.4 MB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
441ec616933b6a25f6c25d50569fc3931bc753d5f81155964f5120d14c2cedec
|
|
BLAKE2b-256 checksum How to use checksums |
8b5b25266adf138fdbe3c987088fa37615e65d2059eaca677fbdb45fbefcf3d4
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.9.7
|