india-pincode
v2.5.9
Published
Ultra-fast Indian pincode lookup — 165K+ post offices, state, district, geo search. Works in React, Node.js, NestJS, and any JS/TS project.
Downloads
343
Maintainers
Readme
india-pincode
Ultra-fast npm package for 165,000+ Indian post offices — lookup by pincode, state, district, area, coordinates. O(1) HashMap performance. Works everywhere: React, Next.js, Node.js, NestJS, and any JavaScript/TypeScript project.
Works With
| Environment | Import |
|---|---|
| React / Vue / Angular / Browser | import { getIndiaPincode } from 'india-pincode/browser' |
| React Native / Expo | import { getIndiaPincode } from 'india-pincode/browser' |
| Node.js / Express / Fastify | import { getIndiaPincode } from 'india-pincode' |
| NestJS | import { PincodeModule, PincodeService } from 'india-pincode/nestjs' |
| Next.js (Server) | import { getIndiaPincode } from 'india-pincode' |
| Next.js (Client) | import { getIndiaPincode } from 'india-pincode/browser' |
Features
- Universal — works in both frontend (React, Vue, Angular) and backend (Node.js, NestJS, Express)
- 165,000+ post offices across 36 states/UTs, 750 districts, 19,500+ unique pincodes
- O(1) lookups — pincode, state, district, area via pre-built HashMaps
- Smart suggestions — fuzzy "Did you mean?" for typos (
chnadigarh→CHANDIGARH) - Geo search — find nearby post offices by lat/lng with bounding-box pruning + Haversine
- Pagination built-in —
limit,page,officeType,deliveryOnlyfilters - Summary APIs — get pincode/district/state summaries in one call
- Error handling — all responses wrapped in
ApiResponse<T>with typed error codes and suggestions - Data validation — sanitized, deduplicated, stripped office suffixes (B.O, S.O, H.O, etc.)
- NestJS module —
@Global()module with injectablePincodeService - TypeScript first — full type definitions, ESM + CJS dual output
- Lazy indexes — built on first use, cached forever (< 200ms cold start)
Installation
npm install india-pincodeReact / Browser Usage
import { useEffect, useState } from 'react';
import { getIndiaPincode } from 'india-pincode/browser';
function PincodeSearch() {
const [result, setResult] = useState(null);
useEffect(() => {
async function lookup() {
const pin = await getIndiaPincode(); // async — loads data via fetch
const res = pin.getByPincode('110001');
if (res.success) setResult(res.data);
}
lookup();
}, []);
return <pre>{JSON.stringify(result, null, 2)}</pre>;
}Note: The browser version automatically fetches data from jsdelivr CDN — zero setup required. Just
npm installand use. ThegetIndiaPincode()call is async (returns a Promise). After initialization, all lookups are synchronous and instant. You can also pass a custom URL:getIndiaPincode('/my-data/pincodes.json.gz').
React Native / Expo Usage
import { useEffect, useState } from 'react';
import { Text, View } from 'react-native';
import { getIndiaPincode } from 'india-pincode/browser';
export default function App() {
const [count, setCount] = useState(0);
useEffect(() => {
async function lookup() {
const pin = await getIndiaPincode();
const res = pin.getByPincode('110001');
if (res.success) setCount(res.data.total);
}
lookup();
}, []);
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>Total offices in 110001: {count}</Text>
</View>
);
}React Native note: Use the
india-pincode/browserentry. In older React Native versions, add aTextDecoderpolyfill if your runtime does not provide it.
Node.js / Express Usage
import { getIndiaPincode } from 'india-pincode';
const pin = getIndiaPincode(); // sync — loads data from filesystem
// All the same methods work:
pin.getByPincode('110001');
pin.getByState('MAHARASHTRA');
pin.search('Koramangala');
pin.findNearby(28.6353, 77.225, 5);Quick Start (Standalone)
import { getIndiaPincode } from 'india-pincode';
const pin = getIndiaPincode();
// ─── O(1) Pincode Lookup ─────────────────────────────────
const result = pin.getByPincode('110001');
if (result.success) {
console.log(result.data);
// { data: [{ pincode: '110001', area: 'Baroda House', district: '...', ... }], total: 5, page: 1, ... }
} else {
console.log(result.error);
// { code: 'INVALID_PINCODE', message: '...' }
}
// ─── Pincode Summary ─────────────────────────────────────
const summary = pin.getPincodeSummary('845401');
if (summary.success) {
console.log(summary.data);
// {
// pincode: '845401',
// district: 'PURBI CHAMPARAN',
// state: 'BIHAR',
// country: 'INDIA',
// areas: [
// { pincode: '845401', area: 'Khairi Ajgarwa', latitude: 26.6704, longitude: 85.13756 },
// { pincode: '845401', area: 'Motihari Chowk', latitude: 26.3859, longitude: 84.5501 },
// ...
// ],
// deliveryAreas: 15,
// hasCoordinates: true
// }
}
// ─── Error Handling ──────────────────────────────────────
const bad = pin.getByPincode('abc');
// { success: false, data: null, error: { code: 'INVALID_PINCODE', message: '...' } }
const notFound = pin.getByPincode('999999');
// { success: false, data: null, error: { code: 'PINCODE_NOT_FOUND', message: '...' } }
// ─── By State (paginated) ────────────────────────────────
const mh = pin.getByState('MAHARASHTRA', { limit: 10, page: 2 });
if (mh.success) {
console.log(mh.data.data); // 10 records
console.log(mh.data.total); // total records in Maharashtra
console.log(mh.data.totalPages); // total pages
}
// ─── Filter: only Head Post Offices that deliver ─────────
pin.getByState('DELHI', { officeType: 'HO', deliveryOnly: true });
// ─── By District ─────────────────────────────────────────
pin.getByDistrict('PUNE', { limit: 20 });
// ─── By Area Name ────────────────────────────────────────
pin.getByArea('Koramangala');
// ─── District Summary ────────────────────────────────────
pin.getDistrictSummary('BANGALORE');
// { district: 'BANGALORE', state: 'KARNATAKA', country: 'INDIA', totalAreas: 120, totalPincodes: 85, pincodes: [...] }
// ─── State Summary ───────────────────────────────────────
pin.getStateSummary('KARNATAKA');
// { state: 'KARNATAKA', country: 'INDIA', totalDistricts: 31, totalAreas: 5200, totalPincodes: 3100, districts: [...] }
// ─── Lists ───────────────────────────────────────────────
pin.getAllStates(); // ['ANDHRA PRADESH', 'ASSAM', ...]
pin.getAllDistricts(); // ['ADILABAD', 'AGAR MALWA', ...]
pin.getDistrictsByState('UP'); // ['AGRA', 'ALIGARH', ...]
pin.getPincodesByState('GOA'); // ['403001', '403002', ...]
pin.getPincodesByDistrict('PUNE'); // ['410501', '411001', ...]
// ─── Search (free text) ─────────────────────────────────
pin.search('Koramangala');
pin.search('5600', { limit: 5 });
// ─── Geo: Find Nearby ───────────────────────────────────
const nearby = pin.findNearby(28.6353, 77.225, 5); // lat, lng, 5km radius
if (nearby.success) {
console.log(nearby.data);
// [{ ...office, distanceKm: 0.42 }, { ...office, distanceKm: 1.1 }, ...]
}
// ─── Validate Pincode Format ────────────────────────────
import { isValidPincode } from 'india-pincode';
isValidPincode('845401'); // true
isValidPincode('000000'); // false
isValidPincode('abc'); // false
// ─── Stats ───────────────────────────────────────────────
pin.getTotalRecords(); // 165000+
pin.getTotalPincodes(); // 19500+NestJS Integration
1. Import the module
import { Module } from '@nestjs/common';
import { PincodeModule } from 'india-pincode/nestjs';
@Module({
imports: [PincodeModule],
})
export class AppModule {}2. Inject the service
import { Controller, Get, Param, Query } from '@nestjs/common';
import { PincodeService } from 'india-pincode/nestjs';
@Controller('pincode')
export class PincodeController {
constructor(private readonly pincodeService: PincodeService) {}
@Get(':pincode')
getByPincode(@Param('pincode') pincode: string) {
return this.pincodeService.getByPincode(pincode);
}
@Get(':pincode/summary')
getSummary(@Param('pincode') pincode: string) {
return this.pincodeService.getPincodeSummary(pincode);
}
@Get('state/:state')
getByState(
@Param('state') state: string,
@Query('limit') limit?: number,
@Query('page') page?: number,
) {
return this.pincodeService.getByState(state, { limit, page });
}
@Get('district/:district')
getByDistrict(@Param('district') district: string) {
return this.pincodeService.getByDistrict(district);
}
@Get('area/:area')
getByArea(@Param('area') area: string) {
return this.pincodeService.getByArea(area);
}
@Get('nearby')
findNearby(
@Query('lat') lat: number,
@Query('lng') lng: number,
@Query('radius') radius?: number,
) {
return this.pincodeService.findNearby(lat, lng, radius);
}
@Get('search/:query')
search(@Param('query') query: string, @Query('limit') limit?: number) {
return this.pincodeService.search(query, { limit });
}
@Get('states')
getAllStates() {
return this.pincodeService.getAllStates();
}
@Get('districts/:state')
getDistricts(@Param('state') state: string) {
return this.pincodeService.getDistrictsByState(state);
}
}API Reference
Response Format
All lookup methods return ApiResponse<T>:
// Success
{ success: true, data: T }
// Error
{ success: false, data: null, error: { code: ErrorCode, message: string } }Error Codes:
| Code | When |
|------|------|
| INVALID_PINCODE | Pincode format is wrong (not 6 digits starting with 1-9) |
| PINCODE_NOT_FOUND | Valid format but no data exists for that pincode |
| STATE_NOT_FOUND | No data for given state name |
| DISTRICT_NOT_FOUND | No data for given district name |
| INVALID_COORDINATES | Lat/lng out of valid range |
| INVALID_INPUT | Empty or missing required input |
| NO_RESULTS | Search/area query returned no matches |
Core Lookups (O(1))
| Method | Returns |
|--------|---------|
| getByPincode(pincode, options?) | ApiResponse<PaginatedResult<PostOffice>> |
| getPincodeSummary(pincode) | ApiResponse<PincodeSummary> |
| getByState(state, options?) | ApiResponse<PaginatedResult<PostOffice>> |
| getByDistrict(district, options?) | ApiResponse<PaginatedResult<PostOffice>> |
| getByArea(name, options?) | ApiResponse<PaginatedResult<PostOffice>> |
Summaries
| Method | Returns |
|--------|---------|
| getStateSummary(state) | ApiResponse<StateSummary> |
| getDistrictSummary(district) | ApiResponse<DistrictSummary> |
Lists
| Method | Returns |
|--------|---------|
| getAllStates() | string[] |
| getAllDistricts() | string[] |
| getDistrictsByState(state) | ApiResponse<string[]> |
| getPincodesByState(state) | ApiResponse<string[]> |
| getPincodesByDistrict(district) | ApiResponse<string[]> |
Search & Geo
| Method | Returns |
|--------|---------|
| search(query, options?) | ApiResponse<PaginatedResult<PostOffice>> |
| findNearby(lat, lng, radiusKm?, limit?) | ApiResponse<NearbyResult[]> |
Utilities
| Method | Returns |
|--------|---------|
| isValidPincode(pincode) | boolean |
| getTotalRecords() | number |
| getTotalPincodes() | number |
| clearCache() | void |
QueryOptions
interface QueryOptions {
limit?: number; // default: 100
page?: number; // default: 1 (1-based)
officeType?: 'BO' | 'SO' | 'HO' | 'PO' | 'GPO';
deliveryOnly?: boolean;
}PostOffice
interface PostOffice {
pincode: string;
area: string;
officeType: 'BO' | 'SO' | 'HO' | 'PO' | 'GPO';
delivery: boolean;
district: string;
state: string;
country: string;
circle: string;
region: string;
division: string;
latitude: number | null;
longitude: number | null;
}PincodeSummary
interface PincodeSummary {
pincode: string;
district: string;
state: string;
country: string;
areas: AreaInfo[];
deliveryAreas: number;
hasCoordinates: boolean;
}
interface AreaInfo {
pincode: string;
area: string;
latitude: number | null;
longitude: number | null;
}Data Validation
All data is validated and sanitized at load time:
- Required fields: pincode, area, district, state (records missing any are dropped)
- Pincode: must be exactly 6 digits starting with 1–9
- Coordinates: latitude -90 to 90, longitude -180 to 180
- Area names: office suffixes (B.O, S.O, H.O, P.O, G.P.O, etc.) are stripped
- Deduplication: duplicate records (same pincode + area + district + officeType) are removed
- Sanitization: whitespace trimmed, "NA" values replaced, NaN/null/undefined rejected
Performance
| Operation | Time | Notes | |-----------|------|-------| | Pincode lookup | < 0.001ms | O(1) HashMap | | State/District/Area lookup | < 0.001ms | O(1) indexed | | Cold start (first call) | ~180ms | Loads & validates all records, builds indexes | | Subsequent calls | < 0.05ms | Everything cached | | Geo nearby (5km) | ~15ms | Bounding-box pre-filter + Haversine |
Smart Suggestions
All error responses include helpful suggestions when possible:
// Typo in state name → fuzzy match
pin.getByState('chnadigarh');
// error.suggestions: ['CHANDIGARH']
// Area name used as district → cross-index lookup
pin.getByDistrict('motihari');
// error.suggestions: ['PURBI CHAMPARAN']
// Invalid pincode (too long) → extracts valid substrings
pin.getByPincode('8454013');
// error.suggestions: ['845401']
// Partial pincode → prefix match
pin.getByPincode('11000');
// error.suggestions: ['110001', '110002', '110003', '110007', '110009']Runtime Differences
| | Node.js | Browser / React / React Native |
|---|---|---|
| Import | from 'india-pincode' | from 'india-pincode/browser' |
| Init | getIndiaPincode() (sync) | await getIndiaPincode() (async) |
| Data loading | Reads .gz file from disk | Fetches .gz from jsdelivr CDN (or custom URL) |
| Decompression | Node.js zlib | pako (browser-safe) |
| API methods | Identical | Identical |
| Bundle size | N/A (server) | ~12 KB + 3.3 MB data (loaded on demand) |
License
MIT
