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

howlongtobeat-ts

v1.2.0

Published

TypeScript library to extrapolate data from HowLongToBeat.com search results.

Readme

howlongtobeat-ts

Ask DeepWiki GitHub npm npm CI

A TypeScript library for interacting with the HowLongToBeat website API. Easily search for games and retrieve estimates on how long it takes to complete them.

This project is heavily inspired by howlongtobeat-js by toasttsunami, but has been rewritten in TypeScript for improved type safety and maintainability.

As also noted by toasttsunami in his implementation, this library was created due to the inactivity of ckatzorke's howlongtobeat project, which appears to be abandoned. Additionally, recent changes to the HowLongToBeat.com API have rendered it non-functional, making alternative solutions necessary.

⚠️ Disclaimer: This library is not an official API and is not affiliated nor endorsed with HowLongToBeat.com or Ziff Davis LLC in any way. Please use this library responsibly and do not abuse or overload the HowLongToBeat servers. Use at your own risk.

Features

  • Search for games on HowLongToBeat
  • Retrieve completion time data for games
  • Fetch a single best match (searchOne) or look a game up directly by id (getById)
  • Resilient networking: configurable timeouts, retries with backoff, 429 handling, an injectable fetch and AbortSignal support
  • Fully typed, with a discriminated-union result type and zero console noise

Installation

npm install howlongtobeat-ts

Requires Node.js 18 or newer (the library uses the global fetch). Ships both ESM and CommonJS builds.

Usage

import { HowLongToBeatService, SearchModifier } from 'howlongtobeat-ts'

const hltb = new HowLongToBeatService()

const results = await hltb.search('The Last of Us')
if (results.success) {
  // `data` is only available on the success branch.
  console.log(results.data)
} else {
  // `error` is only available on the failure branch.
  console.error(results.error)
}

// Filter DLCs in/out via the options object.
await hltb.search('Zelda', { modifier: SearchModifier.HIDE_DLC })

// Just the single best match (or null).
const best = await hltb.searchOne('Hades')
if (best.success && best.data) {
  console.log(best.data.name)
}

// Look a game up directly by its HowLongToBeat id.
const elden = await hltb.getById(68151)

Working with completion times

All *Time fields are expressed in seconds. Use the toHours helper to convert:

import { toHours } from 'howlongtobeat-ts'

const result = await hltb.searchOne('Elden Ring')
if (result.success && result.data) {
  console.log(`Main story: ${toHours(result.data.mainTime)} hours`)
}

Configuration

Pass an options object to the constructor (a bare number is still accepted as minSimilarity for backwards compatibility):

import { HowLongToBeatService, consoleLogger } from 'howlongtobeat-ts'

const hltb = new HowLongToBeatService({
  minSimilarity: 0.5, // min similarity threshold (0–1), clamped
  timeout: 30_000, // per-request timeout in ms
  retries: 2, // retry attempts on transient failures / 429 / 5xx
  logger: consoleLogger, // opt in to diagnostic logging (default: silent)
  // fetch: myCustomFetch,  // inject a custom fetch (proxy, undici agent, …)
})

// Cancel an in-flight request.
const controller = new AbortController()
const promise = hltb.search('Halo', { signal: controller.signal })
controller.abort()

API

HowLongToBeatService

  • constructor(options?: number | ScraperOptions)ScraperOptions extends the HTTP options (timeout, retries, retryDelay, fetch, userAgents, logger) with minSimilarity.
  • search(searchKey, options?): Promise<SearchResult>options is { modifier?, signal? }.
  • searchOne(searchKey, options?): Promise<EntryResult> — the best match or null.
  • getById(id, options?): Promise<EntryResult> — fetch by id (experimental; relies on the public game page).

SearchResult / EntryResult

Discriminated unions:

type SearchResult = { success: true; data: HowLongToBeatEntry[] } | { success: false; error: string }
type EntryResult = { success: true; data: HowLongToBeatEntry | null } | { success: false; error: string }

SearchModifier

NONE (all results), HIDE_DLC, ONLY_DLC.

HowLongToBeatEntry

id, name, alias, type, the *Time fields (in seconds) and matching *Count fields, imageUrl, reviewScore, platforms, similarity, releaseYear, and raw — the typed HowLongToBeatResultEntry exactly as returned by HowLongToBeat.

Development

git clone https://github.com/Deadlock-too/howlongtobeat-ts.git
cd howlongtobeat-ts
npm install

npm run build            # build with tsup
npm test                 # unit tests
npm run test:integration # live API tests (hit HowLongToBeat)
npm run test:coverage    # unit tests with coverage
npm run lint             # eslint
npm run format           # prettier

Releases are managed with Changesets: run npm run changeset to record a change; the release workflow publishes to npm once the generated version PR is merged.

Issues, Questions & Discussions

If you found a bug, report it as soon as possible creating an issue, the code is not perfect for sure, and I will be happy to fix it. If you need any new feature, or want to discuss the current implementation/features, consider opening a discussion or even propose a change with a Pull Request.

License

This project is licensed under the MIT License - see the LICENSE file for details.