OpenAI Function Calling Integration Guide
Use OpenAI's function calling (tool use) to give GPT models real-time access to the BuyWhere product catalog.
Overview
BuyWhere exposes its full product catalog via a REST API. Attach BuyWhere function definitions to your OpenAI tools array to enable GPT to search products, compare prices, get deals, and browse categories.
Base URL: https://api.buywhere.ai/api/v1
Authentication: Authorization: Bearer <your_api_key>
Function Definitions
Copy these into your OpenAI tools array.
search_products
Full-text search across the BuyWhere catalog with optional filters. Returns paginated results from Lazada, Shopee, Qoo10, Carousell, and other Singapore platforms.
{
"type": "function",
"function": {
"name": "search_products",
"description": "Full-text search across product catalog with optional filters. Uses PostgreSQL full-text search with English language ranking. Returns paginated results.",
"parameters": {
"type": "object",
"properties": {
"q": {
"type": "string",
"description": "Full-text search query for products",
"example": "iphone 15 pro"
},
"category": {
"type": "string",
"description": "Filter by category name (case-insensitive partial match)",
"example": "Electronics"
},
"min_price": {
"type": "number",
"description": "Minimum price filter",
"example": 100.00
},
"max_price": {
"type": "number",
"description": "Maximum price filter",
"example": 999.00
},
"source": {
"type": "string",
"description": "Filter by platform/source (e.g., lazada_sg, shopee_sg, carousell, qoo10)",
"example": "shopee_sg"
},
"brand": {
"type": "string",
"description": "Filter by brand name (case-insensitive partial match)",
"example": "Apple"
},
"in_stock": {
"type": "boolean",
"description": "Filter by availability",
"example": true
},
"limit": {
"type": "integer",
"minimum": 1,
"maximum": 100,
"default": 20,
"description": "Number of results per page"
},
"offset": {
"type": "integer",
"minimum": 0,
"default": 0,
"description": "Pagination offset"
}
}
}
}
}
get_product
Retrieve a single product by its unique ID. Only returns active products.
{
"type": "function",
"function": {
"name": "get_product",
"description": "Retrieve a single product by its unique ID. Only returns active products.",
"parameters": {
"type": "object",
"required": ["product_id"],
"properties": {
"product_id": {
"type": "integer",
"description": "Unique product ID",
"example": 12345
}
}
}
}
}
compare_prices
Find and compare the same product across different e-commerce platforms. Uses fuzzy matching on product title and metadata to identify equivalent products. Returns matches sorted by price.
{
"type": "function",
"function": {
"name": "compare_prices",
"description": "Find and compare the same product across different e-commerce platforms. Uses fuzzy matching on product title and metadata to identify equivalent products. Returns matches sorted by price.",
"parameters": {
"type": "object",
"required": ["product_id"],
"properties": {
"product_id": {
"type": "integer",
"description": "Source product ID to find matches for",
"example": 12345
},
"min_price": {
"type": "number",
"description": "Minimum price filter for matches",
"example": 100.00
},
"max_price": {
"type": "number",
"description": "Maximum price filter for matches",
"example": 999.00
}
}
}
}
}
get_deals
Find products currently priced below their original/historical price. A product is identified as a deal when metadata contains original_price and current price is lower by at least min_discount_pct%. Sorted by discount depth (largest discount first).
{
"type": "function",
"function": {
"name": "get_deals",
"description": "Find products currently priced below their original/historical price. A product is identified as a deal when metadata contains original_price and current price is lower by at least min_discount_pct%. Sorted by discount depth (largest discount first).",
"parameters": {
"type": "object",
"properties": {
"category": {
"type": "string",
"description": "Filter by product category",
"example": "Electronics"
},
"min_discount_pct": {
"type": "number",
"minimum": 0,
"maximum": 100,
"default": 10.0,
"description": "Minimum discount percentage (0-100, default 10)"
},
"platform": {
"type": "string",
"description": "Filter by platform/source (e.g., lazada_sg, shopee_sg)",
"example": "shopee_sg"
},
"limit": {
"type": "integer",
"minimum": 1,
"maximum": 100,
"default": 20,
"description": "Max results (1-100, default 20)"
},
"offset": {
"type": "integer",
"minimum": 0,
"default": 0,
"description": "Pagination offset"
}
}
}
}
}
get_categories
List all available product categories with active product counts. Sorted by product count descending.
{
"type": "function",
"function": {
"name": "get_categories",
"description": "List all available product categories with active product counts. Sorted by product count descending.",
"parameters": {
"type": "object",
"properties": {}
}
}
}
get_trending_products
Get trending products ranked by query volume and clicks. Trending is computed over configurable periods.
{
"type": "function",
"function": {
"name": "get_trending_products",
"description": "Get trending products ranked by query volume and clicks. Trending score = views + clicks over the period.",
"parameters": {
"type": "object",
"properties": {
"period": {
"type": "string",
"enum": ["24h", "7d", "30d"],
"default": "7d",
"description": "Trending period: 24h, 7d, or 30d"
},
"category": {
"type": "string",
"description": "Filter by category name",
"example": "Electronics"
},
"limit": {
"type": "integer",
"minimum": 1,
"maximum": 100,
"default": 50,
"description": "Number of products to return (1-100)"
}
}
}
}
}
get_product_by_barcode
Find a product by its UPC/EAN barcode.
{
"type": "function",
"function": {
"name": "get_product_by_barcode",
"description": "Find a product by its UPC/EAN barcode.",
"parameters": {
"type": "object",
"required": ["barcode"],
"properties": {
"barcode": {
"type": "string",
"description": "UPC/EAN barcode (12-13 digits)",
"example": "194252609396"
}
}
}
}
}
bulk_lookup
Bulk product lookup by SKU, UPC, or product URL. Accepts up to 100 identifiers per request.
{
"type": "function",
"function": {
"name": "bulk_lookup",
"description": "Bulk product lookup by SKU, UPC, or product URL. Accepts up to 100 identifiers per request.",
"parameters": {
"type": "object",
"required": ["identifiers"],
"properties": {
"identifiers": {
"type": "array",
"items": {"type": "string"},
"description": "List of SKU, UPC, or product URL to look up (max 100)",
"example": ["SKU123", "194252609396", "https://shopee.sg/product/123"]
}
}
}
}
}
Complete Python Example
Single-Turn: Search and Respond
import json
from openai import OpenAI
import httpx
client = OpenAI(api_key="sk-...")
BUYWHERE_API_KEY = "bw_live_xxxxx"
BUYWHERE_BASE = "https://api.buywhere.ai/api/v1"
BUYWHERE_FUNCTIONS = [
{
"type": "function",
"function": {
"name": "search_products",
"description": "Full-text search across product catalog with optional filters.",
"parameters": {
"type": "object",
"properties": {
"q": {"type": "string", "description": "Full-text search query"},
"category": {"type": "string"},
"min_price": {"type": "number"},
"max_price": {"type": "number"},
"source": {"type": "string"},
"brand": {"type": "string"},
"in_stock": {"type": "boolean"},
"limit": {"type": "integer", "minimum": 1, "maximum": 100, "default": 20},
"offset": {"type": "integer", "minimum": 0, "default": 0},
}
}
}
},
{
"type": "function",
"function": {
"name": "get_product",
"description": "Retrieve a single product by its unique ID.",
"parameters": {
"type": "object",
"required": ["product_id"],
"properties": {
"product_id": {"type": "integer", "description": "Unique product ID"}
}
}
}
},
{
"type": "function",
"function": {
"name": "get_deals",
"description": "Find products currently priced below their original/historical price.",
"parameters": {
"type": "object",
"properties": {
"category": {"type": "string"},
"min_discount_pct": {"type": "number", "default": 10.0},
"platform": {"type": "string"},
"limit": {"type": "integer", "minimum": 1, "maximum": 100, "default": 20},
"offset": {"type": "integer", "default": 0},
}
}
}
},
{
"type": "function",
"function": {
"name": "get_categories",
"description": "List all available product categories with active product counts.",
"parameters": {"type": "object", "properties": {}}
}
},
]
def search_buywhere(q: str, **kwargs):
headers = {"Authorization": f"Bearer {BUYWHERE_API_KEY}"}
params = {"q": q}
params.update({k: v for k, v in kwargs.items() if v is not None})
resp = httpx.get(
f"{BUYWHERE_BASE}/products",
headers=headers,
params=params,
timeout=15.0,
)
resp.raise_for_status()
return resp.json()
def get_product_buywhere(product_id: int):
headers = {"Authorization": f"Bearer {BUYWHERE_API_KEY}"}
resp = httpx.get(
f"{BUYWHERE_BASE}/products/{product_id}",
headers=headers,
timeout=15.0,
)
resp.raise_for_status()
return resp.json()
def get_deals_buywhere(**kwargs):
headers = {"Authorization": f"Bearer {BUYWHERE_API_KEY}"}
resp = httpx.get(
f"{BUYWHERE_BASE}/deals",
headers=headers,
params={k: v for k, v in kwargs.items() if v is not None},
timeout=15.0,
)
resp.raise_for_status()
return resp.json()
def get_categories_buywhere():
headers = {"Authorization": f"Bearer {BUYWHERE_API_KEY}"}
resp = httpx.get(
f"{BUYWHERE_BASE}/categories",
headers=headers,
timeout=15.0,
)
resp.raise_for_status()
return resp.json()
TOOL_MAP = {
"search_products": lambda args: search_buywhere(**args),
"get_product": lambda args: get_product_buywhere(**args),
"get_deals": lambda args: get_deals_buywhere(**args),
"get_categories": lambda args: get_categories_buywhere(),
}
def ask_buywhere(user_query: str) -> str:
response = client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": user_query}],
tools=BUYWHERE_FUNCTIONS,
tool_choice="auto",
)
message = response.choices[0].message
if not message.tool_calls:
return message.content
results = []
for tool_call in message.tool_calls:
fn = tool_call.function
args = json.loads(fn.arguments)
result = TOOL_MAP[fn.name](args)
results.append({
"tool_call_id": tool_call.id,
"function_name": fn.name,
"result": result,
})
final = client.chat.completions.create(
model="gpt-4o",
messages=[
{"role": "user", "content": user_query},
message,
*[{
"role": "tool",
"tool_call_id": r["tool_call_id"],
"content": json.dumps(r["result"], ensure_ascii=False),
} for r in results],
],
)
return final.choices[0].message.content
if __name__ == "__main__":
answer = ask_buywhere(
"What's the best price for an iphone 15 pro in Singapore right now?"
)
print(answer)
Multi-Turn: Category Browsing then Product Search
def browse_then_search(user_query: str) -> str:
messages = [{"role": "user", "content": user_query}]
while True:
response = client.chat.completions.create(
model="gpt-4o",
messages=messages,
tools=BUYWHERE_FUNCTIONS,
tool_choice="auto",
)
msg = response.choices[0].message
messages.append(msg)
if not msg.tool_calls:
return msg.content
for tool_call in msg.tool_calls:
fn = tool_call.function
args = json.loads(fn.arguments)
result = TOOL_MAP[fn.name](args)
messages.append({
"role": "tool",
"tool_call_id": tool_call.id,
"content": json.dumps(result, ensure_ascii=False),
})
# Multi-turn example: discover category first, then search within it
answer = browse_then_search(
"Show me the top categories for electronics, then find me the cheapest "
"Samsung Galaxy phone under $800 in Singapore."
)
API Response Shapes
GET /api/v1/products
{
"total": 142,
"limit": 20,
"offset": 0,
"has_more": true,
"items": [
{
"id": 12345,
"sku": "shopee_sg_987654321",
"source": "shopee_sg",
"merchant_id": "shop_12345",
"name": "iPhone 15 Pro 256GB Natural Titanium",
"description": "Apple iPhone 15 Pro...",
"price": "949.00",
"currency": "SGD",
"price_sgd": "949.00",
"buy_url": "https://shopee.sg/product/123/456",
"affiliate_url": "https://buywhere.ai/track/...",
"image_url": "https://cf.shopee.sg/file/...",
"barcode": "194252609396",
"brand": "Apple",
"category": "Mobile Phones",
"category_path": ["Electronics", "Mobile Phones", "Smartphones"],
"rating": "4.8",
"review_count": 1243,
"is_available": true,
"in_stock": true,
"stock_level": "high",
"last_checked": "2026-04-04T10:30:00Z",
"updated_at": "2026-04-04T10:30:00Z",
"price_trend": "down"
}
],
"facets": {
"categories": [{"value": "Mobile Phones", "count": 42}],
"platforms": [{"value": "shopee_sg", "count": 38}],
"brands": [{"value": "Apple", "count": 25}],
"rating_ranges": [{"min_rating": 4.0, "max_rating": 5.0, "count": 30}],
"price_ranges": [
{"min_price": "500", "max_price": "1000", "count": 55}
]
}
}
GET /api/v1/products/{id}
{
"id": 12345,
"sku": "shopee_sg_987654321",
"source": "shopee_sg",
"merchant_id": "shop_12345",
"name": "iPhone 15 Pro 256GB Natural Titanium",
"description": "Apple iPhone 15 Pro with A17 Pro chip...",
"price": "949.00",
"currency": "SGD",
"price_sgd": "949.00",
"buy_url": "https://shopee.sg/product/123/456",
"affiliate_url": "https://buywhere.ai/track/...",
"image_url": "https://cf.shopee.sg/file/...",
"barcode": "194252609396",
"brand": "Apple",
"category": "Mobile Phones",
"category_path": ["Electronics", "Mobile Phones", "Smartphones"],
"rating": "4.8",
"review_count": 1243,
"is_available": true,
"in_stock": true,
"stock_level": "high",
"last_checked": "2026-04-04T10:30:00Z",
"metadata": {
"original_price": 1099.00,
"shipping_cost": 0,
"free_shipping": true,
"condition": "new"
},
"updated_at": "2026-04-04T10:30:00Z",
"price_trend": "down"
}
GET /api/v1/deals
{
"deals": [
{
"id": 67890,
"sku": "lazada_sg_123456789",
"source": "lazada_sg",
"merchant_id": "lazada_store_456",
"name": "Samsung Galaxy Buds2 Pro",
"description": "Samsung Galaxy Buds2 Pro wireless earbuds...",
"price": "178.00",
"currency": "SGD",
"buy_url": "https://www.lazada.sg/products/...",
"affiliate_url": "https://buywhere.ai/track/...",
"image_url": "https://icms-image.slatic.net/...",
"brand": "Samsung",
"category": "Audio",
"is_available": true,
"metadata": {
"original_price": "279.00",
"discount_pct": 36,
"deal_end_at": "2026-04-30T23:59:59Z"
},
"updated_at": "2026-04-04T08:00:00Z"
}
],
"total": 847,
"limit": 20,
"offset": 0
}
Error Handling
import httpx
def search_safe(query: str, **kwargs):
try:
resp = httpx.get(
f"{BUYWHERE_BASE}/products",
headers={"Authorization": f"Bearer {BUYWHERE_API_KEY}"},
params={"q": query, **kwargs},
timeout=15.0,
)
resp.raise_for_status()
return resp.json()
except httpx.HTTPStatusError as e:
if e.response.status_code == 401:
raise RuntimeError("Invalid BuyWhere API key")
elif e.response.status_code == 429:
raise RuntimeError("BuyWhere rate limit exceeded — retry after a few seconds")
elif e.response.status_code == 404:
return None # product not found
raise
except httpx.RequestError as e:
raise RuntimeError(f"BuyWhere API unreachable: {e}")
Tips for AI Agents
- Use
q(notquery) for search — the parameter name isqto match PostgreSQL full-text search conventions. - Prefer
in_stockfilter for purchase intent — when the user wants to buy something, filterin_stock: trueto remove unavailable products. - Use
compare_pricesafter finding a product — once GPT has a product ID from search, it can callcompare_pricesto find the same product on other platforms. - Chain
get_categories→search_products— first discover available categories, then search within the right category. - Use
bulk_lookupfor shopping lists — when the user provides multiple product names or SKUs, batch them into a single bulk_lookup call. - Handle empty results gracefully — if a search returns 0 products, try broadening the query or removing price filters.
- Use affiliate_url for purchases — always prefer
affiliate_urloverbuy_urlto support BuyWhere attribution.
See Also
- MCP Server Deployment Guide — self-host the BuyWhere MCP server
- Authentication Guide — API key management and rate limits
- Claude Tool Use Guide — Claude-specific tool calling patterns
- OpenAPI spec:
openapi.yaml