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

sound-tank

v2.0.0

Published

A library for interacting with the Reverb Marketplace API

Readme


Quick Start

npm install sound-tank
# or
yarn add sound-tank
# or
pnpm add sound-tank
import Reverb from 'sound-tank';

const reverb = new Reverb({ apiKey: process.env.REVERB_API_KEY });
const { data } = await reverb.listings.getMy({ perPage: 10, state: 'live' });

data.listings.forEach(listing => {
  console.log(`${listing.title}: ${listing.price.display}`);
});

Features

  • Full TypeScript Support - Complete type definitions for all API entities
  • Automatic Pagination - Built-in helpers to fetch all results seamlessly
  • Configuration Management - Easy setup for currency, locale, and shipping preferences
  • Comprehensive Coverage - Listings, orders, and arbitrary endpoint access
  • HTTP Client Abstraction - Clean architecture with testable mock implementations
  • Dual Module Support - Both CommonJS and ESM builds included
  • Well Tested - Extensive unit and integration test coverage
  • Minimal Dependencies - Only requires axios

Table of Contents

Installation

Install via your preferred package manager:

npm install sound-tank
# or
yarn add sound-tank
# or
pnpm add sound-tank

Getting a Reverb API Key

  1. Visit Reverb API Settings
  2. Generate a new API token
  3. Set the appropriate scopes for your use case
  4. Store securely in environment variables
# .env
REVERB_API_KEY=your_api_key_here

You can use the provided .env.example file as a template:

cp .env.example .env
# Edit .env and add your REVERB_API_KEY

Getting Started

Initialize the Client

import Reverb from 'sound-tank';

const reverb = new Reverb({
  apiKey: process.env.REVERB_API_KEY,
  displayCurrency: 'USD', // optional
  locale: 'en',           // optional
  version: '3.0',         // optional
});

Fetch Your Listings

const response = await reverb.listings.getMy({
  perPage: 25,
  page: 1,
  state: 'live',
  query: 'Gibson Les Paul'
});

console.log(response.data.listings);

Error Handling

try {
  const response = await reverb.listings.getMy({ state: 'live' });
  console.log(`Found ${response.data.listings.length} listings`);
} catch (error) {
  console.error('Failed to fetch listings:', error.message);
}

Configuration

Constructor Options

| Option | Type | Required | Default | Description | |--------|------|----------|---------|-------------| | apiKey | string | ✅ Yes | - | Your Reverb API key | | version | string | No | '3.0' | API version to use | | rootEndpoint | string | No | 'https://api.reverb.com/api' | API base URL | | displayCurrency | string | No | 'USD' | Currency for price display | | locale | string | No | 'en' | Language locale (e.g., 'en', 'fr', 'de') | | shippingRegion | string | No | undefined | Shipping region code (e.g., 'US', 'EU') |

Runtime Configuration

You can update configuration after initialization using setters:

reverb.displayCurrency = 'EUR';
reverb.locale = 'fr';
reverb.shippingRegion = 'FR';
reverb.version = '3.0';

These changes automatically update the internal headers and configuration for subsequent API requests.

API Methods

listings.getMy(options?)

Fetch a paginated list of your listings.

Parameters:

  • perPage?: number - Items per page
  • page?: number - Page number (starts at 1)
  • query?: string - Search query to filter listings
  • state?: string - Filter by state: 'live', 'sold', 'draft', or 'all'

Returns: Promise<AxiosResponse<PaginatedReverbResponse<{ listings: Listing[] }>>>

Example:

const response = await reverb.listings.getMy({
  perPage: 50,
  page: 1,
  state: 'live',
  query: 'Fender Stratocaster'
});

const { listings } = response.data;
console.log(`Page 1: ${listings.length} listings`);

listings.getAllMy(options?)

Automatically fetches all listings across all pages using automatic pagination.

Parameters:

  • query?: string - Search query to filter listings
  • state?: ListingStates - Filter by state: ListingStates.LIVE, ListingStates.SOLD, or ListingStates.DRAFT

Returns: Promise<AxiosResponse<Listing[]>>

Example:

const response = await reverb.listings.getAllMy({ state: 'live' });
const allListings = response.data; // All listings from all pages

console.log(`Total listings: ${allListings.length}`);

Note: This method automatically handles pagination by fetching all pages sequentially until all results are retrieved.


listings.getOne(options)

Fetch a single listing by ID.

Parameters:

  • id: string - Listing ID (required)

Returns: Promise<AxiosResponse<Listing>>

Example:

const response = await reverb.listings.getOne({ id: '12345' });
const listing = response.data;

console.log(`${listing.title} - ${listing.price.display}`);
console.log(`Condition: ${listing.condition.displayName}`);

orders.getMy(options?)

Fetch your orders with pagination.

Parameters:

  • page?: number - Page number (starts at 1)
  • perPage?: number - Items per page

Returns: Promise<AxiosResponse<PaginatedReverbResponse<{ orders: Order[] }>>>

Example:

const response = await reverb.orders.getMy({
  page: 1,
  perPage: 25
});

const { orders } = response.data;
orders.forEach(order => {
  console.log(`Order ${order.order_number}: ${order.status}`);
});

_getArbitraryEndpoint(url, params?)

Escape hatch to call any Reverb endpoint not yet wrapped by a resource. The _ prefix indicates this is not part of the stable public API but is intentionally supported.

Parameters:

  • url: string - Endpoint URL (absolute or relative to root endpoint)
  • params?: object - Query parameters

Returns: Promise<AxiosResponse<unknown>>

Example:

// Fetch categories
const categories = await reverb._getArbitraryEndpoint('/categories/flat');

// Fetch listing conditions
const conditions = await reverb._getArbitraryEndpoint('/listing_conditions');

console.log(categories.data);

TypeScript Usage

Sound Tank is written in TypeScript and provides comprehensive type definitions for the entire Reverb API.

Importing Types

import Reverb, {
  Listing,
  Order,
  Price,
  ListingStates,
  ListingCondition,
  ShippingRate,
  Category,
  ReverbOptions
} from 'sound-tank';

Working with Typed Responses

const response = await reverb.listings.getMy();
const listings: Listing[] = response.data.listings;

listings.forEach((listing: Listing) => {
  const price: Price = listing.price;
  const condition: ListingCondition = listing.condition;

  console.log(`${listing.title}`);
  console.log(`  Price: ${price.display} (${price.currency})`);
  console.log(`  Condition: ${condition.displayName}`);
  console.log(`  Year: ${listing.year}`);
});

Available Types

The SDK exports comprehensive TypeScript types including:

Listing Types:

  • Listing - Complete listing data with make, model, price, condition, shipping, etc.
  • ListingState - Listing status information
  • ListingStates - Enum for state values (LIVE, SOLD, DRAFT)
  • ListingCondition - Item condition with UUID and display name
  • ListingShipping - Shipping information and rates
  • ListingStats - View and watch counts

Order Types:

  • Order - Complete order information with buyer, seller, shipping, pricing details
  • OrderStatus - Order status information
  • ShippingAddress - Complete address information

Pricing Types:

  • Price - Currency-aware price with amount, currency, symbol, and formatted display
  • ShippingRate - Regional shipping costs

Other Types:

  • Category - Product categorization
  • Link - HATEOAS navigation links
  • ReverbOptions - SDK configuration options

Advanced Features

Automatic Pagination

The getAllMyListings method demonstrates automatic pagination, fetching all results across multiple API pages:

// Manually paginate
let page = 1;
let allListings = [];
let response;

do {
  response = await reverb.listings.getMy({ page, perPage: 50 });
  allListings = allListings.concat(response.data.listings);
  page++;
} while (response.data.listings.length === 50);

// Or use the built-in helper
const autoResponse = await reverb.listings.getAllMy();
const listings = autoResponse.data; // Same result, simpler code

HTTP Client Abstraction

Sound Tank uses an HTTP client abstraction for testability:

import { MockHttpClient } from 'sound-tank/http';

// Use mock client for testing
const mockClient = new MockHttpClient();
// Your tests here

Configuration Access

Access the internal configuration and headers:

const config = reverb.config;
console.log(config.rootEndpoint);    // 'https://api.reverb.com/api'
console.log(config.displayCurrency); // 'USD'
console.log(config.locale);          // 'en'

const headers = reverb.headers;
console.log(headers['Authorization']);      // 'Bearer your_api_key'
console.log(headers['X-Display-Currency']); // 'USD'

Examples

Find All Guitars Under $1000

const reverb = new Reverb({ apiKey: process.env.REVERB_API_KEY });

const response = await reverb.listings.getAllMy({ query: 'guitar' });
const affordable = response.data.filter(listing =>
  listing.price.amount_cents < 100000 // $1000 = 100,000 cents
);

console.log(`Found ${affordable.length} guitars under $1000`);
affordable.forEach(listing => {
  console.log(`  ${listing.title}: ${listing.price.display}`);
});

Multi-Currency Price Display

const reverb = new Reverb({ apiKey: process.env.REVERB_API_KEY });

// Get prices in USD
reverb.displayCurrency = 'USD';
const usdResponse = await reverb.listings.getMy({ perPage: 5 });
console.log('USD Prices:');
usdResponse.data.listings.forEach(l =>
  console.log(`  ${l.title}: ${l.price.display}`)
);

// Switch to EUR
reverb.displayCurrency = 'EUR';
const eurResponse = await reverb.listings.getMy({ perPage: 5 });
console.log('\nEUR Prices:');
eurResponse.data.listings.forEach(l =>
  console.log(`  ${l.title}: ${l.price.display}`)
);

Export Listings to CSV

const response = await reverb.listings.getAllMy({ state: 'live' });

const csvHeader = 'ID,Title,Price,Currency,Condition,Year,State\n';
const csvRows = response.data.map(listing =>
  `${listing.id},"${listing.title}",${listing.price.amount},${listing.price.currency},${listing.condition.displayName},${listing.year},${listing.state.slug}`
).join('\n');

const csv = csvHeader + csvRows;
console.log(csv);
// Save to file or send via API

Search and Filter

// Search for specific items
const response = await reverb.listings.getMy({
  query: 'Les Paul',
  state: 'live',
  perPage: 100
});

// Filter by condition
const excellent = response.data.listings.filter(
  listing => listing.condition.displayName === 'Excellent'
);

// Filter by year
const vintage = response.data.listings.filter(
  listing => parseInt(listing.year) < 1980
);

console.log(`Found ${excellent.length} in excellent condition`);
console.log(`Found ${vintage.length} vintage items`);

Development

Prerequisites

  • Node.js 18 or higher
  • Yarn package manager

Setup

# Clone the repository
git clone https://github.com/ZacharyEggert/sound-tank.git
cd sound-tank

# Install dependencies
yarn install

# Copy environment template
cp .env.example .env
# Edit .env and add your REVERB_API_KEY

Project Structure

sound-tank/
├── src/
│   ├── Reverb.ts           # Main SDK class
│   ├── index.ts            # Entry point
│   ├── types.ts            # TypeScript type definitions
│   ├── config/
│   │   └── ReverbConfig.ts # Configuration management
│   ├── http/
│   │   ├── HttpClient.ts         # HTTP client interface
│   │   ├── AxiosHttpClient.ts    # Axios implementation
│   │   └── MockHttpClient.ts     # Mock for testing
│   ├── methods/
│   │   ├── listings/       # Listing operations (pure functions)
│   │   └── orders/         # Order operations (pure functions)
│   ├── resources/
│   │   ├── ListingsResource.ts   # Listings resource class
│   │   └── OrdersResource.ts     # Orders resource class
│   └── utils/              # Helper utilities (pagination, url/query builders, logger)
├── tests/                  # Test files
├── dist/                   # Build output (git-ignored)
├── package.json
├── tsconfig.json           # TypeScript configuration
├── tsup.config.ts          # Build configuration
└── vite.config.mts         # Test configuration

Debugging

Set SOUNDTANK_LOG_LEVEL to enable SDK logging:

SOUNDTANK_LOG_LEVEL=DEBUG yarn dev

Valid values: ERROR | WARN | INFO | DEBUG | TRACE. Silent by default.

Available Scripts

| Command | Description | |---------|-------------| | yarn dev | Run tests in watch mode during development | | yarn test | Run all tests once | | yarn build | Build production bundles (CJS + ESM + declarations) | | yarn lint | Type-check code with TypeScript compiler | | yarn ci | Run full CI pipeline (install, lint, test, build) | | yarn release | Build and publish to npm (uses changesets) |

Build System

Sound Tank uses tsup for building:

  • Dual Output: CommonJS (dist/index.js) and ESM (dist/index.mjs)
  • Type Declarations: Full TypeScript .d.ts files
  • Source Maps: Included for debugging
  • Tree-shaking: Optimized bundles
  • External Dependencies: axios marked as external (not bundled)

Build outputs:

dist/
├── index.js        # CommonJS
├── index.mjs       # ES Modules
├── index.d.ts      # TypeScript declarations
└── *.map           # Source maps

Testing

Sound Tank uses Vitest for testing with comprehensive coverage.

Run Tests

# Run all tests once
yarn test

# Run tests in watch mode (for development)
yarn dev

# Run tests with coverage report
yarn test --coverage

Test Structure

  • Unit Tests: Test individual functions in isolation
  • Integration Tests: Test real API interactions (require REVERB_API_KEY in .env)

Writing Tests

Integration tests require a valid API key:

import { config } from 'dotenv';
config(); // Load .env

const reverb = new Reverb({ apiKey: process.env.REVERB_API_KEY });

// Your tests here

Tests are located in the tests/ directory and mirror the src/ structure.

Contributing

Contributions are welcome! Here's how to get started:

Process

  1. Fork the repository on GitHub
  2. Clone your fork locally
  3. Create a feature branch: git checkout -b feature/amazing-feature
  4. Make your changes with clear, focused commits
  5. Test thoroughly: yarn test
  6. Lint your code: yarn lint
  7. Commit with descriptive messages
  8. Push to your fork: git push origin feature/amazing-feature
  9. Open a Pull Request

Guidelines

  • Write TypeScript with strict types
  • Add tests for new features
  • Update documentation as needed
  • Follow existing code style (Prettier configured)
  • Ensure all tests pass (yarn test)
  • Keep commits atomic and well-described
  • Run full CI locally before pushing: yarn ci

Code Style

This project uses:

  • TypeScript strict mode
  • Prettier for formatting (run automatically)
  • ESLint for linting
  • 2-space indentation
  • Single quotes for strings
  • Trailing commas

Changesets

This project uses Changesets for version management:

# After making changes, create a changeset
npx changeset

# Follow prompts to describe your changes
# Commit the generated changeset file

Release Process

Releases are automated via GitHub Actions:

  1. Create and commit a changeset for your changes
  2. Push to main branch (after PR approval)
  3. Changesets GitHub Action creates a "Version Packages" PR
  4. Review and merge the Version Packages PR
  5. Package automatically publishes to npm with provenance

The project uses OIDC trusted publishing for npm, so no NPM_TOKEN is needed.

Links & Resources

License

MIT © Zachary Eggert

See LICENSE for details.