openapi: 3.1.0
info:
  title: TCG Pricing API
  description: |
    Universal trading card game pricing API covering 89+ games from TCGPlayer.
    Get real-time prices, historical data, and search across Pokemon, Magic: The Gathering,
    Yu-Gi-Oh!, One Piece, Lorcana, and many more.
  version: 1.0.0
  contact:
    url: https://tcgapi.dev
  license:
    name: Proprietary
    url: https://tcgapi.dev/terms

servers:
  - url: https://api.tcgapi.dev/v1
    description: Production

security:
  - ApiKeyAuth: []

components:
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: X-API-Key
      description: |
        API key for authentication. Get one free at https://tcgapi.dev/dashboard.
        Format: `tcg_live_xxxxxxxx`

  schemas:
    Game:
      type: object
      properties:
        id:
          type: integer
          description: Internal game ID (matches TCGPlayer categoryId)
        name:
          type: string
          example: Pokemon
        slug:
          type: string
          example: pokemon
        tcgplayer_id:
          type: integer
          description: TCGPlayer category ID
        priority:
          type: integer
          description: "Data refresh priority (3=daily, 2=every 3 days)"
        set_count:
          type: integer
        card_count:
          type: integer
        image_url:
          type: string
          format: uri
          nullable: true
          description: TCGPlayer CDN image URL for the game's highest-value card
        logo_url:
          type: string
          format: uri
          nullable: true
          description: Self-hosted game logo image URL (400x200 transparent PNG, available for major games)
        last_synced_at:
          type: string
          format: date-time
          nullable: true

    Set:
      type: object
      properties:
        id:
          type: integer
        name:
          type: string
          example: "Scarlet & Violet—Surging Sparks"
        slug:
          type: string
        tcgplayer_id:
          type: integer
        abbreviation:
          type: string
          nullable: true
        release_date:
          type: string
          format: date
          nullable: true
        card_count:
          type: integer
        image_url:
          type: string
          format: uri
          nullable: true
          description: TCGPlayer CDN image URL for the set's highest-value card
        set_icon_url:
          type: string
          format: uri
          nullable: true
          description: TCGPlayer set icon/logo image
        game_name:
          type: string
        game_slug:
          type: string

    Card:
      type: object
      properties:
        id:
          type: integer
        name:
          type: string
          example: "Charizard ex"
        clean_name:
          type: string
        number:
          type: string
          nullable: true
        rarity:
          type: string
          nullable: true
        image_url:
          type: string
          format: uri
          nullable: true
        tcgplayer_id:
          type: integer
        tcgplayer_url:
          type: string
          nullable: true
        product_type:
          type: string
          nullable: true
        game_name:
          type: string
        game_slug:
          type: string
        set_name:
          type: string
        set_slug:
          type: string

    Price:
      type: object
      properties:
        card_id:
          type: integer
        market_price:
          type: number
          format: float
          nullable: true
          example: 24.99
        low_price:
          type: number
          format: float
          nullable: true
        foil_market_price:
          type: number
          format: float
          nullable: true
        price_change_24h:
          type: number
          format: float
          nullable: true
          description: "Percentage change over 24 hours"
        price_change_7d:
          type: number
          format: float
          nullable: true
        price_change_30d:
          type: number
          format: float
          nullable: true
        last_updated_at:
          type: string
          format: date-time

    PriceHistory:
      type: object
      properties:
        date:
          type: string
          format: date
        market_price:
          type: number
          format: float
          nullable: true
        low_price:
          type: number
          format: float
          nullable: true
        foil_market_price:
          type: number
          format: float
          nullable: true

    Meta:
      type: object
      properties:
        total:
          type: integer
        page:
          type: integer
        per_page:
          type: integer
        has_more:
          type: boolean

    RateLimit:
      type: object
      properties:
        daily_limit:
          type: integer
        daily_remaining:
          type: integer
        daily_reset:
          type: string
          format: date-time

    Error:
      type: object
      properties:
        error:
          type: object
          properties:
            message:
              type: string
            code:
              type: string

  parameters:
    PageParam:
      name: page
      in: query
      schema:
        type: integer
        default: 1
    PerPageParam:
      name: per_page
      in: query
      schema:
        type: integer
        default: 50
        maximum: 200

paths:
  /games:
    get:
      summary: List all games
      description: Returns all 89+ supported trading card games. No authentication required.
      tags: [Games]
      security: []
      parameters:
        - $ref: '#/components/parameters/PageParam'
        - $ref: '#/components/parameters/PerPageParam'
      responses:
        '200':
          description: List of games
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/Game'
                  meta:
                    $ref: '#/components/schemas/Meta'

  /games/{slug}:
    get:
      summary: Get game details
      description: Get details for a specific game by slug or TCGPlayer category ID. No authentication required.
      tags: [Games]
      security: []
      parameters:
        - name: slug
          in: path
          required: true
          schema:
            type: string
          description: Game slug (e.g., "pokemon") or TCGPlayer category ID
      responses:
        '200':
          description: Game details
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: '#/components/schemas/Game'
        '404':
          description: Game not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

  /games/{slug}/sets:
    get:
      summary: List sets for a game
      description: Returns all sets/expansions for a specific game. No authentication required.
      tags: [Games]
      security: []
      parameters:
        - name: slug
          in: path
          required: true
          schema:
            type: string
        - $ref: '#/components/parameters/PageParam'
        - $ref: '#/components/parameters/PerPageParam'
      responses:
        '200':
          description: List of sets
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/Set'
                  meta:
                    $ref: '#/components/schemas/Meta'

  /sets:
    get:
      summary: List all sets
      description: Returns sets across all games. Filter by game slug.
      tags: [Sets]
      parameters:
        - name: game
          in: query
          schema:
            type: string
          description: Filter by game slug
        - $ref: '#/components/parameters/PageParam'
        - $ref: '#/components/parameters/PerPageParam'
      responses:
        '200':
          description: List of sets

  /sets/{id}:
    get:
      summary: Get set details
      tags: [Sets]
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: Set details

  /sets/{id}/cards:
    get:
      summary: List cards in a set
      description: Returns all cards in a set with current prices.
      tags: [Sets]
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: integer
        - $ref: '#/components/parameters/PageParam'
        - $ref: '#/components/parameters/PerPageParam'
      responses:
        '200':
          description: List of cards with prices

  /sets/{id}/prices:
    get:
      summary: Get all prices for a set
      description: Returns complete pricing data for every card in a set. Pro tier required.
      tags: [Sets]
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: All prices for the set
        '403':
          description: Pro or Business tier required

  /cards/{id}:
    get:
      summary: Get card details
      tags: [Cards]
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: Card details
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: '#/components/schemas/Card'

  /cards/{id}/prices:
    get:
      summary: Get card prices
      tags: [Cards]
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: Current price data
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: '#/components/schemas/Price'

  /cards/tcgplayer/{tcgplayerId}:
    get:
      summary: Lookup card by TCGPlayer ID
      description: Find a card using its TCGPlayer product ID. Useful for migrating from TCGPlayer's API.
      tags: [Cards]
      parameters:
        - name: tcgplayerId
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: Card with price data

  /cards/{id}/history:
    get:
      summary: Get price history
      description: Returns historical price data for a card. Pro tier required.
      tags: [Cards]
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: integer
        - name: range
          in: query
          schema:
            type: string
            enum: [month, quarter, year]
            default: month
      responses:
        '200':
          description: Price history array
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/PriceHistory'
        '403':
          description: Pro tier required

  /cards/{id}/history/detailed:
    get:
      summary: Get full price history
      description: Returns complete price history (all dates). Business tier required.
      tags: [Cards]
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: Complete price history

  /search:
    get:
      summary: Search cards
      description: Search for cards by name across all games. Supports filtering by game, set, rarity, and price range.
      tags: [Search]
      parameters:
        - name: q
          in: query
          required: true
          schema:
            type: string
            minLength: 2
          description: Search query (min 2 characters)
        - name: game
          in: query
          schema:
            type: string
          description: Filter by game slug
        - name: set_id
          in: query
          schema:
            type: integer
        - name: rarity
          in: query
          schema:
            type: string
        - name: min_price
          in: query
          schema:
            type: number
        - name: max_price
          in: query
          schema:
            type: number
        - name: sort
          in: query
          schema:
            type: string
            enum: [relevance, price_asc, price_desc, name]
            default: relevance
        - $ref: '#/components/parameters/PageParam'
        - $ref: '#/components/parameters/PerPageParam'
      responses:
        '200':
          description: Search results

  /prices/top-movers:
    get:
      summary: Top price movers
      description: Cards with the biggest price changes. Filter by game, direction, and time period.
      tags: [Prices]
      parameters:
        - name: game
          in: query
          schema:
            type: string
        - name: direction
          in: query
          schema:
            type: string
            enum: [up, down]
            default: up
        - name: period
          in: query
          schema:
            type: string
            enum: [24h, 7d, 30d]
            default: 24h
        - name: limit
          in: query
          schema:
            type: integer
            default: 20
            maximum: 50
      responses:
        '200':
          description: Top movers list

  /bulk/prices:
    get:
      summary: Bulk price lookup
      description: Get prices for up to 500 cards at once. Pro tier required.
      tags: [Bulk]
      parameters:
        - name: ids
          in: query
          required: true
          schema:
            type: string
          description: Comma-separated card IDs (max 500)
      responses:
        '200':
          description: Prices for requested cards
        '403':
          description: Pro tier required

  /bulk/cards:
    get:
      summary: Bulk card lookup
      description: Get card details for up to 100 cards at once. Pro tier required.
      tags: [Bulk]
      parameters:
        - name: ids
          in: query
          required: true
          schema:
            type: string
          description: Comma-separated card IDs (max 100)
      responses:
        '200':
          description: Card details for requested IDs
        '403':
          description: Pro tier required

  /bulk/history:
    get:
      summary: Bulk price history
      description: Get price history for up to 50 cards at once. Business tier required.
      tags: [Bulk]
      parameters:
        - name: ids
          in: query
          required: true
          schema:
            type: string
          description: Comma-separated card IDs (max 50)
        - name: range
          in: query
          schema:
            type: string
            enum: [month, quarter, year]
            default: month
      responses:
        '200':
          description: Price history grouped by card ID
        '403':
          description: Business tier required

  /export/set/{id}:
    get:
      summary: Export set data
      description: Download all cards and prices for a set as JSON or CSV. Business tier required.
      tags: [Export]
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: integer
        - name: format
          in: query
          schema:
            type: string
            enum: [json, csv]
            default: json
      responses:
        '200':
          description: Set data export
        '403':
          description: Business tier required

  /auth/register:
    post:
      summary: Create account
      tags: [Auth]
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [email, password]
              properties:
                email:
                  type: string
                  format: email
                password:
                  type: string
                  minLength: 8
      responses:
        '201':
          description: Account created
        '409':
          description: Email already registered

  /auth/login:
    post:
      summary: Login
      tags: [Auth]
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [email, password]
              properties:
                email:
                  type: string
                password:
                  type: string
      responses:
        '200':
          description: Login successful
        '401':
          description: Invalid credentials

  /auth/logout:
    post:
      summary: Logout
      tags: [Auth]
      security: []
      responses:
        '200':
          description: Logged out

  /auth/me:
    get:
      summary: Get current user
      tags: [Auth]
      security: []
      responses:
        '200':
          description: Current user info
        '401':
          description: Not authenticated

  /keys:
    get:
      summary: List API keys
      tags: [Keys]
      security: []
      responses:
        '200':
          description: List of API keys
    post:
      summary: Create API key
      tags: [Keys]
      security: []
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                  default: Default
      responses:
        '201':
          description: API key created (key shown only once)

  /keys/{id}:
    delete:
      summary: Deactivate API key
      tags: [Keys]
      security: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Key deactivated

  /usage:
    get:
      summary: Get usage stats
      tags: [Usage]
      security: []
      responses:
        '200':
          description: Usage statistics for all API keys

  /stripe/create-checkout:
    post:
      summary: Start checkout
      tags: [Billing]
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [plan]
              properties:
                plan:
                  type: string
                  enum: [pro, business]
      responses:
        '200':
          description: Checkout URL

  /stripe/portal:
    post:
      summary: Customer portal
      tags: [Billing]
      security: []
      responses:
        '200':
          description: Portal URL

tags:
  - name: Games
    description: Browse available trading card games
  - name: Sets
    description: Card sets and expansions
  - name: Cards
    description: Individual card data and pricing
  - name: Prices
    description: Price analytics and movers
  - name: Search
    description: Search across all cards
  - name: Bulk
    description: Batch operations (Pro+)
  - name: Export
    description: Data exports (Business)
  - name: Auth
    description: Account management
  - name: Keys
    description: API key management
  - name: Usage
    description: Usage and rate limit info
  - name: Billing
    description: Subscription management
