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

apimo.js

v1.1.0

Published

A wrapper for the Apimo API with catalog caching for building custom Real Estate website using their technologies.

Readme

Apimo.js

npm version CI codecov

A TypeScript-first wrapper for the Apimo API with intelligent caching, automatic retries, rate limiting, and catalog transformation for building custom real estate websites.

Note: This library is in active development. No breaking changes are planned, but please pin your version in production.

Features

  • 🔷 TypeScript-first — Full type safety with Zod schema validation on every response
  • 📦 Smart caching — Memory, Filesystem, and Dummy cache adapters with automatic TTL invalidation
  • 🔁 Automatic retries — Configurable retry strategy (exponential / linear / fixed back-off) for transient failures
  • 🚦 Rate limiting — Built-in Bottleneck throttle to stay within Apimo's API limits
  • 🌍 Multi-language — First-class culture support for all endpoints and catalog lookups
  • 🏗️ Full coverage — Properties, agencies, and catalog endpoints
  • 🐛 Developer-friendly errors — Typed error classes with status codes, request URLs, and response bodies

Installation

npm install apimo.js
# or
yarn add apimo.js

Quick Start

import { Apimo, MemoryCache } from 'apimo.js'

const api = new Apimo(
  'YOUR_PROVIDER_ID', // Numeric string — request from Apimo support
  'YOUR_API_TOKEN', // Secret token — request from Apimo support
  {
    culture: 'en',
    catalogs: {
      cache: { active: true, adapter: new MemoryCache() },
    },
  },
)

const { properties } = await api.fetchProperties(agencyId)
const { agencies } = await api.fetchAgencies()

Configuration

Full Config Reference

const api = new Apimo(providerId, token, {
  // Base URL for API requests. Default: 'https://api.apimo.pro'
  baseUrl: 'https://api.apimo.pro',

  // Default language for all requests. Default: 'en'
  culture: 'en',

  catalogs: {
    cache: {
      // Enable catalog caching. Default: true
      active: true,
      // Cache implementation. Default: new MemoryCache()
      adapter: new MemoryCache(),
    },
    transform: {
      // Resolve catalog IDs to human-readable names. Default: true
      active: true,
      // Optionally supply your own resolver function
      transformFn: async (catalogName, culture, id) => { /* ... */ },
    },
  },

  retry: {
    // Total number of attempts (1 = no retries). Default: 3
    attempts: 3,
    // Delay in ms before the first retry. Default: 200
    initialDelayMs: 200,
    // Back-off strategy: 'exponential' | 'linear' | 'fixed'. Default: 'exponential'
    backoff: 'exponential',
  },
})

Cache Adapters

MemoryCache (default)

Stores catalog entries in process memory. Fast and zero-config. Data is lost when the process restarts.

import { MemoryCache } from 'apimo.js'

const memoryCache = new MemoryCache({ cacheExpirationMs: 7 * 24 * 60 * 60 * 1000 }) // default: 1 week

FilesystemCache

Persists catalog entries to JSON files on disk. Survives restarts.

import { FilesystemCache } from 'apimo.js'

const fsCache = new FilesystemCache({
  path: './cache/catalogs', // default
  cacheExpirationMs: 7 * 24 * 60 * 60 * 1000,
})

DummyCache

Disables caching entirely. Every catalog look-up hits the API directly.

import { DummyCache } from 'apimo.js'

const dummyCache = new DummyCache()

API Reference

Properties

fetchProperties(agencyId, options?)

const { total_items, properties, timestamp } = await api.fetchProperties(agencyId, {
  culture: 'fr',
  limit: 20,
  offset: 0,
})

Agencies

fetchAgencies(options?)

const { total_items, agencies } = await api.fetchAgencies({ limit: 10 })

Catalogs

fetchCatalogs()

Returns the list of all available catalog definitions.

const catalogs = await api.fetchCatalogs()

fetchCatalog(catalogName, options?)

Fetches raw entries for a specific catalog.

const entries = await api.fetchCatalog('property_type', { culture: 'en' })

getCatalogEntries(catalogName, options?)

Returns catalog entries, automatically populating the cache when empty or expired.

const entries = await api.getCatalogEntries('property_category', { culture: 'fr' })

Low-level API

get(path, schema, options?)

Makes a direct API call with Zod schema validation. Retries are applied automatically.

import { z } from 'zod'

const result = await api.get(
  ['agencies', '123', 'properties'],
  z.object({ total_items: z.number() }),
  { culture: 'en', limit: 10 },
)

fetch(...args)

Raw authenticated fetch — adds the Authorization header and runs through the rate limiter. Use when you need full control.

const response = await api.fetch('https://api.apimo.pro/catalogs')

Error Handling

All errors thrown by the library extend ApimoError, so you can catch them selectively.

Error Hierarchy

| Class | When thrown | |---|---| | ApimoError | Base class — catch-all | | ApiHttpError | Any non-2xx HTTP response — has .statusCode, .url, .responseBody | | ApiBadRequestError | HTTP 400 — malformed request | | ApiUnauthorizedError | HTTP 401 — invalid credentials | | ApiForbiddenError | HTTP 403 — insufficient permissions | | ApiNotFoundError | HTTP 404 — resource does not exist | | ApiRateLimitError | HTTP 429 — rate limit exceeded | | ApiServerError | HTTP 5xx — Apimo server-side failure | | ApiResponseValidationError | Response body doesn't match expected schema — has .url, .zodError | | ApiConfigurationError | Invalid constructor arguments (empty credentials, bad baseUrl) | | ApiRetryExhaustedError | All retry attempts failed — has .attempts, .cause |

Retry behaviour

Retries are applied automatically to transient errors (429, 5xx, network failures). Non-transient errors (4xx except 429, schema validation failures) are thrown immediately without retrying.

When all attempts are exhausted, ApiRetryExhaustedError is thrown. Its .cause property holds the underlying error from the last attempt.

import {
  ApimoError,
  ApiResponseValidationError,
  ApiRetryExhaustedError,
  ApiServerError,
  ApiUnauthorizedError,
  CacheExpiredError,
} from 'apimo.js'

try {
  const { properties } = await api.fetchProperties(agencyId)
}
catch (error) {
  if (error instanceof ApiUnauthorizedError) {
    // Credentials are wrong — fail fast, no retry
    console.error('Check your provider ID and token.')
  }
  else if (error instanceof ApiRetryExhaustedError) {
    // Transient failure survived all retries
    console.error(`Failed after ${error.attempts} attempts.`, error.cause)
  }
  else if (error instanceof ApiResponseValidationError) {
    // The API changed its response shape
    console.error('Schema mismatch:', error.zodError.format())
  }
  else if (error instanceof ApimoError) {
    // Any other library error
    console.error(error.message)
  }
}

Disabling retries

const api = new Apimo(providerId, token, {
  retry: { attempts: 1 }, // 1 attempt = no retries
})

Data Transformation

When catalogs.transform.active is true (the default), numeric catalog IDs in API responses are automatically resolved to their human-readable names via the cache.

// Without transformation:   { type: 1, category: 6 }
// With transformation:       { type: 'House', category: 'Sale' }

Custom transformer

const api = new Apimo(providerId, token, {
  catalogs: {
    transform: {
      active: true,
      transformFn: async (catalogName, culture, id) => {
        return await myDatabase.lookupCatalog(catalogName, culture, id)
      },
    },
  },
})

Rate Limiting

The library uses Bottleneck internally to throttle requests to 10 per second — well within Apimo's documented daily limits. No extra configuration is required.

TypeScript

All public methods and types are fully typed. Import what you need:

import type {
  AdditionalConfig,
  ApimoAgency,
  ApimoProperty,
  CatalogEntry,
} from 'apimo.js'

Development

# Install dependencies
yarn install

# Run tests
yarn test

# Run tests with coverage
yarn test-coverage

# Lint
yarn lint

# Build
yarn build

Getting API Credentials

You need two things to use this library:

  1. Provider ID — a numeric string identifying your site
  2. API Token — your secret authentication token

Request both by contacting Apimo customer service.

Roadmap

  • [ ] Minified production build
  • [ ] Complete JSDoc coverage for all public methods

Support

License

MIT — see LICENSE for details.


Made with ❤️ by Vitaly Lysen