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

ashby-ts

v0.1.1

Published

Typed TypeScript client for the Ashby API, generated from Ashby's OpenAPI spec. Every endpoint, fully typed, with async-iterator pagination and incremental sync.

Readme

ashby-ts

Typed TypeScript client for the Ashby API, generated from Ashby's published OpenAPI spec.

All 173 endpoints across 53 resource namespaces, fully typed. Zero runtime dependencies. Works in Node 18+, Bun, and edge runtimes (it only needs fetch and btoa).

npm install ashby-ts
# or
bun add ashby-ts

Quick start

import { Ashby } from "ashby-ts";

const ashby = new Ashby({ apiKey: process.env.ASHBY_API_KEY! });

// every list endpoint is an async iterator; pagination is handled for you
for await (const job of ashby.job.list({ status: ["Open"] })) {
  console.log(job.title);
}

const { results: candidate } = await ashby.candidate.info({ id: "..." });

The client surface mirrors Ashby's own API exactly: their endpoints are RPC-style noun.verb calls (application.list, candidate.create), so the client is ashby.application.list(), ashby.candidate.create(), and so on for every endpoint in the spec. If you know their API reference, you know this client. Responses come back as Ashby's own envelopes, typed.

Pagination

Every cursor-paginated endpoint returns a lazy call object. Awaiting it fetches one page. Iterating it fetches everything, following nextCursor until the data runs out.

// one page
const page = await ashby.application.list({ limit: 50 });
page.results; // Application[]
page.nextCursor; // string | undefined

// or the explicit spelling of the same thing
const samePage = await ashby.application.list({ limit: 50 }).page();

// every item, across every page
for await (const app of ashby.application.list({ jobId })) {
  // breaking early stops fetching; no wasted requests
}

// collect everything into one array
const all = await ashby.application.list({ jobId }).all();

Incremental sync

Ashby's list endpoints support syncToken incremental sync, and their docs push it hard for good reason: it's the difference between refetching your whole ATS and fetching what changed. The sync() helper drains all pages and hands you the next token.

// first sync: everything
let { results, syncToken } = await ashby.candidate.sync();

// later syncs: only what changed
({ results, syncToken } = await ashby.candidate.sync({ syncToken }));

sync() exists on the 26 namespaces whose list endpoint supports it. The types won't let you call it anywhere else.

Errors

Ashby reports most failures as HTTP 200 with { success: false, errors: [...] }. The client unwraps that: a successful call resolves with the success envelope, anything else throws AshbyError carrying Ashby's actual error details.

import { AshbyError } from "ashby-ts";

try {
  await ashby.application.changeStage({ applicationId, interviewStageId });
} catch (err) {
  if (err instanceof AshbyError) {
    err.status; // HTTP status (200 for logical errors)
    err.endpoint; // "application.changeStage"
    err.errors; // [{ message, parameter? }]
  }
}

429s and 5xxs are retried twice with backoff, honoring Retry-After. Tune with maxRetries.

Escape hatch

If you'd rather call endpoints by path, request() is the raw typed transport:

const res = await ashby.request("/apiKey.info");

How it's built

The entire client is generated from Ashby's spec; there is no per-endpoint code to drift out of date. Specs run through this package end to end: one comes in, one goes out.

Ashby's OpenAPI spec          spec/ashby-api.json (vendored, 173 endpoints)
  -> openapi-typescript       src/generated/api.ts (types only, zero runtime)
  -> mapped types + Proxy     the client (~200 lines of runtime, no per-endpoint code)
  -> @openpkg-ts/sdk          openpkg.json + docs/api.md (this package's own API, extracted from its types)
  • openapi-typescript turns the vendored spec into types
  • the client surface is a mapped type over those generated types: namespaces, methods, request bodies, response envelopes, which endpoints paginate, which support sync
  • the runtime is one small Proxy-based fetch wrapper, because Ashby's API is uniform enough that nothing else is needed
  • openpkg.json is this package's own API spec, extracted from its types with @openpkg-ts/sdk; docs/api.md is rendered from it

Regenerate against the latest Ashby spec with bun run generate --fetch, and the package's own spec + docs with bun run docs.

Notes

  • Auth is Ashby's standard scheme: your API key as the Basic auth username, blank password. Keys and permissions are managed in Ashby's admin console.
  • This client talks to your real ATS. Endpoints require the corresponding key permissions (candidatesRead, candidatesWrite, ...), and write calls do what they say.
  • Not affiliated with Ashby. Ashby's API is versioned by them; this package tracks the published spec and vendored spec updates are explicit, reviewable diffs.

MIT