openapi: 3.0.3 info: title: BuyWhere Product Catalog API version: '1' description: | Agent-native product catalog API for Southeast Asia and US commerce. Search 1.5M+ products across Shopee, Lazada, Amazon, Walmart, FairPrice, Carousell, Best Denki, and 20+ e-commerce platforms. Compare prices, discover deals, and find best prices through REST or MCP (Model Context Protocol). Responses are structured for LLM and agent consumption (Schema.org-compatible `Product` / `Offer` / `ItemList` shapes). Each product carries normalized `structured_specs` (brand, model, size, color) and `comparison_attributes` so agents can reason and rank without scraping. BuyWhere is MCP-native — the same operations are exposed at `POST https://api.buywhere.ai/mcp` for MCP-compatible clients, with a hosted HTTP transport and a published `@buywhere/mcp-server` STDIO package. contact: name: BuyWhere API email: api@buywhere.ai url: https://api.buywhere.ai/ termsOfService: https://buywhere.ai/terms license: name: Commercial url: https://buywhere.ai/terms servers: - url: https://api.buywhere.ai/v1 description: Production REST API tags: - name: Authentication description: Agent registration and API key issuance. - name: Products description: Product search, lookup, comparison, deals, and price history. - name: Categories description: Product taxonomy and category browsing. - name: MCP description: Model Context Protocol surface (see /mcp endpoint and docs). paths: /auth/register: post: summary: Register Agent And Issue API Key description: Register an AI agent (or application) and receive a Bearer API key with a free-tier rate limit allocation. operationId: registerAgent tags: - Authentication requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/AgentRegistration' example: agent_name: shopping-copilot contact: dev@example.com use_case: Price comparison assistant for Singapore consumers. responses: '201': description: API key issued for the agent. content: application/json: schema: $ref: '#/components/schemas/ApiKeyIssue' example: api_key: bw_free_2f7a9c0b1d8e4f3a9c0b1d8e4f3a9c0b tier: free rate_limit: rpm: 60 daily: 1000 '400': $ref: '#/components/responses/BadRequest' /products/search: get: summary: Search Products By Keyword description: Full-text search across the BuyWhere catalog with filters for merchant platform, region, country, price range, and currency. Set `compact=true` for an LLM-optimized payload. operationId: searchProducts tags: - Products security: - BearerAuth: [] parameters: - name: q in: query schema: type: string description: Keyword search query (full-text). example: wireless headphones - name: domain in: query schema: type: string description: Filter by merchant platform (e.g. `lazada`, `shopee`, `amazon`, `walmart`, `carousell`). example: shopee - name: region in: query schema: type: string enum: [sea, us, eu, au] description: Filter by region. - name: country_code in: query schema: type: string enum: [SG, US, VN, TH, MY] description: | Filter by ISO country code. When provided without an explicit `currency` param, the default currency is inferred (SG→SGD, US→USD, VN→VND, TH→THB, MY→MYR). `min_price` / `max_price` apply in the inferred currency. Default: `SG`. - name: min_price in: query schema: type: number description: Minimum price in the active currency. - name: max_price in: query schema: type: number description: Maximum price in the active currency. - name: currency in: query schema: type: string default: SGD description: Explicit currency override. If omitted and `country_code` is set, currency is inferred from `country_code`. - name: compact in: query schema: type: boolean default: false description: Return a minimal payload for AI agents (id, title, price, currency, url, structured_specs, comparison_attributes). - name: limit in: query schema: type: integer default: 20 maximum: 100 - name: offset in: query schema: type: integer default: 0 responses: '200': description: Product list with meta (total, response_time_ms, cached). content: application/json: schema: $ref: '#/components/schemas/ProductList' '401': $ref: '#/components/responses/Unauthorized' '429': $ref: '#/components/responses/RateLimited' /products/deals: get: summary: List Discounted Products By Discount Percentage description: Returns products sorted by discount percentage, filtered by minimum discount, currency, and country. operationId: getDeals tags: - Products security: - BearerAuth: [] parameters: - name: currency in: query schema: type: string default: SGD - name: country_code in: query schema: type: string enum: [SG, US, VN, TH, MY] description: When set, only deals from that country are returned. - name: min_discount in: query schema: type: number default: 10 description: Minimum discount percentage (0–90). - name: limit in: query schema: type: integer default: 20 maximum: 100 - name: offset in: query schema: type: integer default: 0 responses: '200': description: Discounted products with `price`, `original_price`, and `discount_pct`. content: application/json: schema: $ref: '#/components/schemas/DealList' '401': $ref: '#/components/responses/Unauthorized' '429': $ref: '#/components/responses/RateLimited' /products/compare: get: summary: Compare Multiple Products Side-By-Side description: Compare 2–10 products side-by-side with normalized price, brand, rating, and category path. operationId: compareProducts tags: - Products security: - BearerAuth: [] parameters: - name: ids in: query required: true schema: type: string description: Comma-separated product IDs (2–10). example: 8a2c3a8e-1234-5678-9abc-def012345678,9b3d4b9f-2345-6789-abcd-ef0123456789 responses: '200': description: Array of products with comparison-ready attributes. content: application/json: schema: $ref: '#/components/schemas/CompareResponse' '400': description: Fewer than 2 IDs provided. content: application/json: schema: $ref: '#/components/schemas/Error' '401': $ref: '#/components/responses/Unauthorized' /products/{id}: get: summary: Get Product By ID description: Returns the full product detail including title, description, price, merchant, structured specs, and Schema.org-compatible metadata. operationId: getProduct tags: - Products security: - BearerAuth: [] parameters: - name: id in: path required: true schema: type: string format: uuid responses: '200': description: Product detail. content: application/json: schema: $ref: '#/components/schemas/Product' '404': $ref: '#/components/responses/NotFound' '401': $ref: '#/components/responses/Unauthorized' /products/{id}/prices: get: summary: Get Product Price History description: Returns price history (with min/max/avg stats) over a configurable look-back window. operationId: getProductPrices tags: - Products security: - BearerAuth: [] parameters: - name: id in: path required: true schema: type: string format: uuid - name: days in: query schema: type: integer default: 30 maximum: 90 description: Look-back window in days. responses: '200': description: Price history with min/max/avg stats. content: application/json: schema: $ref: '#/components/schemas/PriceHistory' '404': $ref: '#/components/responses/NotFound' '401': $ref: '#/components/responses/Unauthorized' /categories: get: summary: List Top-Level Product Categories description: List top-level product categories with slug, name, and product count. operationId: listCategories tags: - Categories security: - BearerAuth: [] parameters: - name: currency in: query schema: type: string default: SGD responses: '200': description: Category list with slug, name, and product_count. content: application/json: schema: $ref: '#/components/schemas/CategoryList' '401': $ref: '#/components/responses/Unauthorized' /categories/{slug}: get: summary: Get Products Within A Category description: Returns products in the specified category along with any subcategories. operationId: getCategoryProducts tags: - Categories security: - BearerAuth: [] parameters: - name: slug in: path required: true schema: type: string description: Category slug (from `/categories`). - name: currency in: query schema: type: string default: SGD - name: limit in: query schema: type: integer default: 20 maximum: 100 - name: offset in: query schema: type: integer default: 0 responses: '200': description: Category detail with subcategories and products. content: application/json: schema: $ref: '#/components/schemas/CategoryDetail' '404': $ref: '#/components/responses/NotFound' '401': $ref: '#/components/responses/Unauthorized' components: securitySchemes: BearerAuth: type: http scheme: bearer description: | Pass your API key as a Bearer token. Get a free key at `POST /v1/auth/register`. Tiers: `bw_free_*` (60 rpm), `bw_live_*` (600 rpm), `bw_partner_*` (unlimited). responses: Unauthorized: description: Missing or invalid API key. content: application/json: schema: $ref: '#/components/schemas/Error' NotFound: description: Resource not found. content: application/json: schema: $ref: '#/components/schemas/Error' BadRequest: description: Invalid request. content: application/json: schema: $ref: '#/components/schemas/Error' RateLimited: description: Rate limit exceeded. Implement exponential backoff and respect `Retry-After`. headers: Retry-After: schema: type: integer description: Seconds to wait before retrying. content: application/json: schema: $ref: '#/components/schemas/Error' schemas: AgentRegistration: type: object required: - agent_name properties: agent_name: type: string description: Name or identifier of your agent. contact: type: string format: email description: Contact email (optional). use_case: type: string description: Brief description of your use case. ApiKeyIssue: type: object properties: api_key: type: string description: Bearer token (prefixed with `bw_free_`, `bw_live_`, or `bw_partner_`). tier: type: string enum: [free, live, partner] rate_limit: type: object properties: rpm: type: integer description: Requests per minute. daily: type: integer description: Daily request quota. Product: type: object description: Schema.org-compatible product representation. properties: id: type: string format: uuid title: type: string description: type: string brand: type: string domain: type: string description: Source merchant platform (lazada, shopee, amazon, walmart, etc.). url: type: string format: uri description: Canonical merchant product URL. image_url: type: string format: uri price: type: number original_price: type: number discount_pct: type: number currency: type: string example: SGD country_code: type: string example: SG rating: type: number review_count: type: integer category_path: type: array items: type: string structured_specs: type: object description: Normalized structured attributes (brand, model, size, color, etc.). additionalProperties: true comparison_attributes: type: object description: Attributes pre-normalized for agent comparison reasoning. additionalProperties: true normalized_price_usd: type: number availability: type: string enum: [in_stock, out_of_stock, preorder, limited] updated_at: type: string format: date-time ProductList: type: object properties: data: type: array items: $ref: '#/components/schemas/Product' meta: type: object properties: total: type: integer response_time_ms: type: integer cached: type: boolean limit: type: integer offset: type: integer Deal: allOf: - $ref: '#/components/schemas/Product' - type: object properties: discount_pct: type: number description: Discount percentage (0–90). DealList: type: object properties: data: type: array items: $ref: '#/components/schemas/Deal' meta: type: object properties: total: type: integer response_time_ms: type: integer CompareResponse: type: object properties: data: type: array items: $ref: '#/components/schemas/Product' meta: type: object properties: count: type: integer PricePoint: type: object properties: date: type: string format: date price: type: number currency: type: string PriceHistory: type: object properties: id: type: string format: uuid currency: type: string days: type: integer points: type: array items: $ref: '#/components/schemas/PricePoint' stats: type: object properties: min: type: number max: type: number avg: type: number current: type: number Category: type: object properties: slug: type: string name: type: string product_count: type: integer parent_slug: type: string nullable: true CategoryList: type: object properties: data: type: array items: $ref: '#/components/schemas/Category' CategoryDetail: type: object properties: category: $ref: '#/components/schemas/Category' subcategories: type: array items: $ref: '#/components/schemas/Category' products: type: array items: $ref: '#/components/schemas/Product' meta: type: object properties: total: type: integer limit: type: integer offset: type: integer Error: type: object properties: error: type: object properties: code: type: string enum: [invalid_params, not_found, rate_limited, unauthorized, internal_error] message: type: string request_id: type: string externalDocs: description: BuyWhere developer documentation and MCP guide url: https://api.buywhere.ai/docs/guides/mcp