BuyWhere + Anthropic Claude: Tool Use Integration Guide
Use BuyWhere's product catalog as tools in Claude (claude-3-5-sonnet-20241022 and newer) via the tool_use API. This guide covers tool definitions, API calls, and response handling in Python.
Overview
Anthropic's tool_use API lets you define custom tools that Claude can call during a conversation. BuyWhere provides six tools:
| Tool | What it does |
|---|---|
buywhere_search | Full-text product search across Singapore e-commerce |
buywhere_get_product | Retrieve a product by ID |
buywhere_compare_prices | Find and sort product matches by price |
buywhere_get_deals | Find discounted products |
buywhere_browse_categories | Browse the category taxonomy |
buywhere_get_category_products | Get products in a category |
Prerequisites
- Python 3.10+
anthropicPython package- A BuyWhere API key (
bw_live_xxxxx)
pip install anthropic buywhere-sdk
1. Set your environment
import os
os.environ["ANTHROPIC_API_KEY"] = "sk-ant-..." # from console.anthropic.com
os.environ["BUYWHERE_API_KEY"] = "bw_live_xxxxx" # from buywhere.ai/api
2. Define BuyWhere tools for Claude
Tools are passed to client.messages.create via the tools parameter. Each tool needs an input_schema describing its parameters.
from anthropic import Anthropic
client = Anthropic()
BUYWHERE_TOOLS = [
{
"name": "buywhere_search",
"description": "Search the BuyWhere product catalog by keyword. "
"Returns ranked results from Singapore e-commerce platforms "
"(Lazada, Shopee, Qoo10, Carousell, and more).",
"input_schema": {
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "Product search query.",
"example": "iphone 15 pro"
},
"category": {
"type": "string",
"description": "Optional category filter.",
"example": "Electronics"
},
"min_price": {
"type": "number",
"description": "Minimum price in SGD.",
"example": 100.00
},
"max_price": {
"type": "number",
"description": "Maximum price in SGD.",
"example": 999.00
},
"source": {
"type": "string",
"description": "Platform filter (lazada_sg, shopee_sg, carousell, qoo10).",
"example": "shopee_sg"
},
"limit": {
"type": "integer",
"description": "Max results (1-100, default 20).",
"default": 20
}
},
"required": ["query"]
}
},
{
"name": "buywhere_get_product",
"description": "Retrieve full details for a specific product by its BuyWhere ID.",
"input_schema": {
"type": "object",
"properties": {
"product_id": {
"type": "integer",
"description": "The BuyWhere product ID.",
"example": 12345
}
},
"required": ["product_id"]
}
},
{
"name": "buywhere_compare_prices",
"description": "Search for a product and return results sorted by price ascending "
"to compare prices across all Singapore e-commerce platforms.",
"input_schema": {
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "Product name or search query.",
"example": "Nintendo Switch OLED"
},
"category": {
"type": "string",
"description": "Optional category to narrow the search."
},
"min_price": {
"type": "number",
"description": "Minimum price in SGD."
},
"max_price": {
"type": "number",
"description": "Maximum price in SGD."
},
"limit": {
"type": "integer",
"description": "Max results (1-50, default 10).",
"default": 10
}
},
"required": ["query"]
}
},
{
"name": "buywhere_get_deals",
"description": "Find products currently priced below their original/historical price. "
"Returns deals sorted by discount percentage.",
"input_schema": {
"type": "object",
"properties": {
"category": {
"type": "string",
"description": "Optional category filter.",
"example": "Electronics"
},
"min_discount_pct": {
"type": "number",
"description": "Minimum discount percentage (0-100, default 10).",
"default": 10.0
},
"limit": {
"type": "integer",
"description": "Max results (1-100, default 20).",
"default": 20
}
}
}
},
{
"name": "buywhere_browse_categories",
"description": "Browse the BuyWhere category taxonomy tree. "
"Returns top-level categories when called with no arguments.",
"input_schema": {
"type": "object",
"properties": {}
}
},
{
"name": "buywhere_get_category_products",
"description": "Get paginated products within a specific category.",
"input_schema": {
"type": "object",
"properties": {
"category_id": {
"type": "string",
"description": "The category ID (e.g. 'electronics', 'mobile_phones').",
"example": "electronics"
},
"limit": {
"type": "integer",
"description": "Max results (1-100, default 20).",
"default": 20
},
"offset": {
"type": "integer",
"description": "Pagination offset (default 0).",
"default": 0
}
},
"required": ["category_id"]
}
}
]
3. Implement tool handlers
When Claude decides to call a tool, it returns stop_reason: "tool_use". You handle each tool call by invoking the BuyWhere API, then send the result back to Claude.
from buywhere_sdk import BuyWhere
bw = BuyWhere(api_key=os.environ["BUYWHERE_API_KEY"])
def handle_buywhere_tool(tool_name: str, tool_input: dict) -> str:
"""Handle a BuyWhere tool call. Returns a JSON string of the result."""
try:
if tool_name == "buywhere_search":
result = bw.search(
query=tool_input["query"],
category=tool_input.get("category"),
min_price=tool_input.get("min_price"),
max_price=tool_input.get("max_price"),
source=tool_input.get("source"),
limit=tool_input.get("limit", 20),
)
items = [
{
"id": p.id,
"name": p.name,
"price": float(p.price),
"currency": p.currency,
"source": p.source,
"url": p.affiliate_url or p.buy_url,
}
for p in result.items
]
return json.dumps({"total": result.total, "items": items}, indent=2)
elif tool_name == "buywhere_get_product":
product = bw.get_product(tool_input["product_id"])
return json.dumps({
"id": product.id,
"name": product.name,
"price": float(product.price),
"currency": product.currency,
"source": product.source,
"brand": product.brand,
"rating": float(product.rating) if product.rating else None,
"url": product.affiliate_url or product.buy_url,
}, indent=2)
elif tool_name == "buywhere_compare_prices":
result = bw.search(
query=tool_input["query"],
category=tool_input.get("category"),
min_price=tool_input.get("min_price"),
max_price=tool_input.get("max_price"),
limit=tool_input.get("limit", 10),
)
items = sorted(
[
{
"id": p.id,
"name": p.name,
"price": float(p.price),
"currency": p.currency,
"source": p.source,
}
for p in result.items
],
key=lambda x: x["price"]
)
return json.dumps({"total": result.total, "items": items}, indent=2)
elif tool_name == "buywhere_get_deals":
result = bw.get_deals(
category=tool_input.get("category"),
min_discount_pct=tool_input.get("min_discount_pct", 10.0),
limit=tool_input.get("limit", 20),
)
items = [
{
"id": p.id,
"name": p.name,
"price": float(p.price),
"original_price": float(p.original_price) if p.original_price else None,
"discount_pct": float(p.discount_pct) if p.discount_pct else None,
"currency": p.currency,
"source": p.source,
}
for p in result.items
]
return json.dumps({"total": result.total, "items": items}, indent=2)
elif tool_name == "buywhere_browse_categories":
cats = bw.list_categories()
return json.dumps({
"total": cats.total,
"categories": [
{"name": c.name, "count": c.count}
for c in cats.categories
]
}, indent=2)
elif tool_name == "buywhere_get_category_products":
# Use the SDK's category products method
result = bw.get_category_products(
category_id=tool_input["category_id"],
limit=tool_input.get("limit", 20),
offset=tool_input.get("offset", 0),
)
items = [
{
"id": p.id,
"name": p.name,
"price": float(p.price),
"currency": p.currency,
"source": p.source,
}
for p in result.items
]
return json.dumps({"total": result.total, "items": items}, indent=2)
else:
return json.dumps({"error": f"Unknown tool: {tool_name}"})
except bw.NotFoundError:
return json.dumps({"error": "Product not found"})
except bw.RateLimitError:
return json.dumps({"error": "Rate limit exceeded — retry later"})
except bw.AuthenticationError:
return json.dumps({"error": "Invalid BuyWhere API key"})
except Exception as e:
return json.dumps({"error": str(e)})
4. Run the agent loop
Claude continues the conversation until it returns stop_reason: "end_turn". Tools are called in a loop until all tool results are fed back.
import json
def run_claude_with_buywhere(user_message: str):
"""Send a message to Claude with BuyWhere tools available."""
messages = [{"role": "user", "content": user_message}]
while True:
response = client.messages.create(
model="claude-3-5-sonnet-20241022",
max_tokens=1024,
tools=BUYWHERE_TOOLS,
messages=messages,
)
# Collect tool use results to feed back
tool_results = []
stop_reason = response.stop_reason
if stop_reason == "tool_use":
for content_block in response.content:
if content_block.type == "tool_use":
tool_name = content_block.name
tool_input = content_block.input
tool_result = handle_buywhere_tool(tool_name, tool_input)
tool_results.append({
"type": "tool_result",
"tool_use_id": content_block.id,
"content": tool_result,
})
# Add Claude's response and tool results to conversation
messages.append({"role": "assistant", "content": response.content})
messages.extend(tool_results)
elif stop_reason == "end_turn":
# Claude is done — return final text response
return response.content[0].text
else:
return f"Unexpected stop_reason: {stop_reason}"
5. Full example
import os
import json
from anthropic import Anthropic
from buywhere_sdk import BuyWhere
# Initialize clients
client = Anthropic()
bw = BuyWhere(api_key=os.environ["BUYWHERE_API_KEY"])
# ... (paste BUYWHERE_TOOLS and handler functions from above)
if __name__ == "__main__":
user_prompt = (
"Find the cheapest Nintendo Switch OLED in Singapore and show me "
"how much I would save compared to the original price."
)
response = run_claude_with_buywhere(user_prompt)
print(response)
Example output:
The cheapest Nintendo Switch OLED I found is:
- Price: SGD 428.00 at
shopee_sg- Original price: SGD 499.00
- Savings: SGD 71.00 (14.2% off)
Buy here: https://buywhere.ai/track/...
Tool output format
When Claude calls a tool, the handle_buywhere_tool function returns a formatted JSON string. Structure each response to include the most useful fields for Claude to synthesize:
| Tool | Key response fields |
|---|---|
buywhere_search | total, items[].{id,name,price,currency,source,url} |
buywhere_get_product | id,name,price,currency,source,brand,rating,url |
buywhere_compare_prices | total, items[].{id,name,price,currency,source} sorted by price |
buywhere_get_deals | total, items[].{id,name,price,original_price,discount_pct,currency,source} |
buywhere_browse_categories | total, categories[].{name,count} |
buywhere_get_category_products | total, items[].{id,name,price,currency,source} |
Error handling
The handle_buywhere_tool function catches all errors and returns JSON error strings. This lets Claude gracefully handle failures:
except bw.NotFoundError:
return json.dumps({"error": "Product not found"})
except bw.RateLimitError:
return json.dumps({"error": "Rate limit exceeded — retry later"})
except bw.AuthenticationError:
return json.dumps({"error": "Invalid BuyWhere API key"})
Claude will typically mention these errors in its response to the user and may try alternative approaches (e.g., a different search query).
Rate limits
BuyWhere rate limits by API key tier:
| Tier | Requests/minute |
|---|---|
| Free | 60 |
| Basic | 600 |
| Pro | 6,000 |
If you hit a rate limit, bw.RateLimitError is raised. Implement exponential backoff in production:
import time
def handle_buywhere_tool_with_retry(tool_name: str, tool_input: dict, max_retries=3) -> str:
for attempt in range(max_retries):
try:
return handle_buywhere_tool(tool_name, tool_input)
except bw.RateLimitError:
if attempt < max_retries - 1:
time.sleep(2 ** attempt)
else:
return json.dumps({"error": "Rate limit exceeded after retries"})
Next steps
- Use BuyWhere MCP server for even simpler agent integration
- Browse the Python SDK docs for the full API reference
- See the LangChain integration guide for LangChain-based agents