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: [email protected]
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: [email protected]
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