BuyWhere API Reference

Canonical v1 endpoint reference for the current BuyWhere product catalog API.

Authentication

Authenticated v1 endpoints expect an API key in the Authorization header as a bearer token.

curl --get "https://api.buywhere.ai/v1/products/search" \
  -H "Authorization: Bearer $BUYWHERE_API_KEY" \
  --data-urlencode "q=wireless headphones"

Notes:

  • The OpenAPI spec declares bearer auth globally.
  • The API middleware also accepts X-API-Key in some deployments for compatibility, but Authorization: Bearer ... is the canonical contract for v1 docs.
  • Public health endpoints are documented elsewhere and are not covered in this reference.

Rate Limits

The API enforces per-minute rate limits by API key tier and returns rate-limit headers on responses.

Current implementation defaults:

TierRequests/minuteDaily quota
free1001,000
basic1,0001,000
pro5,00050,000
enterprise20,000Unlimited

Response headers:

  • X-RateLimit-Limit
  • X-RateLimit-Remaining
  • X-RateLimit-Reset
  • X-RateLimit-Region
  • X-DailyQuota-Limit
  • X-DailyQuota-Remaining
  • X-DailyQuota-Reset
  • Retry-After on 429 Too Many Requests

Example:

X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 992
X-RateLimit-Reset: 1777087380
X-DailyQuota-Limit: 1000
X-DailyQuota-Remaining: 874

Common Conventions

Pagination

List endpoints in this v1 surface use offset pagination.

ParameterTypeDefaultRange
limitinteger201 to 100 on search, 1 to 1000 on price history
offsetinteger00 to 10000

List responses include:

  • total: total rows known for the current query
  • limit
  • offset
  • items or entries
  • has_more for product search

Currency

Search and product detail support an optional currency parameter for price conversion. The current OpenAPI spec lists:

AUD, CNY, EUR, GBP, HKD, IDR, JPY, MYR, PHP, SGD, THB, USD, VND

Converted values may appear in:

  • converted_price
  • converted_currency
  • price_sgd
  • usd_price

Market Filters

Search accepts both country and country_code. country_code is treated as an alias for country.

  • country: comma-separated ISO country codes such as SG,US
  • region: comma-separated region codes such as sg,us,sea

GET /v1/products/search

Full-text search over the BuyWhere product catalog with optional market, price, and platform filters.

Query Parameters

ParameterTypeRequiredDescription
qstringNoFull-text query, max 500 chars
categorystringNoPartial category filter
price_minnumberNoInclusive minimum price
price_maxnumberNoInclusive maximum price
platformstringNoSource/platform filter such as shopee_sg
countrystringNoComma-separated country codes
country_codestringNoAlias for country
regionstringNoComma-separated region codes
sort_bystringNorelevance, price_asc, price_desc, newest
limitintegerNoPage size, 1-100, default 20
offsetintegerNoPage offset, 0-10000, default 0
include_facetsbooleanNoInclude category/platform/brand/rating/price facet counts
currencystringNoConvert prices into a target currency

Search Behavior

  • If q is omitted, results default to the most recently updated products.
  • Category filtering uses case-insensitive partial matching.
  • platform is an exact source match.
  • When both price_min and price_max are present, price_min > price_max returns a validation error.
  • Facets are only computed when include_facets=true.

Response Shape

{
  "total": 248,
  "limit": 20,
  "offset": 0,
  "items": [
    {
      "id": 78234,
      "sku": "SHOPEE-SG-HEP-001",
      "source": "shopee_sg",
      "merchant_id": "shop_abc123",
      "name": "Sony WH-1000XM5 Wireless Headphones",
      "description": "Wireless noise-cancelling headphones",
      "price": "449.00",
      "currency": "SGD",
      "price_sgd": "449.00",
      "usd_price": "332.71",
      "converted_price": "332.71",
      "converted_currency": "USD",
      "buy_url": "https://shopee.sg/product/12345",
      "affiliate_url": "https://buywhere.ai/track/abc123",
      "image_url": "https://cdn.example.com/sony.jpg",
      "barcode": null,
      "brand": "Sony",
      "category": "Electronics",
      "category_path": ["Electronics", "Audio", "Headphones"],
      "rating": "4.8",
      "review_count": 1247,
      "avg_rating": "4.8",
      "rating_source": "scraped",
      "region": "sg",
      "country_code": "SG",
      "is_available": true,
      "in_stock": true,
      "stock_level": "high",
      "last_checked": "2026-04-24T18:10:00Z",
      "data_updated_at": "2026-04-24T18:00:00Z",
      "availability_prediction": "likely_in_stock",
      "competitor_count": 6,
      "confidence_score": 0.97,
      "specs": {
        "color": "Black"
      },
      "metadata": {},
      "updated_at": "2026-04-24T18:10:00Z",
      "price_trend": "stable"
    }
  ],
  "has_more": true,
  "next_cursor": null,
  "facets": null,
  "highlights": {
    "78234": "Sony WH-1000XM5 <b>Wireless</b> Headphones"
  }
}

Response Fields

Core fields returned in each item:

FieldTypeDescription
idintegerBuyWhere product ID
skustringSource SKU or normalized SKU
sourcestringPlatform/source key
merchant_idstringSource merchant identifier
namestringProduct title
descriptionstring or nullProduct description
pricestringCurrent native price as a decimal string
currencystringNative ISO currency code
price_sgdstring or nullPrice normalized to SGD
usd_pricestring or nullPrice normalized to USD
converted_pricestring or nullPrice in the requested currency
converted_currencystring or nullCurrency code for converted_price
buy_urlstringDirect merchant URL
affiliate_urlstring or nullBuyWhere tracked outbound URL
image_urlstring or nullProduct image URL
barcodestring or nullUPC/EAN when available
brandstring or nullBrand
categorystring or nullPrimary category
category_pathstring[] or nullHierarchical category path
ratingstring or nullRating value
review_countinteger or nullReview volume
avg_ratingstring or nullAggregated rating
rating_sourcestring or nullRating origin
regionstringRegion code
country_codestringISO country code
is_availablebooleanProduct availability flag
in_stockboolean or nullStock flag when available
stock_levelstring or nullStock bucket such as low, medium, high
last_checkeddatetime or nullLast availability check
data_updated_atdatetime or nullLast data refresh
availability_predictionstring or nullPredicted future availability
competitor_countinteger or nullCount of competing listings
confidence_scorenumber or nullSearch/result confidence
specsobject or nullArbitrary spec key-value pairs
metadataobject or nullExtended internal metadata
updated_atdatetimeLast record update
price_trendstring or nullup, down, or stable

Search envelope fields:

FieldTypeDescription
totalintegerTotal matches when known
total_countinteger or nullAlternate total field present in schema
limitintegerApplied page size
offsetintegerApplied offset
itemsarraySearch results
has_morebooleanWhether another page is likely available
next_cursorstring or nullReserved cursor field in the schema
facetsobject or nullFacet buckets when requested
highlightsobject or nullSearch highlights keyed by product ID

Example

curl --get "https://api.buywhere.ai/v1/products/search" \
  -H "Authorization: Bearer $BUYWHERE_API_KEY" \
  --data-urlencode "q=rice cooker" \
  --data-urlencode "country_code=SG" \
  --data-urlencode "platform=lazada_sg" \
  --data-urlencode "price_min=50" \
  --data-urlencode "price_max=300" \
  --data-urlencode "include_facets=true" \
  --data-urlencode "limit=10"

GET /v1/products/{product_id}

Return the canonical product record for a BuyWhere product ID.

Path Parameters

ParameterTypeRequiredDescription
product_idintegerYesBuyWhere product identifier

Query Parameters

ParameterTypeRequiredDescription
currencystringNoConvert returned prices to a target currency

Response

The endpoint returns a single ProductResponse object with the same field set documented in the search section.

{
  "id": 78234,
  "sku": "SHOPEE-SG-HEP-001",
  "source": "shopee_sg",
  "merchant_id": "shop_abc123",
  "name": "Sony WH-1000XM5 Wireless Headphones",
  "price": "449.00",
  "currency": "SGD",
  "converted_price": "332.71",
  "converted_currency": "USD",
  "buy_url": "https://shopee.sg/product/12345",
  "affiliate_url": "https://buywhere.ai/track/abc123",
  "brand": "Sony",
  "category": "Electronics",
  "region": "sg",
  "country_code": "SG",
  "is_available": true,
  "updated_at": "2026-04-24T18:10:00Z"
}

Example

curl "https://api.buywhere.ai/v1/products/78234?currency=USD" \
  -H "Authorization: Bearer $BUYWHERE_API_KEY"

GET /v1/products/{product_id}/price-history

Return historical price observations for a single product.

Path Parameters

ParameterTypeRequiredDescription
product_idintegerYesBuyWhere product identifier

Query Parameters

ParameterTypeRequiredDescription
daysintegerNoLookback window in days, 1-365, default 30
platformstringNoRestrict history to one source such as shopee_sg
limitintegerNoReturned observations, 1-1000, default 100
offsetintegerNoOffset into the matching history set

Time Range and Granularity

  • The current implementation returns raw observations from the price_history table within the requested days window.
  • Observations are sorted by scraped_at descending.
  • The schema also exposes aggregated_entries, aggregate, and period fields for aggregated time series, but the current router implementation does not accept an aggregation parameter on this endpoint. Treat raw entries as the supported contract today.

Response Shape

{
  "product_id": 78234,
  "entries": [
    {
      "price": "449.00",
      "currency": "SGD",
      "platform": "shopee_sg",
      "scraped_at": "2026-04-24T18:10:00Z"
    },
    {
      "price": "459.00",
      "currency": "SGD",
      "platform": "shopee_sg",
      "scraped_at": "2026-04-22T09:15:00Z"
    }
  ],
  "aggregated_entries": [],
  "total": 2,
  "aggregate": null,
  "period": null
}

Price History Fields

Raw entry fields:

FieldTypeDescription
pricestringObserved price
currencystringPrice currency
platformstringSource platform
scraped_atdatetimeObservation timestamp

Schema-defined aggregated entry fields:

FieldTypeDescription
datestringYYYY-MM-DD date bucket
min_pricestringLowest observed price for the bucket
max_pricestringHighest observed price for the bucket
avg_pricestringAverage price for the bucket
price_countintegerNumber of observations in the bucket
currencystringCurrency for the bucket
platformstring or nullPlatform when bucketed by source

Example

curl --get "https://api.buywhere.ai/v1/products/78234/price-history" \
  -H "Authorization: Bearer $BUYWHERE_API_KEY" \
  --data-urlencode "days=90" \
  --data-urlencode "platform=shopee_sg" \
  --data-urlencode "limit=200"

Errors

Common Status Codes

StatusWhen it happens
200Request succeeded
401Missing or invalid API key
404Product ID not found
422Validation error on a parameter or request shape
429Rate limit or quota exceeded
500Internal server error

Validation Error Shape

OpenAPI declares FastAPI validation errors through HTTPValidationError.

Typical shape:

{
  "detail": [
    {
      "loc": ["query", "limit"],
      "msg": "Input should be less than or equal to 100",
      "type": "less_than_equal"
    }
  ]
}

Runtime Error Shapes

Some endpoints also raise explicit JSON error payloads from router code. Example for an invalid search price range:

{
  "detail": {
    "code": "INVALID_PRICE_RANGE",
    "field": "price_min,price_max",
    "message": "price_min cannot be greater than price_max",
    "price_min": "500",
    "price_max": "100",
    "suggested_query": "price_min=100&price_max=100",
    "retry_after": null
  }
}

Typical not-found response:

{
  "detail": "Product not found"
}

Troubleshooting

401 Unauthorized

Check:

  • Authorization header is present
  • header format is exactly Bearer <api-key>
  • the API key is active

422 Validation Error

Common causes:

  • limit or offset outside allowed bounds
  • non-numeric price_min or price_max
  • q longer than 500 characters
  • price_min greater than price_max
  • non-integer product_id

Empty Search Results

Check:

  • whether platform, country, or region filters are too restrictive
  • whether the requested currency is only affecting price conversion rather than filtering
  • whether offset has paged beyond the available results

Sparse Price History

If price history returns few or zero observations:

  • expand days
  • remove the platform filter
  • confirm the product exists and is still active
  • do not rely on aggregated price history fields unless the endpoint adds an aggregation parameter in a future revision