{
 "openapi": "3.1.0",
 "info": {
  "title": "Goblin Exchange API",
  "version": "1.0.0",
  "description": "Read the site's own WoW Forever auction house data, advisor brief, item index, talents and your own characters. Never calls Blizzard. Limits: 30/min and 1000/day per key, 2000/day all keys. Money in copper. Docs: https://heyfatguy.com/api and /llms.txt."
 },
 "servers": [
  {
   "url": "https://heyfatguy.com/api/v1"
  }
 ],
 "tags": [
  {
   "name": "public",
   "description": "Static JSON, no key"
  },
  {
   "name": "key",
   "description": "Authorization: Bearer gex_..., own account only"
  }
 ],
 "security": [
  {
   "bearer": []
  }
 ],
 "paths": {
  "/market": {
   "get": {
    "summary": "Auction house market",
    "description": "Every item on the AH now: ask, fair, mv (market value), qty, sold_day, chg24, flip {units,cost,relist,profit,roi}, signal. Copper. Static file, about 1.5 MB.",
    "tags": [
     "public"
    ],
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     }
    },
    "security": []
   }
  },
  "/item/{id}": {
   "get": {
    "summary": "Item price history",
    "description": "item, history (per snapshot), book (order book). 404 = no price history in this house.",
    "tags": [
     "public"
    ],
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     }
    },
    "parameters": [
     {
      "name": "id",
      "in": "path",
      "required": true,
      "description": "Item id",
      "schema": {
       "type": "integer"
      }
     }
    ],
    "security": []
   }
  },
  "/advisor": {
   "get": {
    "summary": "Advisor brief",
    "description": "brief {headline, market_read, picks, avoid, data_caveats} and scored lists in picks {flip, craft, vendor, sell, momentum}.",
    "tags": [
     "public"
    ],
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     }
    },
    "security": []
   }
  },
  "/crafts": {
   "get": {
    "summary": "Crafting profits",
    "description": "recipes with reagent cost, sale value, profit, sold_day.",
    "tags": [
     "public"
    ],
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     }
    },
    "security": []
   }
  },
  "/houses": {
   "get": {
    "summary": "Auction houses",
    "description": "default house and {house key: slug}; other houses at /data/{slug}/market.json.",
    "tags": [
     "public"
    ],
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     }
    },
    "security": []
   }
  },
  "/items": {
   "get": {
    "summary": "Item index",
    "description": "All items (about 11k, 2.7 MB): id, name, q, ilvl, lvl, slots, stats, dmg, classMask. Prefer /items/search.",
    "tags": [
     "public"
    ],
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     }
    },
    "security": []
   }
  },
  "/items/sources": {
   "get": {
    "summary": "Item sources",
    "description": "items {id: [{t, ..., p}]} and quests {id: {...}}.",
    "tags": [
     "public"
    ],
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     }
    },
    "security": []
   }
  },
  "/items/meta": {
   "get": {
    "summary": "Item metadata",
    "description": "statNames, Paladin role weights, slot ids.",
    "tags": [
     "public"
    ],
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     }
    },
    "security": []
   }
  },
  "/talents": {
   "get": {
    "summary": "Talent index",
    "description": "Classes and points per level.",
    "tags": [
     "public"
    ],
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     }
    },
    "security": []
   }
  },
  "/talents/{class}": {
   "get": {
    "summary": "Class talent trees",
    "description": "Trait trees, tabs, nodes, ranks, spells, prerequisites.",
    "tags": [
     "public"
    ],
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     }
    },
    "parameters": [
     {
      "name": "class",
      "in": "path",
      "required": true,
      "description": "Class",
      "schema": {
       "type": "string",
       "enum": [
        "warrior",
        "paladin",
        "hunter",
        "rogue",
        "priest",
        "shaman",
        "mage",
        "warlock",
        "druid"
       ]
      }
     }
    ],
    "security": []
   }
  },
  "/me": {
   "get": {
    "summary": "This key",
    "description": "account, battletag, key {id,name,scopes,created}, limits, usage.",
    "tags": [
     "key"
    ],
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     },
     "401": {
      "$ref": "#/components/responses/Error"
     },
     "403": {
      "$ref": "#/components/responses/Error"
     },
     "429": {
      "$ref": "#/components/responses/RateLimited"
     }
    },
    "security": [
     {
      "bearer": []
     }
    ]
   }
  },
  "/characters": {
   "get": {
    "summary": "Your characters",
    "description": "characters [{guid, name, level, class, race, faction, realm, guild, gold, spec, time}], newest first.",
    "tags": [
     "key"
    ],
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     },
     "401": {
      "$ref": "#/components/responses/Error"
     },
     "403": {
      "$ref": "#/components/responses/Error"
     },
     "429": {
      "$ref": "#/components/responses/RateLimited"
     }
    },
    "security": [
     {
      "bearer": []
     }
    ]
   }
  },
  "/characters/{guid}": {
   "get": {
    "summary": "One character snapshot",
    "description": "The full addon snapshot: worn and bag items, sheet stats, talents, completedQuests.",
    "tags": [
     "key"
    ],
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     },
     "401": {
      "$ref": "#/components/responses/Error"
     },
     "403": {
      "$ref": "#/components/responses/Error"
     },
     "429": {
      "$ref": "#/components/responses/RateLimited"
     }
    },
    "parameters": [
     {
      "name": "guid",
      "in": "path",
      "required": true,
      "description": "Character GUID, e.g. Player-5826-0ABCDEF1",
      "schema": {
       "type": "string"
      }
     }
    ],
    "security": [
     {
      "bearer": []
     }
    ]
   }
  },
  "/upgrades": {
   "get": {
    "summary": "AH upgrade finder",
    "description": "characters {guid: {by_role, note, gold, snapshot}}.",
    "tags": [
     "key"
    ],
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     },
     "401": {
      "$ref": "#/components/responses/Error"
     },
     "403": {
      "$ref": "#/components/responses/Error"
     },
     "429": {
      "$ref": "#/components/responses/RateLimited"
     }
    },
    "parameters": [
     {
      "name": "guid",
      "in": "query",
      "required": false,
      "description": "Only this character",
      "schema": {
       "type": "string"
      }
     }
    ],
    "security": [
     {
      "bearer": []
     }
    ]
   }
  },
  "/talent-templates": {
   "get": {
    "summary": "Your talent builds",
    "description": "templates [{id, name, cls, code, ranks, level, role, share}].",
    "tags": [
     "key"
    ],
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     },
     "401": {
      "$ref": "#/components/responses/Error"
     },
     "403": {
      "$ref": "#/components/responses/Error"
     },
     "429": {
      "$ref": "#/components/responses/RateLimited"
     }
    },
    "parameters": [
     {
      "name": "class",
      "in": "query",
      "required": false,
      "description": "Only this class",
      "schema": {
       "type": "string"
      }
     }
    ],
    "security": [
     {
      "bearer": []
     }
    ]
   }
  },
  "/items/search": {
   "get": {
    "summary": "Search items",
    "description": "Planner search over the item index with live AH prices.",
    "tags": [
     "key"
    ],
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     },
     "401": {
      "$ref": "#/components/responses/Error"
     },
     "403": {
      "$ref": "#/components/responses/Error"
     },
     "429": {
      "$ref": "#/components/responses/RateLimited"
     }
    },
    "parameters": [
     {
      "name": "q",
      "in": "query",
      "required": false,
      "description": "Name contains, or exact item id",
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "slot",
      "in": "query",
      "required": false,
      "description": "Equip slot id (1 Head .. 18 Relic)",
      "schema": {
       "type": "integer"
      }
     },
     {
      "name": "min",
      "in": "query",
      "required": false,
      "description": "Minimum required level",
      "schema": {
       "type": "integer"
      }
     },
     {
      "name": "max",
      "in": "query",
      "required": false,
      "description": "Maximum required level",
      "schema": {
       "type": "integer"
      }
     },
     {
      "name": "quality",
      "in": "query",
      "required": false,
      "description": "Minimum quality 0..5",
      "schema": {
       "type": "integer"
      }
     },
     {
      "name": "class",
      "in": "query",
      "required": false,
      "description": "Skip items and quests restricted to other classes; paladin also checks armor/weapon proficiency",
      "schema": {
       "type": "string",
       "enum": [
        "warrior",
        "paladin",
        "hunter",
        "rogue",
        "priest",
        "shaman",
        "mage",
        "warlock",
        "druid"
       ]
      }
     },
     {
      "name": "level",
      "in": "query",
      "required": false,
      "description": "Character level for the paladin plate check (default max, else 60)",
      "schema": {
       "type": "integer"
      }
     },
     {
      "name": "role",
      "in": "query",
      "required": false,
      "description": "Score with the Paladin weights for this role and sort by it",
      "schema": {
       "type": "string",
       "enum": [
        "ret",
        "prot",
        "holy"
       ]
      }
     },
     {
      "name": "onAh",
      "in": "query",
      "required": false,
      "description": "1 = only items listed now",
      "schema": {
       "type": "string",
       "enum": [
        "0",
        "1"
       ]
      }
     },
     {
      "name": "obtain",
      "in": "query",
      "required": false,
      "description": "Source mode",
      "schema": {
       "type": "string",
       "enum": [
        "any",
        "ah",
        "all"
       ]
      }
     },
     {
      "name": "quests",
      "in": "query",
      "required": false,
      "description": "Count quest rewards",
      "schema": {
       "type": "string",
       "enum": [
        "0",
        "1"
       ]
      }
     },
     {
      "name": "dungeons",
      "in": "query",
      "required": false,
      "description": "Count dungeon drops",
      "schema": {
       "type": "string",
       "enum": [
        "0",
        "1"
       ]
      }
     },
     {
      "name": "raids",
      "in": "query",
      "required": false,
      "description": "Count raid drops",
      "schema": {
       "type": "string",
       "enum": [
        "0",
        "1"
       ]
      }
     },
     {
      "name": "faction",
      "in": "query",
      "required": false,
      "description": "Skip quests for the other faction",
      "schema": {
       "type": "string",
       "enum": [
        "H",
        "A"
       ]
      }
     },
     {
      "name": "race",
      "in": "query",
      "required": false,
      "description": "Race id for quest race masks",
      "schema": {
       "type": "integer"
      }
     },
     {
      "name": "sort",
      "in": "query",
      "required": false,
      "description": "Sort order",
      "schema": {
       "type": "string",
       "enum": [
        "score",
        "ilvl",
        "lvl",
        "price"
       ]
      }
     },
     {
      "name": "limit",
      "in": "query",
      "required": false,
      "description": "Page size, default 50, max 200",
      "schema": {
       "type": "integer"
      }
     },
     {
      "name": "offset",
      "in": "query",
      "required": false,
      "description": "Offset",
      "schema": {
       "type": "integer"
      }
     },
     {
      "name": "house",
      "in": "query",
      "required": false,
      "description": "Auction house key (see /houses); only on key routes. Public routes serve the default house.",
      "schema": {
       "type": "string"
      }
     }
    ],
    "security": [
     {
      "bearer": []
     }
    ]
   }
  },
  "/import": {
   "post": {
    "summary": "Push character snapshots",
    "description": "Needs the import scope. Body = the GoblinExchangeDB.chars table as JSON, 1 MB max. Keeps the newest snapshot per GUID.",
    "tags": [
     "key"
    ],
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     },
     "401": {
      "$ref": "#/components/responses/Error"
     },
     "403": {
      "$ref": "#/components/responses/Error"
     },
     "429": {
      "$ref": "#/components/responses/RateLimited"
     },
     "400": {
      "$ref": "#/components/responses/Error"
     },
     "413": {
      "$ref": "#/components/responses/Error"
     },
     "415": {
      "$ref": "#/components/responses/Error"
     }
    },
    "security": [
     {
      "bearer": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "required": [
         "chars"
        ],
        "properties": {
         "chars": {
          "type": "object",
          "additionalProperties": {
           "type": "object"
          }
         }
        }
       }
      }
     }
    }
   }
  }
 },
 "components": {
  "securitySchemes": {
   "bearer": {
    "type": "http",
    "scheme": "bearer",
    "description": "Personal key from https://heyfatguy.com/api (gex_...)"
   }
  },
  "schemas": {
   "Error": {
    "type": "object",
    "properties": {
     "error": {
      "type": "object",
      "properties": {
       "status": {
        "type": "integer"
       },
       "code": {
        "type": "string",
        "enum": [
         "bad_request",
         "unauthorized",
         "forbidden",
         "not_found",
         "method_not_allowed",
         "too_large",
         "unsupported_media_type",
         "rate_limited",
         "upstream",
         "internal"
        ]
       },
       "message": {
        "type": "string"
       }
      }
     }
    }
   }
  },
  "responses": {
   "Error": {
    "description": "Error",
    "content": {
     "application/json": {
      "schema": {
       "$ref": "#/components/schemas/Error"
      }
     }
    }
   },
   "RateLimited": {
    "description": "Over a limit; wait Retry-After seconds",
    "headers": {
     "Retry-After": {
      "schema": {
       "type": "integer"
      }
     }
    },
    "content": {
     "application/json": {
      "schema": {
       "$ref": "#/components/schemas/Error"
      }
     }
    }
   }
  }
 }
}