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 🙏

© 2025 – Pkg Stats / Ryan Hefner

@akomalabs/kemono

v0.1.2

Published

A production-grade TypeScript API wrapper for the Kemono/Coomer platforms

Downloads

65

Readme

@akomalabs/kemono

A production-grade TypeScript API wrapper for the Kemono/Coomer platforms with advanced features like schema validation, caching, and rate limiting.

Features

  • 🚀 Full TypeScript support with comprehensive type definitions
  • ✅ Zod schema validation for type safety
  • 🔄 Built-in caching with in-memory and Redis support
  • 🛡️ Configurable rate limiting
  • 📝 Detailed logging options
  • ⚡ Bun-optimized for maximum performance
  • 🔒 Session management for favorites
  • 🌐 Support for multiple base URLs (kemono.su and coomer.su)

Installation

bun add @akomalabs/kemono

Quick Start

import { KemonoClient } from '@akomalabs/kemono';

// Create a client instance
const client = new KemonoClient({
  baseUrl: 'https://kemono.su/api/v1', // or 'https://coomer.su/api/v1'
  cache: {
    enabled: true,
    ttl: 300, // 5 minutes
  },
});

// Fetch a creator's profile
const profile = await client.creators.getProfile('onlyfans', 'creatorId');

// Get recent posts
const posts = await client.posts.listRecent();

// Get posts from a specific creator
const creatorPosts = await client.posts.getByCreator('onlyfans', 'creatorId');

// Get a specific post with attachments
const post = await client.posts.getPost('onlyfans', 'creatorId', 'postId');
const attachments = await client.posts.getAttachments('onlyfans', 'creatorId', 'postId');

Type System

The library uses Zod for runtime type validation and TypeScript for static typing. All types are inferred from Zod schemas, ensuring runtime type safety.

Available Types and Schemas

import {
  // Schemas (for runtime validation)
  KemonoBaseUrlSchema,
  KemonoServiceSchema,
  KemonoConfigSchema,
  FileSchema,
  AttachmentSchema,
  PostSchema,
  CreatorSchema,
  SearchParamsSchema,
  
  // Types (for static typing)
  type KemonoBaseUrl,
  type KemonoService,
  type KemonoConfig,
  type File,
  type Attachment,
  type Post,
  type Creator,
  type SearchParams,
  
  // Error types
  KemonoApiError,
  AuthenticationError,
  NotFoundError,
  ValidationError,
  RateLimitError,
} from '@akomalabs/kemono';

// Example: Using schemas for runtime validation
const config = KemonoConfigSchema.parse({
  baseUrl: 'https://kemono.su/api/v1',
  cache: { enabled: true },
});

// Example: Using types for static typing
const post: Post = {
  id: '123',
  title: 'Example Post',
  content: 'Hello World',
  // ...other required fields
};

Configuration

The client accepts a comprehensive configuration object that is validated at runtime:

interface KemonoConfig {
  // Base URL for API requests
  baseUrl?: 'https://kemono.su/api/v1' | 'https://coomer.su/api/v1';
  
  // Session key for favorites management
  sessionKey?: string;
  
  // Caching configuration
  cache?: {
    enabled: boolean;
    ttl?: number;
    redis?: {
      host: string;
      port: number;
      password?: string;
    };
  };
  
  // Rate limiting configuration
  rateLimit?: {
    maxRequests: number;
    windowMs: number;
  };
  
  // Logging configuration
  logging?: {
    enabled: boolean;
    level?: 'error' | 'warn' | 'info' | 'debug';
  };
}

API Reference

Creators API

// List all creators
const creators = await client.creators.listAll();

// Get a creator's profile
const profile = await client.creators.getProfile(service, creatorId);

Posts API

// List recent posts
const posts = await client.posts.listRecent({
  q?: string;     // Search query
  o?: number;     // Offset
  limit?: number; // Number of posts to return
});

// Get posts by creator
const posts = await client.posts.getByCreator(service, creatorId);

// Get a specific post
const post = await client.posts.getPost(service, creatorId, postId);

// Get post attachments
const attachments = await client.posts.getAttachments(service, creatorId, postId);

Favorites API

The Favorites API requires a valid session key to be provided in the client configuration. All favorites methods will throw an AuthenticationError if the session key is missing or invalid.

// Initialize client with session key
const client = new KemonoClient({
  baseUrl: 'https://kemono.su/api/v1',
  sessionKey: 'your-session-key', // Required for favorites
});

// List favorite creators with error handling
try {
  const creators = await client.favorites.listCreators();
  console.log(`Found ${creators.length} favorite creators`);
  
  // Access creator properties
  creators.forEach(creator => {
    console.log(`${creator.name} (${creator.service})`);
    console.log(`Last imported: ${creator.last_imported}`);
    console.log(`Favorite sequence: ${creator.faved_seq}`);
  });
} catch (error) {
  if (error instanceof AuthenticationError) {
    console.error('Please provide a valid session key');
  } else {
    console.error('Failed to fetch favorite creators:', error);
  }
}

// List favorite posts with pagination example
try {
  const posts = await client.favorites.listPosts();
  console.log(`Found ${posts.length} favorite posts`);
  
  // Access post properties
  posts.forEach(post => {
    console.log(`${post.title} by ${post.user}`);
    console.log(`Published: ${post.published}`);
    console.log(`Content: ${post.content.substring(0, 100)}...`);
    
    // Handle attachments
    if (post.attachments.length > 0) {
      console.log('Attachments:', post.attachments.map(a => a.name).join(', '));
    }
  });
} catch (error) {
  if (error instanceof AuthenticationError) {
    console.error('Please provide a valid session key');
  } else {
    console.error('Failed to fetch favorite posts:', error);
  }
}

// Managing favorites
async function manageFavorites() {
  try {
    // Add a creator to favorites
    await client.favorites.addCreator('fanbox', 'creator123');
    
    // Add a post to favorites
    await client.favorites.addPost('fanbox', 'creator123', 'post456');
    
    // Remove from favorites
    await client.favorites.removeCreator('fanbox', 'creator123');
    await client.favorites.removePost('fanbox', 'creator123', 'post456');
    
  } catch (error) {
    if (error instanceof AuthenticationError) {
      console.error('Session key required');
    } else if (error instanceof NotFoundError) {
      console.error('Creator or post not found');
    } else if (error instanceof RateLimitError) {
      console.error('Too many requests, please try again later');
    } else {
      console.error('Operation failed:', error);
    }
  }
}

// Response Types

interface FavoriteCreator {
  id: string;          // Creator's unique identifier
  name: string;        // Creator's display name
  service: KemonoService; // Platform (e.g., 'fanbox', 'patreon')
  indexed: string;     // ISO date when the creator was indexed
  updated: string;     // ISO date of last update
  faved_seq: number;   // Sequence number in favorites
  last_imported: string; // ISO date of last import
  public_id: string | null;    // Public identifier if available
  relation_id: string | null;  // Related identifier if available
}

interface FavoritePost {
  id: string;          // Post unique identifier
  user: string;        // Creator's ID
  service: KemonoService; // Platform service
  title: string;       // Post title
  content: string;     // Post content
  added: string;       // ISO date when added to favorites
  published: string;   // ISO date when published
  edited: string | null; // ISO date of last edit
  file: {             // Main file if present
    name: string;     // File name
    path: string;     // File path
  };
  attachments: any[]; // Additional files
  embed: Record<string, any>; // Embedded content
  shared_file: boolean; // Indicates if file is shared
  faved_seq: number;    // Sequence number in favorites
}

Supported Services

  • fanbox
  • patreon
  • gumroad
  • subscribestar
  • dlsite
  • fantia
  • onlyfans
  • fansly

Error Handling

The client includes custom error classes for better error handling:

try {
  await client.creators.getProfile('onlyfans', 'nonexistent');
} catch (error) {
  if (error instanceof NotFoundError) {
    // Handle 404 error
  } else if (error instanceof AuthenticationError) {
    // Handle authentication error
  } else if (error instanceof ValidationError) {
    // Handle validation errors (e.g., invalid config)
  } else if (error instanceof RateLimitError) {
    // Handle rate limit errors
  } else if (error instanceof KemonoApiError) {
    // Handle other API errors
  }
}

Development

# Install dependencies
bun install

# Run tests
bun test

# Build package
bun run build

License

MIT License

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.