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

  1. Use q (not query) for search — the parameter name is q to match PostgreSQL full-text search conventions.
  2. Prefer in_stock filter for purchase intent — when the user wants to buy something, filter in_stock: true to remove unavailable products.
  3. Use compare_prices after finding a product — once GPT has a product ID from search, it can call compare_prices to find the same product on other platforms.
  4. Chain get_categoriessearch_products — first discover available categories, then search within the right category.
  5. Use bulk_lookup for shopping lists — when the user provides multiple product names or SKUs, batch them into a single bulk_lookup call.
  6. Handle empty results gracefully — if a search returns 0 products, try broadening the query or removing price filters.
  7. Use affiliate_url for purchases — always prefer affiliate_url over buy_url to support BuyWhere attribution.

See Also