{
  "openapi": "3.0.3",
  "info": {
    "title": "autopricer.tf Public REST API",
    "description": "Read-only Team Fortress 2 item pricing API. No API key. This specification covers only public GET endpoints that do not modify database records and are not dashboard UI feeds.\n\nHuman docs: https://autopricer.tf/docs\nMarkdown: https://autopricer.tf/docs.md\nAI index: https://autopricer.tf/llms.txt",
    "version": "1.0.0",
    "contact": {
      "url": "https://autopricer.tf/docs"
    }
  },
  "servers": [
    {
      "url": "https://autopricer.tf",
      "description": "Production"
    }
  ],
  "externalDocs": {
    "description": "Public REST API documentation (Markdown)",
    "url": "https://autopricer.tf/docs.md"
  },
  "tags": [
    {
      "name": "Discovery",
      "description": "Find the public documentation and endpoint index"
    },
    {
      "name": "Health",
      "description": "Service liveness"
    },
    {
      "name": "Items",
      "description": "Tracked TF2 item catalog and prices"
    },
    {
      "name": "Images",
      "description": "Cached item icon URLs"
    }
  ],
  "paths": {
    "/api": {
      "get": {
        "tags": ["Discovery"],
        "summary": "API discovery document",
        "description": "Returns links to documentation and the public endpoint index. No authentication.",
        "operationId": "getApiDiscovery",
        "responses": {
          "200": {
            "description": "Discovery document",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": ["success", "name", "documentation", "endpoints"],
                  "properties": {
                    "success": { "type": "boolean", "example": true },
                    "name": { "type": "string", "example": "autopricer.tf Public REST API" },
                    "description": { "type": "string" },
                    "documentation": {
                      "type": "object",
                      "properties": {
                        "html": { "type": "string", "format": "uri", "example": "https://autopricer.tf/docs" },
                        "markdown": { "type": "string", "format": "uri", "example": "https://autopricer.tf/docs.md" },
                        "openapi": { "type": "string", "format": "uri", "example": "https://autopricer.tf/openapi.json" },
                        "llms": { "type": "string", "format": "uri", "example": "https://autopricer.tf/llms.txt" }
                      }
                    },
                    "rate_limit": {
                      "type": "object",
                      "properties": {
                        "requests_per_minute_per_ip": { "type": "integer", "example": 300 }
                      }
                    },
                    "endpoints": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "method": { "type": "string", "example": "GET" },
                          "path": { "type": "string", "example": "/api/health" },
                          "description": { "type": "string" }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/api/health": {
      "get": {
        "tags": ["Health"],
        "summary": "Liveness check",
        "description": "HTTP 200 when the service is online and the live listing feed is connected. HTTP 503 when the live feed is disconnected.",
        "operationId": "getHealth",
        "responses": {
          "200": {
            "description": "Online",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/HealthOnline" },
                "example": { "success": true, "status": "Online" }
              }
            }
          },
          "503": {
            "description": "Live listing feed disconnected",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/HealthOffline" },
                "example": { "success": false, "status": "Offline — WS disconnected" }
              }
            }
          },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/api/items/count": {
      "get": {
        "tags": ["Items"],
        "summary": "Total tracked item count",
        "operationId": "getItemsCount",
        "responses": {
          "200": {
            "description": "Count",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ItemsCount" },
                "example": { "success": true, "count": 86546 }
              }
            }
          },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/api/items/{page}": {
      "get": {
        "tags": ["Items"],
        "summary": "Paginated item catalog",
        "description": "Returns up to 1000 items per page, ordered by createdAt ascending. Page numbers start at 1. Item price fields are JSON strings. Optional raw key/ref breakdown and listing snapshots can be attached.",
        "operationId": "getItemsPage",
        "parameters": [
          {
            "name": "page",
            "in": "path",
            "required": true,
            "schema": { "type": "integer", "minimum": 1, "example": 1 },
            "description": "Page number, starting at 1. Each page has at most 1000 items."
          },
          {
            "name": "raw",
            "in": "query",
            "required": false,
            "schema": { "type": "string", "enum": ["true", "1", "yes", "on"] },
            "description": "Include raw key/ref price breakdown fields."
          },
          {
            "name": "listings",
            "in": "query",
            "required": false,
            "schema": { "type": "string", "enum": ["true", "1", "yes", "on"] },
            "description": "Attach the latest buy/sell listing snapshot for each item."
          }
        ],
        "responses": {
          "200": {
            "description": "Page of items",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ItemsPage" }
              }
            }
          },
          "400": {
            "description": "Invalid page",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Error" },
                "example": { "success": false, "error": "Invalid page" }
              }
            }
          },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/api/images": {
      "get": {
        "tags": ["Images"],
        "summary": "Recent item images",
        "description": "Up to 500 items that have a cached image URL, most recently updated first. Not a complete catalog.",
        "operationId": "getImages",
        "responses": {
          "200": {
            "description": "Image list",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Images" }
              }
            }
          },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    }
  },
  "components": {
    "responses": {
      "RateLimited": {
        "description": "More than 300 requests per minute from this IP",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/Error" },
            "example": {
              "success": false,
              "error": "Too many requests — please slow down."
            }
          }
        }
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "required": ["success", "error"],
        "properties": {
          "success": { "type": "boolean", "example": false },
          "error": { "type": "string" }
        }
      },
      "HealthOnline": {
        "type": "object",
        "required": ["success", "status"],
        "properties": {
          "success": { "type": "boolean", "example": true },
          "status": { "type": "string", "example": "Online" }
        }
      },
      "HealthOffline": {
        "type": "object",
        "required": ["success", "status"],
        "properties": {
          "success": { "type": "boolean", "example": false },
          "status": { "type": "string", "example": "Offline — WS disconnected" }
        }
      },
      "ItemsCount": {
        "type": "object",
        "required": ["success", "count"],
        "properties": {
          "success": { "type": "boolean", "example": true },
          "count": { "type": "integer", "example": 86546 }
        }
      },
      "Images": {
        "type": "object",
        "required": ["success", "images"],
        "properties": {
          "success": { "type": "boolean", "example": true },
          "images": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/ImageRow" }
          }
        }
      },
      "ImageRow": {
        "type": "object",
        "required": ["name", "imageUrl"],
        "properties": {
          "name": { "type": "string", "example": "Jellyfish Jam Throttlehead" },
          "imageUrl": {
            "type": "string",
            "format": "uri",
            "example": "https://steamcdn-a.akamaihd.net/apps/440/icons/sum22_throttlehead.ccf997318471e47b40a4e8c3396a5aea48a8a884.png"
          }
        }
      },
      "ItemsPage": {
        "type": "object",
        "required": ["success", "items"],
        "properties": {
          "success": { "type": "boolean", "example": true },
          "items": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/Item" }
          }
        }
      },
      "Item": {
        "type": "object",
        "required": ["name"],
        "properties": {
          "name": { "type": "string", "example": "Strange Ullapool Caber" },
          "sku": { "type": "string", "nullable": true, "example": "307;11" },
          "imageUrl": { "type": "string", "nullable": true },
          "price": { "type": "string", "nullable": true, "example": "13.2419", "description": "Keys, JSON string" },
          "stablePrice": { "type": "string", "nullable": true, "example": "13.4232" },
          "buyOrder": { "type": "string", "nullable": true, "example": "12.8423" },
          "sellOrder": { "type": "string", "nullable": true, "example": "13.3327" },
          "stableBuyOrderPrice": { "type": "string", "nullable": true },
          "stableSellOrderPrice": { "type": "string", "nullable": true },
          "verified": { "type": "boolean" },
          "accurate": { "type": "boolean" },
          "suspicious": { "type": "boolean" },
          "stn": { "type": "boolean" },
          "new": { "type": "boolean" },
          "type": { "type": "string", "enum": ["free", "fixed", "manual"] },
          "updatedAt": { "type": "string", "description": "Unix milliseconds as a string. \"0\" means queued for pricing." },
          "createdAt": { "type": "string", "format": "date-time" },
          "price1dAgo": { "type": "string", "nullable": true },
          "price7dAgo": { "type": "string", "nullable": true },
          "price30dAgo": { "type": "string", "nullable": true },
          "raw_price_key": { "type": "string", "nullable": true, "description": "Present when raw=true" },
          "raw_price_ref": { "type": "string", "nullable": true, "description": "Present when raw=true" },
          "raw_buy_key": { "type": "string", "nullable": true, "description": "Present when raw=true" },
          "raw_buy_ref": { "type": "string", "nullable": true, "description": "Present when raw=true" },
          "raw_sell_key": { "type": "string", "nullable": true, "description": "Present when raw=true" },
          "raw_sell_ref": { "type": "string", "nullable": true, "description": "Present when raw=true" },
          "listings": { "$ref": "#/components/schemas/Listings", "description": "Present when listings=true" }
        }
      },
      "Listings": {
        "type": "object",
        "properties": {
          "observed_at": { "type": "string", "nullable": true, "format": "date-time" },
          "buy": { "type": "array", "items": { "$ref": "#/components/schemas/Listing" } },
          "sell": { "type": "array", "items": { "$ref": "#/components/schemas/Listing" } }
        }
      },
      "Listing": {
        "type": "object",
        "properties": {
          "steamid": { "type": "string", "example": "76561198000000000" },
          "keys": { "type": "number", "nullable": true, "example": 12 },
          "metal": { "type": "number", "nullable": true, "example": 52.77 },
          "is_bot": { "type": "boolean" },
          "bumped_at": { "type": "number", "example": 1787870425 },
          "source": { "type": "string", "example": "bptf" },
          "channel": { "type": "string", "nullable": true, "example": "request" },
          "trade_url": { "type": "string", "nullable": true, "example": "https://steamcommunity.com/tradeoffer/new/?partner=123456789&token=EXAMPLE" },
          "details": { "type": "string", "nullable": true },
          "stock_text": { "type": "string", "nullable": true },
          "stock_level": { "type": "number", "nullable": true },
          "stock_limit": { "type": "number", "nullable": true },
          "observed_at": { "type": "string", "format": "date-time" }
        }
      }
    }
  }
}
