Zero-dependency HSN/SAC code lookup and GST toolkit for India — search 12,000+ codes, look up GST rates, validate GSTIN/PAN, compute CGST/SGST/IGST, and export invoice data.
🚀 Live Demo — See every feature in action, no setup needed.
Try it live at hsn-gst-demo.karthi-21.com before installing.
hsn-code-package bundles the full CBIC/WCO Harmonized System Nomenclature (HSN) code list along with Service Accounting Codes (SAC) and a set of pure utility functions for GST work in India. It has no runtime dependencies — the data ships as JSON inside the package and everything runs on the Node.js standard library.
It provides:
- HSN code lookup — search, validate, and browse 12,604 HSN codes by code or description.
- GST rate data — chapter-level CGST/SGST/IGST rates derived from CBIC Notification 09/2025-CT(Rate).
- GST calculation — rate-agnostic helpers for tax, reverse tax, invoice totals, and round-off.
- GSTIN & PAN validation — format checks plus MOD-36 checksum verification and state decoding.
- SAC codes — 496 service accounting codes with search.
- Export utilities — CSV/JSON export and a GSTR-1 rate-wise summary.
- CLI —
hsncommand for lookups, validation, and export from the terminal. - TypeScript — complete type definitions bundled (no
@typesneeded).
npm install hsn-code-packageRequires Node.js >= 14.
const hsn = require('hsn-code-package');
// 1. Look up an HSN code
hsn.getHsnByExactCode('52010011');
// → { code: '52010011', description: 'COTTON, NOT CARDED OR COMBED ... BENGAL DESHI' }
// 2. Find its GST rate
hsn.getGstRateByCode('52010011');
// → { code: '52010011', igstRate: 5, cgstRate: 2.5, sgstRate: 2.5, cessRate: 0, ... }
// 3. Calculate GST on an amount
hsn.calculateGSTBreakdown(10000, 5, { isInterState: false });
// → { taxableAmount: 10000, cgst: 250, sgst: 250, igst: 0, cess: 0, totalTax: 500, grandTotal: 10500 }
// 4. Validate a GSTIN
hsn.validateGSTIN('27AAPFU0939F1ZV');
// → { isValid: true, stateCode: '27', stateName: 'Maharashtra', panNumber: 'AAPFU0939F', ... }- HSN code lookup
- GST rate data
- GST calculation utilities
- GSTIN & PAN validation
- SAC codes (services)
- Export utilities
- CLI
- TypeScript
- Data sources & update schedule
- Migrating from v1 / v2.x
- Live demo
- Contributing
Returns every HSN code in the dataset.
hsn.getAllHsn();
// → [ { code: '1011010', description: '...' }, ... ] (12,604 entries)Bundled HSN, SAC, GST-rate, and metadata records are immutable. The canonical
arrays returned by getAllHsn() and getAllSac() are also immutable and keep
the same reference; clone an array or record before modifying it. Search result
arrays may be reordered, but their records remain immutable.
Case-insensitive partial search on the description.
hsn.getCodeByTxt('cotton');
// → [ { code: '12072010', description: '...COTTON SEEDS : OF SEED QUALITY' }, ... ]Returns all entries whose code string contains the given value (partial match). Use getHsnByExactCode for a strict lookup.
hsn.getDesByCode('5201');
// → [ { code: '52010011', description: '...' }, { code: '52010012', ... }, ... ]Returns the single entry for an exact code, or undefined.
hsn.getHsnByExactCode('52010011');
// → { code: '52010011', description: 'COTTON, NOT CARDED OR COMBED ...' }Returns true if the exact code exists.
hsn.isValidHsnCode('52010011'); // → true
hsn.isValidHsnCode('00000000'); // → falseReturns all codes under a chapter (first two digits). Accepts '52', '5', or 52.
hsn.getHsnChapter('52'); // → all chapter-52 (cotton) codesAdvanced description search with match type and pagination.
hsn.searchHsn('silk', { matchType: 'contains', limit: 10, offset: 0 });| Option | Type | Default | Description |
|---|---|---|---|
matchType |
'contains' | 'startsWith' | 'exact' |
'contains' |
How the description is matched. |
limit |
number |
0 |
Max results (0 = unlimited). |
offset |
number |
0 |
Results to skip (for pagination). |
Returns metadata about the bundled dataset.
hsn.getStats();
// → { version: '2.3.0', lastUpdated: '2026-06-02',
// gstRatesLastUpdated: '2026-06-02', gstRateSource: 'chapter-level',
// gstNotificationRef: 'Notification No. 09/2025-CT(Rate) dated 17 Sep 2025',
// totalCodes: 12604, chapterCount: 86,
// source: 'CBIC / WCO Harmonized System Nomenclature' }Returns a summary of a chapter, or null if none exist.
hsn.getChapterSummary('52');
// → { chapter: '52', totalCodes: 123, codeRange: { from: '...', to: '...' }, codes: [...] }Returns codes whose description contains all of the given keywords (AND match).
hsn.findCodesByDescription(['cotton', 'carded']);
// → entries matching both termsValidates many codes at once.
hsn.bulkValidateHsnCodes(['52010011', '00000000']);
// → { valid: ['52010011'], invalid: ['00000000'],
// summary: { total: 2, validCount: 1, invalidCount: 1 } }Note: GST rates are chapter-level mappings derived from CBIC Notification No. 09/2025-CT(Rate) dated 17 Sep 2025, effective 2025-09-22. They are a best-effort classification at the chapter level — always confirm against the official notification for legal/billing use.
The bundled fallback was generated on 2026-06-02 and is labeled
chapter-level. gstRatesLastUpdated records the date the bundled rate content
materially changed; it is not a record of background source checks.
Returns GST rate details for an exact code, or null.
hsn.getGstRateByCode('52010011');
// → { code: '52010011', igstRate: 5, cgstRate: 2.5, sgstRate: 2.5, cessRate: 0,
// rateSource: 'chapter-level', effectiveFrom: '2025-09-22',
// notificationRef: 'Notification No. 09/2025-CT(Rate)' }Returns the HSN entry merged with its GST rate, or undefined if the code does not exist.
hsn.getHsnByExactCodeWithRate('52010011');
// → { code: '52010011', description: '...', igstRate: 5, cgstRate: 2.5, ... }Returns all HSN codes under a given IGST rate slab.
hsn.getHsnByRateSlabs(5); // → all codes taxed at 5% IGSTThese are pure, rate-agnostic helpers — you pass the rate in; they do not look it up. All monetary results are rounded to 2 decimals (half-up).
hsn.calculateTax(10000, 18);
// → { taxableAmount: 10000, rate: 18, taxAmount: 1800, total: 11800 }Splits GST into CGST/SGST (intra-state) or IGST (inter-state), plus optional cess.
hsn.calculateGSTBreakdown(10000, 18, { isInterState: false });
// → { taxableAmount: 10000, cgst: 900, sgst: 900, igst: 0, cess: 0,
// totalTax: 1800, grandTotal: 11800 }| Option | Type | Default | Description |
|---|---|---|---|
isInterState |
boolean |
false |
true → IGST, false → CGST+SGST. |
cessRate |
number |
0 |
Additional cess percentage. |
Extracts the base amount and tax from a tax-inclusive total.
hsn.reverseCalculateTax(11800, 18);
// → { grandTotal: 11800, rate: 18, taxableAmount: 10000, taxAmount: 1800 }Returns 'CGST_SGST' when the two state codes match, otherwise 'IGST'.
hsn.getApplicableTaxType('27', '27'); // → 'CGST_SGST'
hsn.getApplicableTaxType('27', '33'); // → 'IGST'Aggregates a list of line items into invoice totals with round-off.
hsn.calculateInvoiceTotals(
[{ taxableValue: 10000, gstRate: 18 }, { taxableValue: 5000, gstRate: 5 }],
false
);
// → { totalTaxableValue, totalCGST, totalSGST, totalIGST, totalCess,
// totalTax, grandTotal, roundOff }Each item: { taxableValue: number, gstRate: number, cessRate?: number }.
Groups line items by their GST rate (useful for GSTR-1 summaries).
hsn.groupItemsByTaxRate([{ taxableValue: 1000, gstRate: 18 }, { taxableValue: 500, gstRate: 5 }]);
// → { '18': [ {...} ], '5': [ {...} ] }Rounds a monetary amount to 2 decimals using half-up rounding.
hsn.applyRoundOffRules(10.005); // → 10.01Validates format, state code, and MOD-36 checksum. Returns a result object.
hsn.validateGSTIN('27AAPFU0939F1ZV');
// → { isValid: true, stateCode: '27', stateName: 'Maharashtra',
// panNumber: 'AAPFU0939F', entityNumber: '1', checkDigit: 'V' }
hsn.validateGSTIN('27AAPFU0939F1ZX');
// → { isValid: false, error: 'Invalid checksum: expected V, got X' }Trims and uppercases a GSTIN string.
hsn.formatGSTIN('27aapfu0939f1zv '); // → '27AAPFU0939F1ZV'Returns the state name for a GSTIN's first two digits. Throws on unknown codes.
hsn.getStateFromGSTIN('27AAPFU0939F1ZV'); // → 'Maharashtra'Checks PAN format (AAAAA9999A).
hsn.isValidPAN('AAPFU0939F'); // → trueDecomposes a GSTIN into its parts (does not run the checksum).
hsn.getGSTINComponents('27AAPFU0939F1ZV');
// → { stateCode: '27', stateName: 'Maharashtra', pan: 'AAPFU0939F',
// entityNumber: '1', checkDigit: 'V' }Returns all 496 SAC entries.
hsn.getAllSac(); // → [ { code: '9954', description: 'Construction services' }, ... ]Returns the single SAC entry for an exact code, or undefined.
hsn.getSacByCode('9954'); // → { code: '9954', description: 'Construction services' }Description search with the same SearchOptions as searchHsn.
hsn.searchSac('construction', { matchType: 'contains', limit: 5 });Looks a code up in both HSN and SAC datasets and tags the result.
hsn.getCodeDetails('9954'); // → { code: '9954', description: '...', type: 'SAC' }
hsn.getCodeDetails('52010011'); // → { code: '52010011', description: '...', type: 'HSN' }Converts an array of objects to CSV (with quoting/escaping).
hsn.exportToCSV(hsn.getCodeByTxt('cotton'));
// → "code,description\n12072010,..."| Option | Type | Default | Description |
|---|---|---|---|
delimiter |
string |
',' |
Field delimiter. |
headers |
string[] |
keys of the first row | Explicit column order/selection. |
preventFormulaInjection |
boolean |
true |
Prefix spreadsheet-like string cells with '. |
Formula protection applies to string values and headers beginning with = or
@, non-numeric strings beginning with + or - after optional whitespace,
and strings beginning with a tab or carriage return. Signed decimal and
scientific-notation strings such as -1234.50 and +1e3 remain unchanged.
Disable protection only when legacy formula handling is required and the output
will not be opened by spreadsheet software.
Serialises to JSON (pretty by default).
hsn.exportToJSON(hsn.getCodeByTxt('cotton'), { pretty: false });Produces a rate-wise summary suitable for GSTR-1 reporting.
hsn.generateGSTR1Summary([{ taxableValue: 10000, gstRate: 18, isInterState: false }]);
// → [ { taxRate: 18, taxableValue: 10000, igst: 0, cgst: 900, sgst: 900,
// cess: 0, totalTax: 1800, count: 1 } ]Each item requires a non-negative finite taxableValue and either gstRate or
the compatible igstRate field. cessRate defaults to 0, and isInterState
defaults to false. When both rate fields are supplied, gstRate takes
precedence. Malformed items, non-finite numbers, negative values, and a
non-boolean isInterState throw an indexed field error instead of being
included in the summary.
Installed as the hsn binary (use npx hsn if installed locally).
hsn search <query> [--limit N] [--type contains|startsWith|exact]
hsn validate <code>
hsn chapter <chapter>
hsn stats
hsn gstin <gstin>
hsn sac <query> [--limit N]
hsn export <query> [--format csv|json]
hsn helpExamples:
hsn search cotton --limit 3
hsn validate 52010011
hsn gstin 27AAPFU0939F1ZV
hsn export silk --format jsonThe validate and gstin commands exit with status 0 for a valid identifier
and status 1 for an invalid identifier or usage error.
Type definitions are bundled — there is no separate @types package.
import { getHsnByExactCode, calculateGSTBreakdown, HsnCode, GSTBreakdown } from 'hsn-code-package';
const entry: HsnCode | undefined = getHsnByExactCode('52010011');
const tax: GSTBreakdown = calculateGSTBreakdown(10000, 18, { isInterState: false });Exported interfaces include HsnCode, SearchOptions, HsnStats, GstRate, HsnCodeWithRate, GSTINValidationResult, GSTINComponents, SacCode, CodeDetails, TaxResult, GSTBreakdown, ReverseTaxResult, InvoiceLineItem, InvoiceTotals, GSTBreakdownOptions, ChapterSummary, and BulkValidationResult.
- HSN/SAC codes: CBIC / WCO Harmonized System Nomenclature.
- GST rates: CBIC Notification No. 09/2025-CT(Rate) dated 17 Sep 2025 (effective 2025-09-22), mapped at the chapter level.
- The read-only update workflow checks weekly only when
GST_RATE_SOURCE_URLis configured with an authoritative machine-readable workbook. Failed, unchanged, and changed checks do not modify repository data. After reviewing a reported difference, a maintainer can runnode scripts/update-gst-rates.js --writelocally and review the resulting diff before committing it.gstRatesLastUpdatedadvances only for that validated material update. A workbook must match at least 80% of its rows to bundled HSN codes. Metadata reportsauthoritative-excelonly when every generated rate came from the workbook, andmixedwhen chapter-level fallbacks remain. - Workbook parsing is intentionally fail-closed. Every populated worksheet row
must contain a recognized HSN-code field and GST-rate field; footer, note, or
commentary rows abort the refresh and must be removed from the reviewed input
before using
--write.
Rates are provided as a developer convenience and classified at chapter granularity. For legal, billing, or filing purposes, verify against the official CBIC notification.
Version 3.0 makes previously permissive behavior explicit and safe:
getAllHsn()andgetAllSac()return stable, frozen arrays containing frozen records. Clone before modification:const editable = hsn.getAllHsn().map(row => ({ ...row })).generateGSTR1Summary()now requires finite, non-negative numeric values and throws indexed errors for malformed entries. Normalize imported strings and supplytaxableValueplusgstRateorigstRatebefore calling it.exportToCSV()protects spreadsheet-formula strings by default. Legitimate signed numeric strings remain unchanged. Pass{ preventFormulaInjection: false }only when preserving potentially executable spreadsheet input is intentional.- Invalid
hsn validateandhsn gstincommands now exit with status1. - TypeScript models canonical HSN/SAC/rate records and arrays as readonly.
HsnStatsnow includes required GST-rate provenance fields.
See CHANGELOG.md for the complete release summary.
| From | Change |
|---|---|
| v1.x | Package now exports a named-function API (e.g. getHsnByExactCode) instead of a default lookup object. HSN entries use { code, description }. |
| v2.0–v2.1 | Added GST calculation utilities (calculateTax, calculateGSTBreakdown, …). No breaking changes to HSN lookup functions. |
| v2.2 | Added GSTIN/PAN validation, SAC codes, export utilities, and the hsn CLI. No breaking changes. |
| v2.3 | Added GST rate data (getGstRateByCode, getHsnByExactCodeWithRate, getHsnByRateSlabs) and advanced HSN lookups. No breaking changes. |
| v3.0 | Hardened validation, exports, CLI statuses, immutable data, rate ingestion, and TypeScript contracts. See the migration notes above. |
No function signatures or return shapes changed within the published 2.x line; the safety changes described above begin in 3.0.
https://hsn-gst-demo.karthi-21.com/
An interactive showcase built on top of this package — try HSN lookups, GSTIN validation, GST calculations, SAC search, invoice generation, and CSV export right in the browser. No installation required.
Built and maintained by Karthikeyan.
Issues and pull requests are welcome at the GitHub repository.
npm install # install dev dependencies
npm test # run the test suite with coverage
npm run typecheck # type-check the bundled .d.ts against the smoke testPlease keep the package free of runtime dependencies and ensure npm test and npm run typecheck pass before opening a PR.
ISC