← Back to documentation

claude_tools_guide

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:

ToolWhat it does
buywhere_searchFull-text product search across Singapore e-commerce
buywhere_get_productRetrieve a product by ID
buywhere_compare_pricesFind and sort product matches by price
buywhere_get_dealsFind discounted products
buywhere_browse_categoriesBrowse the category taxonomy
buywhere_get_category_productsGet products in a category

Prerequisites

  • Python 3.10+
  • anthropic Python 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:

ToolKey response fields
buywhere_searchtotal, items[].{id,name,price,currency,source,url}
buywhere_get_productid,name,price,currency,source,brand,rating,url
buywhere_compare_pricestotal, items[].{id,name,price,currency,source} sorted by price
buywhere_get_dealstotal, items[].{id,name,price,original_price,discount_pct,currency,source}
buywhere_browse_categoriestotal, categories[].{name,count}
buywhere_get_category_productstotal, 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:

TierRequests/minute
Free60
Basic600
Pro6,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