@bzync/ph-address-intel
v0.1.8
Published
Headless Philippine address lookup library with PSGC hierarchy and ZIP code autofill
Maintainers
Readme
PH Address Intel
Framework-agnostic TypeScript library for Philippine address lookup, ZIP autofill, and hierarchical region → province → municipality → barangay selection.
Features
- ZIP Autofill — Resolve a 4-digit ZIP code into region, province, municipality, and barangays.
- Cascading Selection — Full PSGC hierarchy:
getRegions → getProvinces → getMunicipalities → getBarangays - Full Path Resolution — Resolve any PSGC code upward through the hierarchy with
getFullPath. - Free-Text Search — Search across all address levels with optional fuzzy matching and parent scoping.
- Alias Resolution — Resolve region nicknames and abbreviations (e.g.
"NCR","CALABARZON") to PSGC codes. - Validation — Verify that address codes exist and form a consistent hierarchy.
- Zero Runtime Dependencies — No API calls. Works offline and at the edge.
- TypeScript First — Fully typed API with
.d.tssupport (CJS + ESM). - Official PSGC Data — Based on PSA PSGC Q4 2025. Covers 17 regions, 80 provinces, 42,000+ barangays.
Installation
npm install @bzync/ph-address-intelyarn add @bzync/ph-address-intel
pnpm add @bzync/ph-address-intel
bun add @bzync/ph-address-intelQuick Start
import {
lookupByZip,
getRegions,
getProvinces,
getMunicipalities,
getBarangays,
getFullPath,
search,
resolveAlias,
validate,
} from '@bzync/ph-address-intel'
// ZIP autofill
const result = lookupByZip('4322')
// Hierarchical selection
const regions = getRegions()
const provinces = getProvinces('040000000')
const municipalities = getMunicipalities('045600000')
const barangays = getBarangays('045645000')
// Full path from any PSGC code
const path = getFullPath('045645001') // barangay → full chain
// Scoped search (municipalities within CALABARZON only)
const hits = search('San Jose', { types: ['municipality'], parentCode: '040000000' })
// Alias resolution
const ncr = resolveAlias('NCR')
// Validation
const check = validate({ regionCode: '040000000', provinceCode: '045600000' })API Reference
lookupByZip(zip: string): ZipLookupResult | null
Resolves a 4-digit ZIP code to the full address hierarchy.
const result = lookupByZip('4322')
if (result) {
console.log(result.region.name) // "Region IV-A (CALABARZON)"
console.log(result.province?.name) // "Quezon"
console.log(result.municipality.name) // "Sariaya"
console.log(result.barangays.length) // number of barangays
}Hierarchical getters
getRegions(): Region[]
getProvinces(regionCode: string): Province[]
getMunicipalities(provinceCode: string): Municipality[]
getBarangays(municipalityCode: string): Barangay[]Returns all items at each level. Pass the parent PSGC code to get children.
Single-item lookups
getRegion(code: string): Region | undefined
getProvince(code: string): Province | undefined
getMunicipality(code: string): Municipality | undefined
getBarangay(code: string): Barangay | undefinedO(1) lookups by PSGC code.
getFullPath(code: string): FullPath | null
Resolves any PSGC code upward through the hierarchy and returns the complete address chain.
// From a barangay code
const path = getFullPath('045645001')
// → { region, province, municipality, barangay }
// From a municipality code
const path = getFullPath('045645000')
// → { region, province, municipality, barangay: null }
// NCR municipality (no province)
const path = getFullPath('133900000')
// → { region, province: null, municipality, barangay: null }
// Unknown code
getFullPath('999999999') // → nullAccepts codes at any level — region, province, municipality, or barangay. Levels below the given code are null.
search(query: string, options?: SearchOptions): SearchResult[]
Searches across all address levels. Supports substring matching (default) and fuzzy matching, with optional scoping to a parent region, province, or municipality.
// Substring search
search('Manila')
// Fuzzy search with options
search('Mandaluyong', { fuzzy: true, limit: 5, types: ['municipality'] })
// Scoped to a region (useful for cascading dropdowns)
search('San Jose', { types: ['municipality'], parentCode: '040000000' })
// Scoped to a municipality
search('Antipolo', { types: ['barangay'], parentCode: '045645000' })SearchOptions
| Option | Type | Default | Description |
|--------------|------------------------------------------------------------------|---------|------------------------------------------------------|
| fuzzy | boolean | false | Enable Dice's bigram fuzzy matching |
| limit | number | — | Max results to return |
| types | Array<'region' \| 'province' \| 'municipality' \| 'barangay'> | all | Restrict search to specific levels |
| parentCode | string | — | Scope results to children of a region, province, or municipality |
Results are sorted by score (descending) when fuzzy: true. When parentCode is set, regions are excluded from results (the parent context is already known).
resolveAlias(alias: string): SearchResult | null
Resolves a common region nickname, abbreviation, or number to a SearchResult. Returns null if not recognized.
resolveAlias('NCR') // → { type: 'region', code: '130000000', name: '...' }
resolveAlias('Region IV-A') // → { type: 'region', code: '040000000', name: '...' }
resolveAlias('BARMM') // → { type: 'region', code: '150000000', name: '...' }
resolveAlias('unknown') // → nullRecognized aliases include region numbers (Region 1–13), Roman numerals (Region I–XIII), official names, and common shorthands (NCR, CAR, CALABARZON, MIMAROPA, CARAGA, etc.).
validate(input: ValidationInput): ValidationResult
Validates that the provided PSGC codes exist and form a consistent hierarchy. Only fields that are provided are validated — partial inputs are fine.
validate({ regionCode: '040000000', provinceCode: '045600000' })
// → { valid: true, errors: [] }
validate({ regionCode: '040000000', provinceCode: '010100000' })
// → { valid: false, errors: ['Province "Ilocos Norte" does not belong to region "040000000"'] }TypeScript Types
interface Region {
code: string
name: string
}
interface Province {
code: string
name: string
regionCode: string
}
interface Municipality {
code: string
name: string
regionCode: string
provinceCode: string | null
isCity: boolean
zipCodes: string[]
}
interface Barangay {
code: string
name: string
municipalityCode: string
}
interface ZipLookupResult {
zip: string
region: Region
province: Province | null
municipality: Municipality
barangays: Barangay[]
}
interface SearchResult {
type: 'region' | 'province' | 'municipality' | 'barangay'
code: string
name: string
score?: number
regionCode?: string
provinceCode?: string | null
municipalityCode?: string
}
interface FullPath {
region: Region
province: Province | null
municipality: Municipality | null
barangay: Barangay | null
}
interface SearchOptions {
fuzzy?: boolean
limit?: number
types?: Array<'region' | 'province' | 'municipality' | 'barangay'>
parentCode?: string
}
interface ValidationInput {
regionCode?: string
provinceCode?: string
municipalityCode?: string
barangayCode?: string
}
interface ValidationResult {
valid: boolean
errors: string[]
}PSGC Code Structure
RRPPMMMBBB| Part | Meaning | |------|--------------| | RR | Region | | PP | Province | | MMM | Municipality | | BBB | Barangay |
Example:
040000000 → Region IV-A (CALABARZON)
045600000 → Quezon
045645000 → Sariaya
045645001 → Barangay AntipoloNotes
- NCR has no provinces —
provinceisnullin ZIP lookup results. - ZIP dataset contains ~958 mappings; some barangays share ZIP codes.
- BARMM barangay data may be incomplete (PSGC limitation).
License
MIT License © 2026
Support
Star the repo if this helped you: https://github.com/bzync/ph-address-intel
