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

htag-sdk

v0.7.0

Published

Official TypeScript SDK for HtAG Location Intelligence APIs — address search, property data, and market analytics for Australia

Readme

htag-sdk

The official TypeScript SDK for the HtAG Location Intelligence API.

Provides typed access to Australian address data, property sales records, and market analytics. Zero runtime dependencies — uses native fetch.

import { HtAgApiClient } from 'htag-sdk';

const client = new HtAgApiClient({
  apiKey: process.env.HTAG_API_KEY!,
  environment: 'prod',
});

const results = await client.address.search({ q: '100 George St Sydney' });
for (const r of results.results) {
  console.log(`${r.address_label}  (score ${r.score.toFixed(2)})`);
}

Installation

npm install htag-sdk

Or with your preferred package manager:

yarn add htag-sdk
pnpm add htag-sdk

Requires Node.js 18+ (for native fetch).

Quick Start

1. Get an API Key

Sign up at developer.htagai.com and create an API key from the Settings page.

2. Create a Client

import { HtAgApiClient } from 'htag-sdk';

const client = new HtAgApiClient({
  apiKey: 'sk-org--your-org-id-your-key-value',
  environment: 'prod',   // 'dev' or 'prod'
});

Or use a custom base URL:

const client = new HtAgApiClient({
  apiKey: 'sk-...',
  baseUrl: 'https://api.staging.htagai.com',
});

3. Make Requests

// Search for addresses
const results = await client.address.search({ q: '15 Miranda Court Noble Park' });
console.log(`${results.total} matches`);

// Get insights for an address
const insights = await client.address.insights({
  address: '15 Miranda Court, Noble Park VIC 3174',
});
for (const record of insights.results) {
  console.log(`Bushfire: ${record.bushfire}, Flood: ${record.flood}`);
}

Usage

Address Search

Find addresses by free-text query with fuzzy matching.

const results = await client.address.search({
  q: '100 Hickox St Traralgon',
  threshold: 0.3,   // minimum match score (0.1 - 1.0)
  limit: 5,         // max results (1 - 50)
});

for (const match of results.results) {
  console.log(match.address_label);
  console.log(`  Key: ${match.address_key}`);
  console.log(`  Score: ${match.score.toFixed(2)}`);
  console.log(`  Location: ${match.lat}, ${match.lon}`);
}

Address Insights

Retrieve enriched data for addresses including risk flags, SEIFA scores, and zoning.

Provide exactly one of address, addressKeys, or legalParcelId:

// By address string
const insights = await client.address.insights({
  address: '15 Miranda Court, Noble Park VIC 3174',
});

// By GNAF address keys (up to 50)
const insights2 = await client.address.insights({
  addressKeys: ['100102HICKOXSTREETTRARALGONVIC3844'],
});

// By legal parcel ID
const insights3 = await client.address.insights({
  legalParcelId: '2\\TP574754',
});

for (const record of insights.results) {
  console.log(`Address: ${record.address_label}`);
  console.log(`  Bushfire risk: ${record.bushfire}`);
  console.log(`  Flood risk: ${record.flood}`);
  console.log(`  Heritage: ${record.heritage}`);
  console.log(`  SEIFA (IRSAD): ${record.IRSAD}`);
  console.log(`  Zoning: ${record.zoning}`);
}

Address Standardisation

Standardise raw address strings into structured, canonical components.

const result = await client.address.standardise({
  addresses: [
    '12 / 100-102 HICKOX STR TRARALGON, VIC 3844',
    '15a smith st fitzroy vic 3065',
  ],
});

for (const item of result.results) {
  if (item.error) {
    console.log(`Failed: ${item.input_address} — ${item.error}`);
  } else {
    const addr = item.standardised_address!;
    console.log(item.input_address);
    console.log(`  -> ${addr.street_number} ${addr.street_name} ${addr.street_type}`);
    console.log(`     ${addr.suburb_or_locality} ${addr.state} ${addr.postcode}`);
    console.log(`  Key: ${item.address_key}`);
  }
}

Sold Property Search

Search for recently sold properties near an address or coordinates.

const sold = await client.property.soldSearch({
  address: '100 George St, Sydney NSW 2000',
  radius: 2000,              // metres
  propertyType: 'house',
  saleValueMin: 500_000,
  saleValueMax: 2_000_000,
  bedroomsMin: 3,
  startDate: '2024-01-01',
});

console.log(`${sold.total} properties found`);
for (const prop of sold.results) {
  const price = prop.sold_price ? `$${prop.sold_price.toLocaleString()}` : 'undisclosed';
  console.log(`  ${prop.street_address}, ${prop.suburb} — ${price} (${prop.sold_date})`);
}

All filter parameters are optional:

| Parameter | Type | Description | |-----------|------|-------------| | address | string | Free-text address to centre the search on | | addressKey | string | GNAF address key | | lat, lon | number | Coordinates for point-based search | | radius | number | Search radius in metres (default 2000, max 5000) | | proximity | string | 'any', 'sameStreet', or 'sameSuburb' | | propertyType | string | 'house', 'unit', 'townhouse', 'land', 'rural' | | saleValueMin, saleValueMax | number | Price range filter (AUD) | | bedroomsMin, bedroomsMax | number | Bedroom count range | | bathroomsMin, bathroomsMax | number | Bathroom count range | | carSpacesMin, carSpacesMax | number | Car space range | | startDate, endDate | string | Date range (ISO 8601, e.g. '2024-01-01') | | landAreaMin, landAreaMax | number | Land area in sqm |

Parameters use camelCase in TypeScript and are automatically converted to snake_case for the API.

Market Snapshots

Get current market metrics at suburb or LGA level.

const snapshots = await client.markets.snapshots({
  level: 'suburb',
  propertyType: ['house'],
  areaId: ['SAL10001'],
  limit: 10,
});

for (const snap of snapshots.results) {
  console.log(`${snap.suburb} (${snap.state_name})`);
  console.log(`  Typical price: $${snap.typical_price?.toLocaleString()}`);
  console.log(`  Rent: $${snap.rent}/wk`);
  if (snap.yield != null) console.log(`  Yield: ${(snap.yield * 100).toFixed(1)}%`);
  if (snap.one_y_price_growth != null) {
    console.log(`  1Y growth: ${(snap.one_y_price_growth * 100).toFixed(1)}%`);
  }
}

Market Query (Advanced)

Run complex market searches with filter logic using AND/OR/NOT operators.

const results = await client.markets.query({
  level: 'suburb',
  mode: 'search',
  property_types: ['house'],
  typical_price_min: 500_000,
  typical_price_max: 1_500_000,
  logic: {
    and: [
      { field: 'one_y_price_growth', gte: 0.05 },
      { field: 'vacancy_rate', lte: 0.03 },
    ],
  },
  limit: 20,
});

for (const snap of results.results) {
  console.log(`${snap.suburb}: $${snap.typical_price?.toLocaleString()}`);
}

Market Trends

Access historical trend data via client.markets.trends. All trend methods share the same parameter signature:

// Price history
const prices = await client.markets.trends.price({
  level: 'suburb',
  areaId: ['SAL10001'],
  propertyType: ['house'],
  periodEndMin: '2020-01-01',
  limit: 50,
});
for (const p of prices.results) {
  console.log(`${p.period_end}: $${p.typical_price?.toLocaleString()} (${p.sales} sales)`);
}

// Rent history
const rents = await client.markets.trends.rent({
  level: 'suburb',
  areaId: ['SAL10001'],
});

// Yield history
const yields = await client.markets.trends.yieldHistory({
  level: 'suburb',
  areaId: ['SAL10001'],
});

// Supply & demand (inventory, vacancies, clearance rate)
const supply = await client.markets.trends.supplyDemand({
  level: 'suburb',
  areaId: ['SAL10001'],
});

// Search interest index (buy/rent search indices)
const search = await client.markets.trends.searchIndex({
  level: 'suburb',
  areaId: ['SAL10001'],
});

// Hold period
const hold = await client.markets.trends.holdPeriod({
  level: 'suburb',
  areaId: ['SAL10001'],
});

// Performance essentials (price, rent, sales, rentals, yield)
const perf = await client.markets.trends.performance({
  level: 'suburb',
  areaId: ['SAL10001'],
});

// Growth rates (price, rent, yield changes)
const growth = await client.markets.trends.growthRates({
  level: 'suburb',
  areaId: ['SAL10001'],
});

// Demand profile (sales by dwelling type and bedrooms)
const demand = await client.markets.trends.demandProfile({
  level: 'suburb',
  areaId: ['SAL10001'],
});

Common trend parameters:

| Parameter | Type | Description | |-----------|------|-------------| | level | 'suburb' | 'lga' | Geographic level (required) | | areaId | string[] | Area identifiers (required) | | propertyType | string[] | ['house'], ['unit'], etc. | | periodEndMin | string | Filter from this date | | periodEndMax | string | Filter up to this date | | bedrooms | string | string[] | Bedroom filter | | limit | number | Max results (default 100, max 1000) | | offset | number | Pagination offset |

Request Cancellation

All methods accept an AbortSignal for cancellation:

const controller = new AbortController();

// Cancel after 5 seconds
setTimeout(() => controller.abort(), 5000);

try {
  const results = await client.address.search({
    q: '100 George St',
    signal: controller.signal,
  });
} catch (err) {
  if (err instanceof HtAgError && err.message === 'Request aborted') {
    console.log('Request was cancelled');
  }
}

Error Handling

The SDK raises typed errors for API failures:

import {
  HtAgApiClient,
  HtAgError,
  AuthenticationError,
  RateLimitError,
  ValidationError,
  ServerError,
} from 'htag-sdk';

const client = new HtAgApiClient({ apiKey: 'sk-...' });

try {
  const results = await client.address.search({ q: 'Syd' });
} catch (err) {
  if (err instanceof AuthenticationError) {
    // 401 or 403 — bad API key
    console.error(`Auth failed (HTTP ${err.status})`);
  } else if (err instanceof RateLimitError) {
    // 429 — throttled (after exhausting retries)
    console.error('Rate limited, try again later');
  } else if (err instanceof ValidationError) {
    // 400 or 422 — bad request params
    console.error(`Invalid request: ${err.message}`);
    console.error('Details:', err.body);
  } else if (err instanceof ServerError) {
    // 5xx — upstream failure (after exhausting retries)
    console.error(`Server error (HTTP ${err.status})`);
  } else if (err instanceof HtAgError) {
    // Network/timeout/other
    console.error(`Request failed: ${err.message}`);
  }
}

All errors carry:

  • message — human-readable description
  • status — HTTP status code (if applicable)
  • body — raw response body
  • url — the request URL that failed
  • cause — the underlying error (for network failures)

Retries

The SDK automatically retries transient failures:

  • Retried statuses: 429, 500, 502, 503, 504
  • Network errors: connection failures, timeouts
  • Max retries: 3 (configurable)
  • Backoff: exponential (0.5s base, 2x multiplier, random jitter)

Configure retry behaviour:

const client = new HtAgApiClient({
  apiKey: 'sk-...',
  maxRetries: 5,       // default is 3
  timeout: 120_000,    // request timeout in ms (default 30000)
});

Configuration Reference

| Option | Type | Default | Description | |--------|------|---------|-------------| | apiKey | string | required | Your HtAG API key | | environment | 'dev' | 'prod' | 'prod' | API environment | | baseUrl | string | — | Custom base URL (overrides environment) | | timeout | number | 30000 | Request timeout in milliseconds | | maxRetries | number | 3 | Maximum retry attempts | | retryBaseDelay | number | 500 | Base delay between retries in ms |

CommonJS

The package ships with both ESM and CommonJS builds:

// ESM (recommended)
import { HtAgApiClient } from 'htag-sdk';

// CommonJS
const { HtAgApiClient } = require('htag-sdk');

TypeScript

All types are exported for use in your application:

import type {
  AddressRecord,
  AddressSearchResult,
  SoldPropertyRecord,
  MarketSnapshot,
  PriceHistoryOut,
  RentHistoryOut,
  BaseResponse,
  LevelEnum,
  PropertyTypeEnum,
} from 'htag-sdk';

Requirements

  • Node.js >= 18 (for native fetch)
  • No runtime dependencies

License

MIT