npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

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

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 (chnadigarhCHANDIGARH)
  • Geo search — find nearby post offices by lat/lng with bounding-box pruning + Haversine
  • Pagination built-in — limit, page, officeType, deliveryOnly filters
  • 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 injectable PincodeService
  • TypeScript first — full type definitions, ESM + CJS dual output
  • Lazy indexes — built on first use, cached forever (< 200ms cold start)

Installation

npm install india-pincode

React / 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 install and use. The getIndiaPincode() 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/browser entry. In older React Native versions, add a TextDecoder polyfill 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