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

adzuna-sdk

v0.1.2

Published

TypeScript SDK for the Adzuna API

Readme

adzuna-sdk

A fully type-safe TypeScript SDK for the Adzuna API, built on Ky and designed for Bun (also runs on Node 18+).

Features

  • Type-safe everywhere. Request options, responses, and error bodies are all typed against the Adzuna OpenAPI spec.
  • Ergonomic surface. adzuna.jobs.search({ country: 'gb', page: 1, fullTime: true }) instead of "1" flag strings and snake_case field names.
  • Auth handled once. Pass appId and appKey to the constructor; every request gets them injected automatically.
  • Retries built in. Retries on 408, 429, 500, 502, 503, 504 via Ky, with configurable limits.
  • Const-object enums. Country.GB for autocomplete, 'gb' for terseness, both type-check.
  • ESM-only, tree-shakeable. No runtime overhead beyond Ky.

Install

bun add adzuna-sdk
# or
npm install adzuna-sdk
# or
pnpm add adzuna-sdk

Getting API credentials

Sign up at developer.adzuna.com to get your app_id and app_key. The free tier allows 25 calls per minute and 250 per day.

Quick start

import { AdzunaClient, Country, SortBy } from 'adzuna-sdk';

const adzuna = new AdzunaClient({
  appId: process.env.ADZUNA_APP_ID!,
  appKey: process.env.ADZUNA_APP_KEY!,
});

const { count, results } = await adzuna.jobs.search({
  country: Country.GB,
  page: 1,
  what: 'typescript',
  where: 'London',
  fullTime: true,
  sortBy: SortBy.Date,
  salaryMin: 60_000,
});

console.log(`${count} matching jobs`);
for (const job of results) {
  console.log(`${job.title} at ${job.company?.display_name} (${job.salary_min}-${job.salary_max})`);
}

API reference

The client exposes adzuna.jobs.* for all job-related endpoints and a top-level adzuna.version().

new AdzunaClient(options)

| Option | Type | Default | Description | | ---------- | ------------- | ------------------------------------- | ------------------------------------------- | | appId | string | required | Your Adzuna application ID. | | appKey | string | required | Your Adzuna application key. | | baseUrl | string | https://api.adzuna.com/v1/api | Override the API base URL. | | retry | number | 2 | Retry attempts for transient failures. | | timeout | number | 30_000 | Request timeout in milliseconds. | | fetch | typeof fetch| globalThis.fetch | Custom fetch implementation (tests, proxies, etc.). |

adzuna.jobs.search(options)

Search the Adzuna job database.

const res = await adzuna.jobs.search({
  country: 'gb',
  page: 1,
  resultsPerPage: 20,
  what: 'react',
  whatAnd: 'typescript',
  whatOr: 'remote hybrid',
  whatExclude: 'senior manager',
  titleOnly: 'engineer',
  where: 'Manchester',
  distance: 25,
  maxDaysOld: 7,
  category: 'it-jobs',
  sortBy: 'date',
  sortDir: 'down',
  salaryMin: 50_000,
  salaryMax: 120_000,
  salaryIncludeUnknown: true,
  fullTime: true,
  permanent: true,
  company: 'acme-corp',
  // location0..location7 also accepted
});

Returns JobSearchResults.

adzuna.jobs.categories(options)

List available job categories for a country.

const { results } = await adzuna.jobs.categories({ country: 'gb' });
// results: Array<{ tag: string; label: string }>

adzuna.jobs.histogram(options)

Distribution of live jobs by salary.

const { histogram } = await adzuna.jobs.histogram({
  country: 'gb',
  what: 'engineer',
  category: 'it-jobs',
});
// histogram: Record<salary, jobCount>
// e.g. { '30000': 120, '40000': 80, '50000': 45 }

adzuna.jobs.topCompanies(options)

Leaderboard of employers matching a query.

const { leaderboard } = await adzuna.jobs.topCompanies({
  country: 'us',
  what: 'python',
  location0: 'US',
  location1: 'California',
});
// leaderboard: Array<{ display_name, canonical_name, count, average_salary }>

adzuna.jobs.geodata(options)

Live job counts broken down by sub-location.

const { locations } = await adzuna.jobs.geodata({
  country: 'gb',
  category: 'it-jobs',
  location0: 'UK',
});
// locations: Array<{ count, location: { display_name, area } }>

adzuna.jobs.history(options)

Historical average salary by month. Requires a location with 20+ jobs.

const { month } = await adzuna.jobs.history({
  country: 'gb',
  category: 'it-jobs',
  months: 12,
});
// month: Record<'YYYY-MM', averageSalary>
// e.g. { '2025-08': 52000, '2025-09': 53100 }

adzuna.version()

Returns the current API version.

const { api_version, software_version } = await adzuna.version();

Constants

Each enum is exposed as a const object (for discoverability) and a type (for parameter checking). You can pass either the named constant or a raw string literal.

import { Country, SortBy, SortDir, ContractTime, ContractType } from 'adzuna-sdk';

Country.GB;     // 'gb' — autocomplete friendly
Country.US;     // 'us'
// ...all 19 supported countries

SortBy.Default | SortBy.Hybrid | SortBy.Date | SortBy.Salary | SortBy.Relevance;
SortDir.Up | SortDir.Down;

// Response-side unions (useful for narrowing):
ContractTime.FullTime | ContractTime.PartTime;
ContractType.Permanent | ContractType.Contract;

Both of these type-check identically:

adzuna.jobs.search({ country: Country.GB, page: 1 });
adzuna.jobs.search({ country: 'gb', page: 1 });

Boolean flags

The Adzuna API uses string "1" values for several filter flags. The SDK accepts boolean and handles translation:

| SDK option | Wire format | | ----------------------- | ---------------------- | | fullTime: true | full_time=1 | | partTime: true | part_time=1 | | contract: true | contract=1 | | permanent: true | permanent=1 | | salaryIncludeUnknown: true | salary_include_unknown=1 | | <any flag>: false | omitted from request |

Error handling

The SDK throws AdzunaError for any 4xx/5xx response whose body matches the documented Exception schema. Other failures (network, parse errors) throw the underlying Ky HTTPError or fetch error.

import { AdzunaClient, AdzunaError } from 'adzuna-sdk';

try {
  await adzuna.jobs.search({ country: 'gb', page: 1 });
} catch (err) {
  if (err instanceof AdzunaError) {
    console.error(`[${err.status}] ${err.exception}: ${err.display}`);
    console.error(`See: ${err.doc}`);
  } else {
    throw err;
  }
}

AdzunaError shape:

class AdzunaError extends Error {
  readonly status: number;        // HTTP status
  readonly exception: string;     // e.g. 'AUTH_FAILED'
  readonly display: string | undefined;  // human-readable message
  readonly doc: string | undefined;      // link to Adzuna docs
}

Retries and timeout

Retries are handled by Ky and default to 2 attempts on 408, 429, 500, 502, 503, 504.

new AdzunaClient({
  appId,
  appKey,
  retry: 5,        // more aggressive retries
  timeout: 10_000, // fail fast
});

To disable retries entirely, pass retry: 0.

Custom fetch (testing, proxies, instrumentation)

The fetch option accepts any fetch-compatible function. Useful for injecting a mock in tests or routing through a proxy.

const adzuna = new AdzunaClient({
  appId,
  appKey,
  fetch: async (input, init) => {
    console.log('→', input);
    const res = await globalThis.fetch(input, init);
    console.log('←', res.status);
    return res;
  },
});

TypeScript types

All response types are exported from the package root:

import type {
  Job,
  JobSearchResults,
  Category,
  Categories,
  SalaryHistogram,
  TopCompanies,
  JobGeoData,
  HistoricalSalary,
  Version,
  Location,
  Company,
  // and option types:
  SearchOptions,
  CategoriesOptions,
  HistogramOptions,
  TopCompaniesOptions,
  GeodataOptions,
  HistoryOptions,
} from 'adzuna-sdk';

If you need the raw OpenAPI types (e.g. to write your own wrapper), they're re-exported as components, operations, and paths.

Development

bun install          # install deps
bun run generate     # regenerate src/schema.ts from adzuna_swagger.json
bun run typecheck    # tsc --noEmit
bun test             # run the test suite
bun run build        # emit dist/

The schema is generated from adzuna_swagger.json via openapi-typescript. Regenerate whenever the swagger changes; the resulting src/schema.ts is checked in so consumers don't need the generator as a runtime dependency.

Releases

Releases are fully automated via GitHub Actions. See RELEASE_PROCESS.md for how to cut a release (spoiler: Actions → Release → Run workflow → pick patch/minor/major).

License

MIT.