V2 Search Products

Search products across the BuyWhere catalog with full-text search, filters, and regional targeting.

Base URL: https://api.buywhere.ai/v2/search

Overview

The V2 Search endpoint provides full-text search across millions of products from multiple Southeast Asian and US e-commerce platforms. It supports regional filtering, price range filtering, availability filtering, and currency conversion.

HTTP Method

GET /v2/search

Authentication

Requires API key in the Authorization header:

Authorization: Bearer bw_live_xxxxxxxxxxxxxxxx

Query Parameters

ParameterTypeRequiredDefaultDescription
qstringYes-Full-text search query (max 500 chars)
categorystringNo-Filter by category name (partial, case-insensitive)
platformstringNo-Filter by source platform (e.g., shopee_sg, lazada_sg)
min_pricenumberNo-Minimum price filter in SGD
max_pricenumberNo-Maximum price filter in SGD
countrystringNo-Filter by country code(s), comma-separated (e.g., SG,MY,US)
regionstringNoautoFilter by region: us, sg, sea
in_stockbooleanNo-Filter by availability
limitintegerNo20Results per page (1-100)
offsetintegerNo0Pagination offset
currencystringNoSGDResponse currency: SGD, MYR, THB, PHP, VND, USD

Request Example

cURL

curl -X GET "https://api.buywhere.ai/v2/search?q=laptop+gaming&category=electronics&min_price=500&max_price=3000&limit=10" \
  -H "Authorization: Bearer bw_live_xxxxxxxxxxxxxxxx"

Python

import httpx

API_KEY = "bw_live_xxxxxxxxxxxxxxxx"
BASE_URL = "https://api.buywhere.ai"

headers = {"Authorization": f"Bearer {API_KEY}"}

response = httpx.get(
    f"{BASE_URL}/v2/search",
    params={
        "q": "laptop gaming",
        "category": "electronics",
        "min_price": 500,
        "max_price": 3000,
        "limit": 10
    },
    headers=headers
)
data = response.json()
print(f"Found {data['total']} products")
for item in data['items']:
    print(f"  - {item['title']}: {item['currency']} {item['price']}")

JavaScript (Node.js)

const API_KEY = "bw_live_xxxxxxxxxxxxxxxx";
const BASE_URL = "https://api.buywhere.ai";

const response = await fetch(
  `${BASE_URL}/v2/search?q=laptop+gaming&category=electronics&min_price=500&max_price=3000&limit=10`,
  {
    headers: { "Authorization": `Bearer ${API_KEY}` }
  }
);

const data = await response.json();
console.log(`Found ${data.total} products`);
data.items.forEach(item => {
  console.log(`  - ${item.title}: ${item.currency} ${item.price}`);
});

Go

package main

import (
    "encoding/json"
    "fmt"
    "net/http"
    "net/url"
)

func searchProducts(apiKey, query string) error {
    baseURL, _ := url.Parse("https://api.buywhere.ai/v2/search")
    params := url.Values{}
    params.Set("q", query)
    params.Set("category", "electronics")
    params.Set("min_price", "500")
    params.Set("max_price", "3000")
    params.Set("limit", "10")
    baseURL.RawQuery = params.Encode()

    req, _ := http.NewRequest("GET", baseURL.String(), nil)
    req.Header.Set("Authorization", "Bearer "+apiKey)

    client := &http.Client{}
    resp, err := client.Do(req)
    if err != nil {
        return err
    }
    defer resp.Body.Close()

    var result map[string]interface{}
    json.NewDecoder(resp.Body).Decode(&result)
    
    total := int(result["total"].(float64))
    fmt.Printf("Found %d products\n", total)
    
    items := result["items"].([]interface{})
    for _, item := range items {
        m := item.(map[string]interface{})
        fmt.Printf("  - %s: %s %s\n", m["title"], m["currency"], m["price"])
    }
    return nil
}

Response

Success Response (200 OK)

{
  "query": "laptop gaming",
  "total": 1247,
  "limit": 10,
  "offset": 0,
  "has_more": true,
  "items": [
    {
      "id": 18472931,
      "sku": "ASUS-ROG-STRIX-G16",
      "source": "shopee_sg",
      "title": "ASUS ROG Strix G16 Gaming Laptop",
      "description": "Intel Core i9, 32GB RAM, RTX 4070, 1TB SSD",
      "price": "2499.00",
      "currency": "SGD",
      "buy_url": "https://shopee.sg/product/123456",
      "affiliate_url": "https://buywhere.ai/track/abc123",
      "image_url": "https://cf.shopee.sg/file/xxxxx",
      "brand": "ASUS",
      "category": "Computers & Laptops",
      "rating": 4.8,
      "review_count": 342,
      "is_available": true,
      "in_stock": true,
      "stock_level": null,
      "updated_at": "2026-04-15T10:30:00Z"
    }
  ]
}

Response Fields

FieldTypeDescription
querystringThe search query that was executed
totalintegerTotal number of matching products
limitintegerResults per page for this request
offsetintegerCurrent offset
has_morebooleanWhether more results are available
itemsarrayArray of product objects

Product Object Fields

FieldTypeDescription
idintegerUnique BuyWhere product ID
skustringProduct SKU from source platform
sourcestringSource platform identifier
titlestringProduct title
descriptionstringProduct description
pricestringPrice as decimal string
currencystringISO currency code
buy_urlstringDirect link to product on source
affiliate_urlstringBuyWhere tracked affiliate link
image_urlstringProduct image URL
brandstringBrand name
categorystringPrimary category
ratingfloatAverage rating (0-5)
review_countintegerNumber of reviews
is_availablebooleanCurrently in stock
in_stockbooleanStock status
stock_levelstringStock level indicator
updated_atdatetimeLast update timestamp

Error Responses

401 Unauthorized

{
  "error": {
    "code": "UNAUTHORIZED",
    "message": "Invalid or missing API key"
  }
}

422 Validation Error

{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Request validation failed",
    "details": {
      "errors": [
        {
          "field": "q",
          "message": "String exceeds maximum length of 500 characters",
          "type": "string_too_long"
        }
      ]
    }
  }
}

429 Rate Limited

{
  "error": {
    "code": "RATE_LIMIT_EXCEEDED",
    "message": "Rate limit exceeded",
    "details": {
      "retry_after": 60
    }
  }
}

Regional Filtering

The region parameter supports three values:

RegionCountriesDescription
usUSUnited States
sgSGSingapore
seaSG, MY, TH, PH, VN, IDSoutheast Asia

If not specified, the API attempts to detect the region from request headers.

Currency Conversion

Specify the currency parameter to receive prices in your preferred currency. Supported currencies:

  • SGD - Singapore Dollar (default)
  • MYR - Malaysian Ringgit
  • THB - Thai Baht
  • PHP - Philippine Peso
  • VND - Vietnamese Dong
  • USD - US Dollar

Caching

Search results are cached for 60 seconds to improve performance for repeated queries.

Client-side caching: Use Cache-Control: max-age=60 for browser caching.

Cache invalidation: Vary query parameters to get fresh results; identical queries return cached responses.

Performance Characteristics

Performance varies based on query complexity, result size, and load conditions:

ConditionExpected LatencyNotes
Baseline (simple query, 10 results)p50 < 50ms, p99 < 150msLow traffic, warm cache
Baseline (complex query, 50 results)p50 < 100ms, p99 < 300msFull-text search overhead
Moderate load (100 concurrent)p50 < 200ms, p99 < 500msTypical API usage
Heavy load (1000+ concurrent)p50 < 500ms, p99 < 1200msPeak traffic scenarios

Performance tips:

  • Use specific queries: Broader searches take longer
  • Limit results: Smaller result sets return faster
  • Cache aggressively: Search results are cacheable for 60s

Rate Limits

TierRequests/minuteNotes
Free60Default limit
Pro300Higher throughput
Enterprise1000+Custom limits

When rate limited (429):

{
  "error": {
    "code": "RATE_LIMIT_EXCEEDED",
    "message": "Rate limit exceeded",
    "details": {
      "retry_after": 32
    }
  }
}

Implement exponential backoff for retries:

import time
import random

def fetch_with_retry(url, headers, max_retries=3):
    for attempt in range(max_retries):
        response = requests.get(url, headers=headers)
        if response.status_code == 429:
            wait = int(response.headers.get("Retry-After", 60))
            time.sleep(wait * (2 ** attempt) + random.random())
            continue
        return response
    raise Exception("Max retries exceeded")

Related Endpoints