Skip to content

TypeScript SDK

The official TypeScript SDK provides a fully typed interface to the Pulls API with built-in error handling and Effect integration.

Terminal window
npm install @pulls/sdk
import { PullsAPI } from '@pulls/sdk'
const pulls = new PullsAPI({
apiKey: process.env.PULLS_API_KEY!
})
// Get a card
const card = await pulls.cards.get('sv7-001')
console.log(card.name) // "Bulbasaur"
// Search cards
const results = await pulls.search.cards({
query: 'Charizard',
tcg: 'pokemon'
})
// List prices
const prices = await pulls.prices.list({
cardIds: ['sv7-001', 'sv7-002']
})
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
})

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')
}
}

The SDK provides first-class Effect support:

import { PullsEffect } from '@pulls/sdk/effect'
import { Effect, Layer } from 'effect'
// Create service layer
const PullsLayer = PullsEffect.layer({
apiKey: process.env.PULLS_API_KEY!
})
// Use in Effect programs
const 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 layer
const result = await Effect.runPromise(
program.pipe(Effect.provide(PullsLayer))
)
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'))
)
)
)
// Get card by ID
pulls.cards.get(id: string, options?: {
includePrices?: boolean
}): Promise<Card>
// List cards with pagination
pulls.cards.list(options?: {
tcg?: Tcg
setId?: string
limit?: number
cursor?: string
}): Promise<Paginated<Card>>
// Search cards
pulls.search.cards(options: {
query: string
tcg?: Tcg
limit?: number
}): Promise<Card[]>
// Get current prices
pulls.prices.list(options: {
cardIds?: string[]
printingIds?: string[]
tcg?: Tcg
}): Promise<Price[]>
// Get price history
pulls.prices.history(printingId: string, options?: {
days?: number
}): Promise<PriceHistory[]>
// Get graded prices
pulls.prices.graded(cardId: string): Promise<GradedPrices>
// List sets
pulls.sets.list(options?: {
tcg?: Tcg
}): Promise<Set[]>
// Get set by ID
pulls.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>
}

The SDK is open source: github.com/pulls-app/sdk-typescript

Issues and PRs welcome.