TypeScript SDK
The official TypeScript SDK provides a fully typed interface to the Pulls API with built-in error handling and Effect integration.
Installation
Section titled “Installation”npm install @pulls/sdkbun add @pulls/sdkpnpm add @pulls/sdkQuick Start
Section titled “Quick Start”import { PullsAPI } from '@pulls/sdk'
const pulls = new PullsAPI({ apiKey: process.env.PULLS_API_KEY!})
// Get a cardconst card = await pulls.cards.get('sv7-001')console.log(card.name) // "Bulbasaur"
// Search cardsconst results = await pulls.search.cards({ query: 'Charizard', tcg: 'pokemon'})
// List pricesconst prices = await pulls.prices.list({ cardIds: ['sv7-001', 'sv7-002']})Configuration
Section titled “Configuration”const pulls = new PullsAPI({ // Required apiKey: 'pk_live_...',
// Optional baseUrl: 'https://api.pulls.app', // Default timeout: 30_000, // 30s default retries: 3, // Auto-retry on 5xx/network errors})Error Handling
Section titled “Error Handling”The SDK exports typed error classes matching API errors:
import { PullsAPI, CardNotFoundError, RateLimitExceededError, UnauthorizedError} from '@pulls/sdk'
try { const card = await pulls.cards.get('invalid-id')} catch (error) { if (error instanceof CardNotFoundError) { console.log(`Card ${error.cardId} not found`) } else if (error instanceof RateLimitExceededError) { console.log(`Retry after ${error.retryAfter} seconds`) } else if (error instanceof UnauthorizedError) { console.log('Invalid API key') }}Effect Integration
Section titled “Effect Integration”The SDK provides first-class Effect support:
import { PullsEffect } from '@pulls/sdk/effect'import { Effect, Layer } from 'effect'
// Create service layerconst PullsLayer = PullsEffect.layer({ apiKey: process.env.PULLS_API_KEY!})
// Use in Effect programsconst program = Effect.gen(function* () { const pulls = yield* PullsEffect
const card = yield* pulls.cards.get('sv7-001') const prices = yield* pulls.prices.list({ cardIds: [card.id] })
return { card, prices }})
// Run with provided layerconst result = await Effect.runPromise( program.pipe(Effect.provide(PullsLayer)))Error handling with Effect
Section titled “Error handling with Effect”import { CardNotFoundError } from '@pulls/sdk'
const program = pulls.cards.get('maybe-invalid').pipe( Effect.catchTag('CardNotFoundError', (error) => Effect.succeed({ notFound: true, cardId: error.cardId }) ), Effect.catchTag('RateLimitExceededError', (error) => Effect.sleep(error.retryAfter * 1000).pipe( Effect.andThen(() => pulls.cards.get('maybe-invalid')) ) ))API Reference
Section titled “API Reference”// Get card by IDpulls.cards.get(id: string, options?: { includePrices?: boolean}): Promise<Card>
// List cards with paginationpulls.cards.list(options?: { tcg?: Tcg setId?: string limit?: number cursor?: string}): Promise<Paginated<Card>>
// Search cardspulls.search.cards(options: { query: string tcg?: Tcg limit?: number}): Promise<Card[]>Prices
Section titled “Prices”// Get current pricespulls.prices.list(options: { cardIds?: string[] printingIds?: string[] tcg?: Tcg}): Promise<Price[]>
// Get price historypulls.prices.history(printingId: string, options?: { days?: number}): Promise<PriceHistory[]>
// Get graded pricespulls.prices.graded(cardId: string): Promise<GradedPrices>// List setspulls.sets.list(options?: { tcg?: Tcg}): Promise<Set[]>
// Get set by IDpulls.sets.get(id: string): Promise<Set>type Tcg = 'pokemon' | 'mtg' | 'yugioh' | 'lorcana' | 'onepiece'
interface Card { id: string name: string tcg: Tcg set: { id: string; name: string } number?: string rarity?: string imageUrl?: string prices?: { market: number low: number high: number }}
interface Price { cardId: string printingId: string market: number low: number high: number change24h: number updatedAt: string}
interface GradedPrices { cardId: string psa: Record<string, number> // grade -> price cgc: Record<string, number> bgs: Record<string, number>}Source Code
Section titled “Source Code”The SDK is open source: github.com/pulls-app/sdk-typescript
Issues and PRs welcome.