{"id":31693868,"url":"https://github.com/timmikeladze/ygoapi","last_synced_at":"2025-10-08T15:53:43.659Z","repository":{"id":317163213,"uuid":"1056771161","full_name":"TimMikeladze/ygoapi","owner":"TimMikeladze","description":"⚔️ TypeScript client for the YGOPRODeck API, a Yu-Gi-Oh! card database.","archived":false,"fork":false,"pushed_at":"2025-09-20T07:23:04.000Z","size":79,"stargazers_count":1,"open_issues_count":0,"forks_count":1,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-09-29T09:34:28.747Z","etag":null,"topics":["ygo","ygo-api","ygoapi","yu-gi-oh","yu-gi-oh-tcg","yugi","yugioh","yugioh-api"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/TimMikeladze.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-09-14T19:11:10.000Z","updated_at":"2025-09-22T18:35:38.000Z","dependencies_parsed_at":"2025-09-29T09:34:32.800Z","dependency_job_id":null,"html_url":"https://github.com/TimMikeladze/ygoapi","commit_stats":null,"previous_names":["timmikeladze/ygoapi"],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/TimMikeladze/ygoapi","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TimMikeladze%2Fygoapi","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TimMikeladze%2Fygoapi/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TimMikeladze%2Fygoapi/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TimMikeladze%2Fygoapi/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/TimMikeladze","download_url":"https://codeload.github.com/TimMikeladze/ygoapi/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TimMikeladze%2Fygoapi/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":278972328,"owners_count":26078017,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-08T02:00:06.501Z","response_time":56,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ygo","ygo-api","ygoapi","yu-gi-oh","yu-gi-oh-tcg","yugi","yugioh","yugioh-api"],"created_at":"2025-10-08T15:53:35.148Z","updated_at":"2025-10-08T15:53:43.650Z","avatar_url":"https://github.com/TimMikeladze.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# YGOPRODeck API TypeScript Client\n\nA comprehensive TypeScript client for the YGOPRODeck API, providing type-safe access to the Yu-Gi-Oh! card database. See the [API guide](https://ygoprodeck.com/api-guide/) for more information.\n\n## Features\n\n- 🔒 **Type-safe** - Full TypeScript support.\n- 🎯 **Complete coverage** - ALL YGOPRODeck API endpoints and parameters supported.\n- 🛠 **Rich utilities** - Helper functions and convenience methods for common tasks.\n- 📦 **Built in caching** - Caches API responses and card images.\n\n## Installation\n\n```bash\nnpm install ygoapi\n```\n\n## Quick Start\n\n```typescript\nimport { YgoApi } from 'ygoapi'\n\nconst api = new YgoApi()\n\n// Get a specific card by name\nconst darkMagician = await api.getCardByName('Dark Magician')\nconsole.log(darkMagician)\n\n// Search cards by fuzzy name matching\nconst magicians = await api.searchCards('Magician', {\n\tnum: 10,\n\toffset: 0,\n})\n\nconsole.log(magicians.data)\n// Get cards from a specific archetype\nconst blueEyesCards = await api.getCardsByArchetype('Blue-Eyes', {\n\tnum: 10,\n\toffset: 0,\n})\nconsole.log(blueEyesCards.data)\n```\n\nRun the example:\n\n```bash\nnode examples/basic.js\n```\n\n## Using the Cache\n\nThe YgoApi client supports two types of caching:\n\n1. **API Response Cache** - Caches API responses (card data, sets, archetypes)\n2. **Image Cache** - Downloads and caches card images to local filesystem\n\n### API Response Cache\n\n#### Option 1: Filesystem Cache (Recommended)\n\n```typescript\nimport { YgoApi, FileSystemKVStore } from 'ygoapi';\n\n// Use built-in filesystem cache for API responses\nconst api = new YgoApi({\n  cache: new FileSystemKVStore({\n    cacheDir: '.cache/ygoapi/data',  // Default location\n    maxAge: 10 * 60 * 1000           // Fallback TTL (10 minutes)\n  }),\n  cacheTtl: 10 * 60 * 1000            // TTL per cache entry (10 minutes)\n});\n\n// Data is automatically cached to disk\nconst card = await api.getCardByName('Dark Magician');\n\n// Clean up old cache files periodically\nif (api.cache instanceof FileSystemKVStore) {\n  await api.cache.cleanup();\n}\n```\n\n**How TTL works:**\n- `cacheTtl` is used as the TTL for each individual cache entry\n- `maxAge` is the fallback if `cacheTtl` is not provided\n- Each cached file's age is checked on retrieval\n\n#### Option 2: In-Memory Cache\n\n```typescript\nimport { YgoApi } from 'ygoapi';\n\n// Simple in-memory cache implementation\nclass MemoryCache {\n  private cache = new Map\u003cstring, { value: string; expires: number }\u003e();\n\n  get(key: string): string | null {\n    const item = this.cache.get(key);\n    if (!item || Date.now() \u003e item.expires) {\n      this.cache.delete(key);\n      return null;\n    }\n    return item.value;\n  }\n\n  set(key: string, value: string, ttl?: number): void {\n    const expires = Date.now() + (ttl || 300000); // Default 5 minutes\n    this.cache.set(key, { value, expires });\n  }\n\n  delete(key: string): void {\n    this.cache.delete(key);\n  }\n}\n\n// Create API instance with in-memory caching\nconst api = new YgoApi({\n  cache: new MemoryCache(),\n  cacheTtl: 600000 // 10 minutes\n});\n```\n\n### Image Cache (Filesystem-based)\n\nThe image cache automatically downloads and stores card images locally for faster access:\n\n```typescript\nimport { YgoApi } from 'ygoapi';\n\n// Enable image caching with default settings\nconst api = new YgoApi({\n  imageCacheEnabled: true  // Uses default: .cache/ygoapi/images\n});\n\n// Or configure custom cache directory\nconst api = new YgoApi({\n  imageCacheEnabled: true,\n  imageCache: {\n    cacheDir: './my-card-images',\n    maxAge: 7 * 24 * 60 * 60 * 1000  // 7 days\n  }\n});\n\n// Images are automatically cached in the background\nconst card = await api.getCardByName('Dark Magician');\n\n// Get local filesystem path to cached image\nconst imagePath = await api.getLocalImagePath(card, 'small');\n// Returns: '.cache/ygoapi/images/46986414/small.jpg'\n\n// Use in your application\nif (imagePath) {\n  console.log(`Image cached at: ${imagePath}`);\n  // Read file, serve via HTTP, etc.\n}\n```\n\n### Image Cache Features\n\n- **Automatic caching** - Images download in background when cards are fetched\n- **Multiple sizes** - Caches default, small, and cropped versions\n- **Organized storage** - Files organized by card ID in subdirectories\n- **Cleanup support** - Remove old cached images\n\n```typescript\n// Get different image sizes\nconst defaultImage = await api.getLocalImagePath(card, 'default');\nconst smallImage = await api.getLocalImagePath(card, 'small');\nconst croppedImage = await api.getLocalImagePath(card, 'cropped');\n\n// Clean up old cached images (based on maxAge)\nawait api.cleanupImageCache();\n```\n\n### Cache Directory Structure\n\n```\n.cache/ygoapi/images/\n├── 46986414/\n│   ├── default.jpg\n│   ├── small.jpg\n│   └── cropped.jpg\n├── 89631139/\n│   ├── default.jpg\n│   ├── small.jpg\n│   └── cropped.jpg\n```\n\n### Custom Image Cache Implementation\n\nYou can provide a custom KVStore for image caching:\n\n```typescript\nclass S3ImageCache {\n  async get(key: string): Promise\u003cstring | null\u003e {\n    // Return S3 URL or local path\n  }\n\n  async set(key: string, value: string): Promise\u003cvoid\u003e {\n    // Download from value URL and upload to S3\n  }\n\n  async delete(key: string): Promise\u003cvoid\u003e {\n    // Delete from S3\n  }\n}\n\nconst api = new YgoApi({\n  imageCacheEnabled: true,\n  imageCache: new S3ImageCache()\n});\n```\n\n### Complete Caching Example\n\nHere's a complete example using both API response cache and image cache:\n\n```typescript\nimport { YgoApi, FileSystemKVStore, FileSystemImageCache } from 'ygoapi';\n\nconst api = new YgoApi({\n  // API response cache (disk-based)\n  cache: new FileSystemKVStore({\n    cacheDir: '.cache/ygoapi/data',\n    maxAge: 10 * 60 * 1000  // 10 minutes\n  }),\n  cacheTtl: 10 * 60 * 1000,\n\n  // Image cache (disk-based)\n  imageCacheEnabled: true,\n  imageCache: {\n    cacheDir: '.cache/ygoapi/images',\n    maxAge: 7 * 24 * 60 * 60 * 1000  // 7 days\n  }\n});\n\n// First request: Fetches from API, caches response and images\nconst card = await api.getCardByName('Dark Magician');\n\n// Second request: Uses cached response (instant)\nconst card2 = await api.getCardByName('Dark Magician');\n\n// Get local image path\nconst imagePath = await api.getLocalImagePath(card, 'small');\nconsole.log(`Image cached at: ${imagePath}`);\n\n// Periodic cleanup (run in background or cron job)\nif (api.cache instanceof FileSystemKVStore) {\n  await api.cache.cleanup();\n}\nawait api.cleanupImageCache();\n```\n\n### Cache Directory Structure\n\nWhen using filesystem caches, your directory structure will look like:\n\n```\n.cache/ygoapi/\n├── data/                           # API response cache\n│   ├── a1b2c3d4...json            # Hashed cache keys\n│   ├── e5f6g7h8...json\n│   └── ...\n└── images/                         # Image cache\n    ├── 46986414/\n    │   ├── default.jpg\n    │   ├── small.jpg\n    │   └── cropped.jpg\n    └── 89631139/\n        ├── default.jpg\n        ├── small.jpg\n        └── cropped.jpg\n```\n\n### Cache Benefits\n\n- **Reduces API calls** - Repeated requests return cached data\n- **Respects rate limits** - Helps stay under 20 requests/second limit\n- **Faster image loading** - Serve images from local filesystem\n- **Offline support** - Access cached images without internet\n- **Persistent cache** - Survives application restarts\n- **Automatic cleanup** - Remove expired cache files\n- **Improves performance** - Faster response times for cached data\n- **Automatic invalidation** - Cache entries expire based on TTL\n\nThe cache automatically stores successful API responses and serves them for subsequent identical requests until the TTL expires.\n\nYou can also use the default export for convenience:\n\n```typescript\nimport api from 'ygoapi';\n\nconst card = await api.getCardByName('Blue-Eyes White Dragon');\n```\n\n## API Reference\n\n### Core Methods\n\n#### `getCardInfo(params?: CardInfoParams): Promise\u003cCardInfoResponse\u003e`\n\nGet card information with optional filtering parameters.\n\n```typescript\n// Get all cards\nconst allCards = await api.getCardInfo();\n\n// Get cards with specific filters\nconst waterDragons = await api.getCardInfo({\n  attribute: 'WATER',\n  race: 'Dragon',\n  type: 'Effect Monster'\n});\n\n// Get cards with ATK greater than 2500\nconst strongCards = await api.getCardInfo({\n  atk: 'gt2500'\n});\n```\n\n#### `getCardByName(name: string): Promise\u003cCard | null\u003e`\n\nGet a single card by exact name match.\n\n```typescript\nconst card = await api.getCardByName('Dark Magician');\nif (card) {\n  console.log(`ATK: ${card.atk}, DEF: ${card.def}`);\n}\n```\n\n#### `getCardById(id: string | number): Promise\u003cCard | null\u003e`\n\nGet a single card by its ID (passcode).\n\n```typescript\nconst card = await api.getCardById('46986414'); // Dark Magician\n```\n\n#### `searchCards(fname: string, params?: Omit\u003cCardInfoParams, 'fname'\u003e): Promise\u003cCardInfoResponse\u003e`\n\nSearch cards using fuzzy name matching.\n\n```typescript\n// Find all cards with \"Dragon\" in the name\nconst dragons = await api.searchCards('Dragon');\n\n// Find all LIGHT Dragons with \"Dragon\" in name\nconst lightDragons = await api.searchCards('Dragon', {\n  attribute: 'LIGHT'\n});\n```\n\n#### `getCardsByArchetype(archetype: string, params?: Omit\u003cCardInfoParams, 'archetype'\u003e): Promise\u003cCardInfoResponse\u003e`\n\nGet all cards from a specific archetype.\n\n```typescript\nconst heroCards = await api.getCardsByArchetype('Elemental HERO');\n```\n\n#### `getCardsBySet(cardset: string, params?: Omit\u003cCardInfoParams, 'cardset'\u003e): Promise\u003cCardInfoResponse\u003e`\n\nGet all cards from a specific set.\n\n```typescript\nconst metalRaiders = await api.getCardsBySet('Metal Raiders');\n```\n\n### Utility Methods\n\n#### `getRandomCard(): Promise\u003cCard\u003e`\n\nGet a random card from the database.\n\n```typescript\nconst randomCard = await api.getRandomCard();\nconsole.log(`Random card: ${randomCard.name}`);\n```\n\n#### `getAllCardSets(): Promise\u003cCardSetInfo[]\u003e`\n\nGet information about all card sets.\n\n```typescript\nconst sets = await api.getAllCardSets();\nsets.forEach(set =\u003e {\n  console.log(`${set.set_name} (${set.set_code}) - ${set.num_of_cards} cards`);\n});\n```\n\n#### `getCardSetInfo(setcode: string): Promise\u003cCardSetDetails\u003e`\n\nGet detailed information about a specific card set.\n\n```typescript\nconst setInfo = await api.getCardSetInfo('LOB-001');\n```\n\n#### `getAllArchetypes(): Promise\u003cArchetype[]\u003e`\n\nGet all available archetypes.\n\n```typescript\nconst archetypes = await api.getAllArchetypes();\narchetypes.forEach(archetype =\u003e {\n  console.log(archetype.archetype_name);\n});\n```\n\n#### `checkDatabaseVersion(): Promise\u003cDatabaseVersion\u003e`\n\nCheck the current database version.\n\n```typescript\nconst version = await api.checkDatabaseVersion();\nconsole.log(`Database version: ${version.database_version}`);\nconsole.log(`Last update: ${version.last_update}`);\n```\n\n### Specialized Search Methods\n\n#### `getStapleCards(params?: Omit\u003cCardInfoParams, 'staple'\u003e): Promise\u003cCardInfoResponse\u003e`\n\nGet cards considered staples in the game.\n\n```typescript\nconst staples = await api.getStapleCards();\n```\n\n#### `getCardsByFormat(format: Format, params?: Omit\u003cCardInfoParams, 'format'\u003e): Promise\u003cCardInfoResponse\u003e`\n\nGet cards available in a specific format.\n\n```typescript\nconst tcgCards = await api.getCardsByFormat('tcg');\nconst speedDuelCards = await api.getCardsByFormat('speed duel');\nconst rushDuelCards = await api.getCardsByFormat('rush duel');\nconst masterDuelCards = await api.getCardsByFormat('master duel');\n```\n\n#### `getBanlistCards(banlist: BanlistType, params?: Omit\u003cCardInfoParams, 'banlist'\u003e): Promise\u003cCardInfoResponse\u003e`\n\nGet cards that appear on a specific banlist.\n\n```typescript\nconst tcgBanned = await api.getBanlistCards('TCG');\nconst ocgBanned = await api.getBanlistCards('OCG');\nconst goatBanned = await api.getBanlistCards('Goat');\n```\n\n#### `getCardsWithPagination(num: number, offset: number, params?: Omit\u003cCardInfoParams, 'num' | 'offset'\u003e): Promise\u003cCardInfoResponse\u003e`\n\nGet cards with pagination support.\n\n```typescript\nconst firstPage = await api.getCardsWithPagination(50, 0);\nconsole.log(`Total cards: ${firstPage.meta?.total_rows}`);\nconsole.log(`Pages remaining: ${firstPage.meta?.pages_remaining}`);\n```\n\n### Convenience Methods\n\n#### Type-specific Methods\n\n```typescript\n// Get cards by type\nconst spellCards = await api.getCardsByType('Spell Card');\nconst linkMonsters = await api.getCardsByType('Link Monster');\n\n// Get cards by attribute\nconst fireCards = await api.getCardsByAttribute('FIRE');\nconst lightCards = await api.getCardsByAttribute('LIGHT');\n\n// Get cards by race\nconst dragons = await api.getCardsByRace('Dragon');\nconst spellcasters = await api.getCardsByRace('Spellcaster');\n```\n\n#### Stat-based Methods\n\n```typescript\n// Get cards by level\nconst level4Cards = await api.getCardsByLevel(4);\nconst highLevelCards = await api.getCardsByLevel('gte8');\n\n// Get cards by ATK/DEF\nconst strongCards = await api.getCardsByATK('gte3000');\nconst defensiveCards = await api.getCardsByDEF('gte2500');\n```\n\n#### Advanced Search Methods\n\n```typescript\n// Get cards with misc info (views, formats, dates, etc.)\nconst cardsWithMisc = await api.getCardsWithMiscInfo({\n  archetype: 'Blue-Eyes'\n});\n```\n\n## Helper Functions\n\n### `buildComparison(operator: ComparisonOperator, value: number)`\n\nBuild comparison queries for ATK, DEF, or Level.\n\n```typescript\nimport { buildComparison } from 'ygoapi';\n\nconst strongCards = await api.getCardInfo({\n  atk: buildComparison('gte', 3000) // ATK \u003e= 3000\n});\n```\n\n### `getCardImages(card: Card)`\n\nExtract image URLs from a card object.\n\n```typescript\nimport { getCardImages } from 'ygoapi';\n\nconst card = await api.getCardByName('Dark Magician');\nif (card) {\n  const images = getCardImages(card);\n  console.log('Default image:', images.default.image_url);\n  console.log('Alternative images:', images.alternates);\n}\n```\n\n### Card Type Checkers\n\n```typescript\nimport { isMonsterCard, isSpellCard, isTrapCard, isExtraDeckMonster } from 'ygoapi';\n\nconst card = await api.getCardByName('Blue-Eyes White Dragon');\nif (card) {\n  console.log('Is monster:', isMonsterCard(card));\n  console.log('Is spell:', isSpellCard(card));\n  console.log('Is trap:', isTrapCard(card));\n  console.log('Is extra deck:', isExtraDeckMonster(card));\n}\n```\n\n## Advanced Search Features\n\n### Link Monsters\n\nSearch for Link monsters with specific criteria:\n\n```typescript\n// Get all Link monsters\nconst linkMonsters = await api.getCardInfo({\n  type: 'Link Monster'\n});\n\n// Get Link-2 monsters\nconst link2Monsters = await api.getCardInfo({\n  type: 'Link Monster',\n  link: 2\n});\n\n// Get Link monsters with specific markers\nconst topBottomLinks = await api.getCardInfo({\n  type: 'Link Monster',\n  linkmarker: ['Top', 'Bottom']\n});\n```\n\n### Pendulum Monsters\n\nSearch for Pendulum monsters by scale:\n\n```typescript\n// Get all Pendulum monsters\nconst pendulums = await api.getCardInfo({\n  type: 'Pendulum Effect Monster'\n});\n\n// Get Pendulum monsters with scale 7\nconst scale7Pendulums = await api.getCardInfo({\n  type: 'Pendulum Effect Monster',\n  scale: 7\n});\n```\n\n### Advanced Filtering\n\n```typescript\n// Cards with effects vs without effects\nconst effectCards = await api.getCardInfo({\n  has_effect: true,\n  type: 'Effect Monster'\n});\n\nconst vanillaCards = await api.getCardInfo({\n  has_effect: false\n});\n\n// Date-based filtering\nconst oldCards = await api.getCardInfo({\n  startdate: '2000-01-01',\n  enddate: '2005-12-31',\n  dateregion: 'tcg'\n});\n\n// Multiple card names/IDs\nconst specificCards = await api.getCardInfo({\n  name: 'Dark Magician|Blue-Eyes White Dragon'\n});\n\nconst cardsByIds = await api.getCardInfo({\n  id: '46986414,89631139'\n});\n```\n\n## Query Parameters\n\nThe `CardInfoParams` interface supports extensive filtering options:\n\n### Basic Filters\n\n```typescript\ninterface CardInfoParams {\n  // Card identification\n  name?: string;                    // Exact name match\n  fname?: string;                   // Fuzzy name search\n  id?: string | number;             // Card ID/passcode\n  konami_id?: string | number;      // Konami ID\n  \n  // Card properties\n  type?: CardType;                  // Card type\n  atk?: number | `${ComparisonOperator}${number}`;\n  def?: number | `${ComparisonOperator}${number}`;\n  level?: number | `${ComparisonOperator}${number}`;\n  race?: CardRace | CardRace[];     // Card race/type\n  attribute?: Attribute | Attribute[];\n  \n  // Link monsters\n  link?: number;                    // Link value\n  linkmarker?: LinkMarker | LinkMarker[];\n  \n  // Pendulum monsters\n  scale?: number;                   // Pendulum scale\n  \n  // Set/Archetype\n  cardset?: string;                 // Card set name\n  archetype?: string;               // Archetype name\n  \n  // Format/Banlist\n  banlist?: BanlistType;           // TCG, OCG, or Goat\n  format?: Format;                 // Game format\n  \n  // Special filters\n  staple?: \"yes\";                  // Staple cards only\n  has_effect?: boolean;            // Cards with/without effects\n  \n  // Date filters\n  startdate?: string;              // YYYY-MM-DD\n  enddate?: string;                // YYYY-MM-DD\n  dateregion?: DateRegion;         // tcg or ocg\n  \n  // Sorting/Pagination\n  sort?: SortOption;               // Sort order\n  num?: number;                    // Number of results\n  offset?: number;                 // Result offset\n  \n  // Additional options\n  misc?: \"yes\";                    // Include misc info\n  tcgplayer_data?: boolean;        // Use TCGPlayer data\n  \n  // Language\n  language?: Language;             // fr, de, it, pt\n}\n```\n\n### Comparison Operators\n\nFor ATK, DEF, and Level fields, you can use comparison operators:\n\n```typescript\n// Examples\natk: 2500,           // Exactly 2500\natk: \"lt2500\",       // Less than 2500\natk: \"lte2500\",      // Less than or equal to 2500\natk: \"gt2500\",       // Greater than 2500\natk: \"gte2500\",      // Greater than or equal to 2500\n```\n\n### Array Parameters\n\nMany parameters accept arrays for multiple values:\n\n```typescript\nconst cards = await api.getCardInfo({\n  attribute: ['LIGHT', 'DARK'],     // LIGHT OR DARK\n  race: ['Dragon', 'Spellcaster'],  // Dragon OR Spellcaster\n  linkmarker: ['Top', 'Bottom']     // Top AND Bottom link markers\n});\n```\n\n## Multi-language Support\n\nThe API supports multiple languages:\n\n```typescript\n// Get cards in French\nconst frenchCards = await api.getCardInfo({\n  fname: 'Magicien',\n  language: 'fr'\n});\n\n// Get German Blue-Eyes archetype\nconst germanBlueEyes = await api.getCardsByArchetype('Blue-Eyes', {\n  language: 'de'\n});\n```\n\n**Supported languages:**\n- `fr` - French\n- `de` - German\n- `it` - Italian\n- `pt` - Portuguese\n\n**Note:** Card images are only available in English.\n\n## Types Reference\n\n### Card Types\n\nAll card types from the game are supported:\n\n```typescript\ntype CardType = \n  | \"Effect Monster\"\n  | \"Normal Monster\"\n  | \"Fusion Monster\"\n  | \"Synchro Monster\"\n  | \"XYZ Monster\"\n  | \"Link Monster\"\n  | \"Spell Card\"\n  | \"Trap Card\"\n  // ... and many more\n```\n\n### Attributes\n\n```typescript\ntype Attribute = \n  | \"DARK\"\n  | \"LIGHT\"\n  | \"EARTH\"\n  | \"FIRE\"\n  | \"WATER\"\n  | \"WIND\"\n  | \"DIVINE\"\n```\n\n### Races\n\nMonster races:\n\n```typescript\ntype MonsterRace = \n  | \"Dragon\"\n  | \"Spellcaster\"\n  | \"Warrior\"\n  | \"Beast-Warrior\"\n  | \"Fiend\"\n  // ... and more\n```\n\nSpell races:\n\n```typescript\ntype SpellRace = \n  | \"Normal\"\n  | \"Quick-Play\"\n  | \"Field\"\n  | \"Equip\"\n  | \"Continuous\"\n  | \"Ritual\"\n```\n\nTrap races:\n\n```typescript\ntype TrapRace = \n  | \"Normal\"\n  | \"Continuous\"\n  | \"Counter\"\n```\n\n## Error Handling\n\nThe client includes comprehensive error handling:\n\n```typescript\nimport { YgoApiError } from 'ygoapi';\n\ntry {\n  const card = await api.getCardByName('Invalid Card Name');\n} catch (error) {\n  if (error instanceof YgoApiError) {\n    console.log(`API Error ${error.statusCode}: ${error.message}`);\n  } else {\n    console.log('Network or other error:', error);\n  }\n}\n```\n\n## Rate Limiting\n\nThe YGOPRODeck API has rate limiting:\n\n- **20 requests per second**\n- Exceeding this limit results in a **1-hour IP ban**\n- Cache responses locally to minimize API calls\n\nThe **YgoApi client** automatically throttles requests to comply with the 20 requests/second limit. It uses a built-in queue system (`SignalBasedQueue`) that spaces out requests to avoid bans. Throttling behavior can be customized by providing a different interval or your own queue implementation via the `requestQueue` option and the `TimeQueue` interface.\n\nThe default signal-based queue allows up to 20 requests to *start* per second, but does not wait for their completion before processing the next requests.\n\n### Custom Throttling\n\n```typescript\nimport { YgoApi, SignalBasedQueue } from 'ygoapi'\nimport type { TimeQueue } from 'ygoapi'\n\nconst api = new YgoApi({\n  requestQueue: new SignalBasedQueue(1000) // 1000ms throttling between fetch starts\n})\n\nclass MyQueue implements TimeQueue {\n  // The method needs to pass down the resolution of the task\n  enqueue\u003cT\u003e(task: () =\u003e Promise\u003cT\u003e, signal: AbortSignal): Promise\u003cT\u003e {\n    //... Your implementation ...\n  }\n  // ... Your implementation ...\n}\n\nconst myApi = new YgoApi({\n  requestQueue: new MyQueue()\n})\n```\n\n## Testing\n\nThis library includes comprehensive test coverage with 53+ tests covering all features:\n\n```bash\n# Run tests\nbun test\n\n# Run tests with coverage\nbun test --coverage\n```\n\nTest categories:\n- ✅ Core API endpoints (6 endpoints)\n- ✅ Advanced search parameters (25+ parameters)\n- ✅ Link monsters and Pendulum cards\n- ✅ Banlist and format filtering\n- ✅ Multi-language support (5 languages)\n- ✅ Error handling and edge cases\n- ✅ Helper functions and utilities\n- ✅ Convenience methods\n- ✅ Real API integration tests\n\n## Best Practices\n\n1. **Cache responses locally** - Don't make repeated calls for the same data\n2. **Respect rate limits** - Max 20 requests per second\n3. **Download images locally** - Don't hotlink card images\n4. **Use specific queries** - Filter results to reduce bandwidth\n5. **Handle errors gracefully** - Always wrap API calls in try-catch blocks\n6. **Use pagination** - For large result sets, use `num` and `offset` parameters\n7. **Leverage TypeScript** - Take advantage of full type safety and IntelliSense\n\n## Example Applications\n\n### Card Search Application\n\n```typescript\nimport { YgoApi, isMonsterCard, isSpellCard, isTrapCard } from 'ygoapi';\n\nconst api = new YgoApi();\n\nasync function searchCards(query: string) {\n  try {\n    const response = await api.searchCards(query, {\n      sort: 'name',\n      num: 20\n    });\n    \n    return response.data.map(card =\u003e ({\n      id: card.id,\n      name: card.name,\n      type: card.type,\n      atk: card.atk,\n      def: card.def,\n      image: card.card_images[0]?.image_url_small\n    }));\n  } catch (error) {\n    console.error('Search failed:', error);\n    return [];\n  }\n}\n```\n\n### Deck Builder Helper\n\n```typescript\nimport { YgoApi, isMonsterCard, isSpellCard, isTrapCard } from 'ygoapi';\n\nconst api = new YgoApi();\n\nasync function getArchetypeCards(archetype: string) {\n  const response = await api.getCardsByArchetype(archetype);\n  \n  const monsters = response.data.filter(isMonsterCard);\n  const spells = response.data.filter(isSpellCard);\n  const traps = response.data.filter(isTrapCard);\n  \n  return { monsters, spells, traps };\n}\n```\n\n### Price Tracker\n\n```typescript\nimport { YgoApi } from 'ygoapi';\n\nconst api = new YgoApi();\n\nasync function getCardPrices(cardName: string) {\n  const card = await api.getCardByName(cardName);\n  if (!card) return null;\n  \n  const prices = card.card_prices[0];\n  return {\n    tcgplayer: parseFloat(prices.tcgplayer_price),\n    cardmarket: parseFloat(prices.cardmarket_price),\n    ebay: parseFloat(prices.ebay_price),\n    amazon: parseFloat(prices.amazon_price),\n    coolstuffinc: parseFloat(prices.coolstuffinc_price)\n  };\n}\n```\n\n## Contributing\n\nPlease see [CONTRIBUTING.md](./CONTRIBUTING.md) for contribution guidelines.\n\n## License\n\nThis project is licensed under the MIT License.\n\n## API Coverage Summary\n\nThis library provides **100% coverage** of the YGOPRODeck API:\n\n### ✅ All Endpoints (6/6)\n- Card Information (`/cardinfo.php`)\n- Random Card (`/randomcard.php`)\n- Card Sets (`/cardsets.php`) \n- Card Set Information (`/cardsetsinfo.php`)\n- Archetypes (`/archetypes.php`)\n- Database Version (`/checkDBVer.php`)\n\n### ✅ All Search Parameters (25+)\n- Card identification: `name`, `fname`, `id`, `konami_id`\n- Card properties: `type`, `atk`, `def`, `level`, `race`, `attribute`\n- Link monsters: `link`, `linkmarker`\n- Pendulum cards: `scale`\n- Filtering: `cardset`, `archetype`, `banlist`, `format`, `staple`, `has_effect`\n- Date filtering: `startdate`, `enddate`, `dateregion`\n- Sorting \u0026 pagination: `sort`, `num`, `offset`\n- Advanced: `misc`, `tcgplayer_data`, `language`\n\n### ✅ All Features\n- Comparison operators (`lt`, `lte`, `gt`, `gte`)\n- Multiple values (pipe-separated names, comma-separated IDs)\n- Multi-language support (English, French, German, Italian, Portuguese)\n- Complete TypeScript definitions\n- Parameter validation\n- Error handling\n- Helper functions\n- Convenience methods\n\n## Changelog\n\n### v1.0.0\n- ✅ Complete YGOPRODeck API implementation\n- ✅ Full TypeScript support with 30+ type definitions\n- ✅ 53+ comprehensive tests with 200+ assertions\n- ✅ All 6 API endpoints supported\n- ✅ All 25+ search parameters supported\n- ✅ Multi-language support (5 languages)\n- ✅ Advanced features (Link monsters, Pendulum cards, banlist filtering)\n- ✅ Helper functions and convenience methods\n- ✅ Parameter validation and error handling\n- ✅ Zero external dependencies\n\n## Important Usage Notes\n\n⚠️ **Images**: Do not continually hotlink images directly from this site. Please download and re-host the images yourself. Failure to do so will result in an IP blacklist. Please read the guide on where to download images.\n\n💾 **Data Caching**: Please download and store all data pulled from this API locally to keep the amount of API calls used to a minimum. Failure to do so may result in either your IP address being blacklisted or the API being rolled back.\n\n⏱️ **Rate Limiting**: Rate limiting is enabled on the API. The rate limit is 20 requests per 1 second. If you exceed this, you are blocked from accessing the API for 1 hour.\n\n## Disclaimer\n\nThis library is not affiliated with YGOPRODeck, Konami, or Yu-Gi-Oh!. Yu-Gi-Oh! is a trademark of Konami Digital Entertainment, Inc.\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftimmikeladze%2Fygoapi","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftimmikeladze%2Fygoapi","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftimmikeladze%2Fygoapi/lists"}