← Back to documentation

BuyWhere Price Widget Integration

v1Updated: April 19, 2026published

Embed real-time price comparison widgets into any website. Display product prices from multiple merchants with just a few lines of JavaScript.

BuyWhere Price Widget Integration

Embed real-time price comparison widgets into any website. Display product prices from multiple merchants with just a few lines of JavaScript.

Base URL: https://api.buywhere.ai
Widget Version: 1.0


Quick Start

1. Load the Widget

Option A: Script Tag (Recommended for static HTML)

<script src="https://cdn.buywhere.ai/widgets/price-widget@1.0.js" defer></script>

Option B: ES Module (For React/Vue/Next.js)

import PriceWidget from 'https://cdn.buywhere.ai/widgets/price-widget@1.0.mjs';

2. Add a Container

<div id="price-badge"></div>

3. Render the Widget

<script>
  PriceWidget.render('#price-badge', {
    query: 'Sony WH-1000XM5 Wireless Headphones',
    type: 'badge',
    country: 'SG'
  });
</script>

Result: Inline price badges showing the lowest prices across Shopee, Lazada, and other merchants.


Widget Types

Badge (type: 'badge')

Compact inline badges showing merchant name + price. Best for product listings, comparison tables, and inline placements.

PriceWidget.render('#my-badge', {
  query: 'iphone 15 pro 256gb',
  type: 'badge',
  maxPlatforms: 4,
  showPriceDiff: true
});

Visual:
![Badge Example]

  • Shows up to maxPlatforms merchant badges
  • Green/red dot indicates in-stock status
  • +N more appears when additional merchants exist
  • showPriceDiff: true displays price range

Card (type: 'card')

Full product card with lowest price, savings percentage, and CTA button. Best for product detail pages and featured deals.

PriceWidget.render('#my-card', {
  query: 'samsung galaxy s24 ultra',
  type: 'card',
  country: 'US'
});

Visual:
![Card Example]

  • Shows lowest price prominently
  • Stock status badge
  • Crossed-out highest price with savings %
  • "View Deal" CTA button

Comparison (type: 'comparison')

Full table of all merchants with price, stock, and rating. Best for dedicated comparison pages and detailed research views.

PriceWidget.render('#my-comparison', {
  query: 'macbook air m3',
  type: 'comparison',
  maxMerchants: 6,
  showRating: true
});

Visual:
![Comparison Example]

  • Sortable merchant table
  • Price, stock status, and rating columns
  • "View" links to each merchant

Configuration

Global Configuration

Create a custom loader to share settings across widgets:

const loader = new PriceWidget.Loader({
  apiKey: 'bw_live_your_key_here',
  baseUrl: '/api',           // Use your proxy or '/api' for BuyWhere proxy
  defaultCurrency: 'S$',
  defaultCountry: 'SG',
  cacheTimeout: 5 * 60 * 1000  // 5 minutes
});

// Render multiple widgets with the same loader
PriceWidget.render('#badge-1', { query: 'product a', type: 'badge', loader });
PriceWidget.render('#badge-2', { query: 'product b', type: 'badge', loader });

Render Options

OptionTypeDefaultDescription
querystringrequiredProduct search query
productIdnumberDirect product ID lookup (faster, exact match)
typestring'badge'Widget type: 'badge', 'card', 'comparison'
countrystring'SG'ISO country code: 'SG', 'MY', 'US', etc.
regionstringRegion override: 'sea', 'us', 'eu'
currencystringOverride display currency
loaderLoaderdefaultLoaderCustom PriceWidgetLoader instance
showLoadingbooleantrueShow skeleton loader while fetching
maxPlatformsnumber4Max badges to show (badge type)
maxMerchantsnumber6Max rows in table (comparison type)
showPriceDiffbooleanfalseShow price range (badge type)
showRatingbooleantrueShow merchant ratings (comparison type)
showHistorybooleanfalseShow price history (card type)

Installation

For React / Next.js

import { useEffect, useRef } from 'react';
import PriceWidget from '@/lib/price-widget';

export function PriceBadge({ query, country = 'SG' }) {
  const containerRef = useRef<HTMLDivElement>(null);

  useEffect(() => {
    if (containerRef.current) {
      PriceWidget.render(containerRef.current, {
        query,
        type: 'badge',
        country,
      });
    }
  }, [query, country]);

  return <div ref={containerRef} />;
}

For Vue 3

<template>
  <div ref="container"></div>
</template>

<script setup>
import { ref, onMounted } from 'vue';
import PriceWidget from '@/lib/price-widget';

const props = defineProps({
  query: { type: String, required: true },
  country: { type: String, default: 'SG' }
});

const container = ref(null);

onMounted(() => {
  PriceWidget.render(container.value, {
    query: props.query,
    type: 'badge',
    country: props.country
  });
});
</script>

For Vanilla JS (HTML pages)

<!DOCTYPE html>
<html>
<head>
  <title>Product Page</title>
  <script src="https://cdn.buywhere.ai/widgets/price-widget@1.0.js" defer></script>
</head>
<body>
  <h1>Sony WH-1000XM5</h1>
  <div id="price-widget"></div>

  <script>
    document.addEventListener('DOMContentLoaded', () => {
      PriceWidget.render('#price-widget', {
        query: 'Sony WH-1000XM5 Wireless Headphones',
        type: 'badge',
        country: 'SG'
      });
    });
  </script>
</body>
</html>

Styling

CSS Classes

The widget uses scoped CSS classes to avoid conflicts:

ElementCSS Class
Root container.bw-widget
Badge type.bw-widget-badge
Card type.bw-widget-card
Comparison type.bw-widget-comparison
Skeleton loader.bw-widget-skeleton

Custom Styling

Override styles with higher specificity:

/* Custom badge colors */
.bw-widget-badge a {
  border-radius: 4px;
  font-family: 'Inter', sans-serif;
}

/* Custom card styling */
.bw-widget-card {
  box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
  max-width: 320px;
}

/* Custom table styling */
.bw-widget-comparison table {
  font-size: 14px;
}

Platform Colors

The widget automatically applies platform-specific colors:

PlatformBackgroundText
Shopeebg-red-50text-red-600
Lazadabg-blue-50text-blue-600
Amazon.sgbg-orange-50text-orange-600
Amazon.combg-orange-50text-orange-600
Carousellbg-green-50text-green-600
Qoo10bg-purple-50text-purple-600
Walmartbg-blue-50text-blue-600
Best Buybg-blue-50text-blue-600
Targetbg-red-50text-red-600
eBaybg-indigo-50text-indigo-600

API Proxy (Recommended)

For production, proxy through your backend to protect your API key:

const loader = new PriceWidget.Loader({
  apiKey: 'bw_live_your_key_here',
  baseUrl: 'https://your-proxy.com/buywhere-api'
});

Proxy Requirements

Your proxy should:

  1. Forward Authorization header
  2. Pass through query parameters: query, country, region, productId
  3. Return the price data in the format below

Expected Response Format

{
  "productName": "Sony WH-1000XM5 Wireless Headphones",
  "prices": [
    {
      "merchant": "Shopee",
      "price": "349.00",
      "currency": "S$",
      "url": "https://shopee.sg/sony-wh1000xm5",
      "in_stock": true,
      "rating": 4.8,
      "last_updated": "2026-04-18T10:30:00Z"
    }
  ],
  "lowestPrice": {
    "merchant": "Shopee",
    "price": "349.00",
    "currency": "S$",
    "url": "https://shopee.sg/sony-wh1000xm5",
    "in_stock": true
  },
  "highestPrice": {
    "merchant": "Lazada",
    "price": "399.00",
    "currency": "S$",
    "url": "https://lazada.sg/sony-wh1000xm5",
    "in_stock": true
  },
  "priceDiff": "50.00"
}

Caching

The widget includes built-in caching to reduce API calls:

const loader = new PriceWidget.Loader({
  cacheTimeout: 5 * 60 * 1000  // 5 minutes (default)
});

// Clear cache manually
loader.clearCache();

// Per-render cache busting
PriceWidget.render('#widget', {
  query: 'product',
  // Cache is keyed by query + options
});

Cache key format: price-{query}-{stringifiedOptions}


Error Handling

The widget gracefully handles errors:

PriceWidget.render('#widget', {
  query: 'some product',
  type: 'badge'
}).catch(error => {
  console.error('Widget failed:', error);
});

Error States

ScenarioBehavior
Network errorFalls back to mock data in development; shows error message in production
No resultsShows "No products found" message
API errorLogs error, shows error state in widget

Mock Data (Development)

In development or when the API is unreachable, the widget generates realistic mock data so you can build UI without a live connection.


Accessibility

The widget follows accessibility best practices:

  • All interactive elements are keyboard navigable
  • Links have proper rel="noopener noreferrer" for external URLs
  • Stock status uses color + icon (not color alone)
  • Skeleton loaders use aria-busy="true"

Supported Countries

CountryCodeCurrencyMerchants
SingaporeSGS$Shopee, Lazada, Amazon.sg, Carousell, Qoo10
MalaysiaMYRMShopee, Lazada, Amazon.my
United StatesUS$Amazon.com, Walmart, Target, Best Buy, eBay
ThailandTH฿Shopee, Lazada
PhilippinesPHShopee, Lazada
VietnamVNShopee, Lazada

US Price Comparison API

The widget uses BuyWhere's price comparison API to fetch real-time pricing from US merchants.

Endpoint

GET /api/prices

Parameters

ParameterTypeRequiredDescription
querystringYesProduct search query
countrystringYesISO country code (US for United States)
regionstringNoRegion override (us for US region)
productIdnumberNoDirect product ID lookup (exact match)

Headers

HeaderRequiredDescription
AuthorizationYesBearer <your_api_key>
Content-TypeYesapplication/json

US-Specific Features

Target.com Integration The widget supports Target.com as a US merchant. Target products include:

  • Fashion & Apparel
  • Home Goods & Decor
  • Beauty & Personal Care
  • Electronics

US Merchant Response Format

{
  "productName": "Apple AirPods Pro (2nd Generation)",
  "prices": [
    {
      "merchant": "Amazon.com",
      "price": "249.00",
      "currency": "$",
      "url": "https://amazon.com/dp/XXXXX",
      "in_stock": true,
      "rating": 4.7,
      "last_updated": "2026-04-18T10:30:00Z"
    },
    {
      "merchant": "Walmart",
      "price": "229.00",
      "currency": "$",
      "url": "https://walmart.com/ip/XXXXX",
      "in_stock": true,
      "rating": 4.5,
      "last_updated": "2026-04-18T10:30:00Z"
    },
    {
      "merchant": "Target",
      "price": "249.99",
      "currency": "$",
      "url": "https://target.com/p/XXXXX",
      "in_stock": true,
      "rating": 4.6,
      "last_updated": "2026-04-18T10:30:00Z"
    },
    {
      "merchant": "Best Buy",
      "price": "249.99",
      "currency": "$",
      "url": "https://bestbuy.com/site/XXXXX",
      "in_stock": true,
      "rating": 4.4,
      "last_updated": "2026-04-18T10:30:00Z"
    },
    {
      "merchant": "eBay",
      "price": "199.00",
      "currency": "$",
      "url": "https://ebay.com/itm/XXXXX",
      "in_stock": true,
      "rating": 4.2,
      "last_updated": "2026-04-18T10:30:00Z"
    }
  ],
  "lowestPrice": {
    "merchant": "eBay",
    "price": "199.00",
    "currency": "$",
    "url": "https://ebay.com/itm/XXXXX",
    "in_stock": true
  },
  "highestPrice": {
    "merchant": "Target",
    "price": "249.99",
    "currency": "$",
    "url": "https://target.com/p/XXXXX",
    "in_stock": true
  },
  "priceDiff": "50.99"
}

API Key Setup

  1. Sign up at buywhere.ai for an API key
  2. Configure the loader with your key:
const loader = new PriceWidget.Loader({
  apiKey: 'bw_live_your_key_here',
  baseUrl: 'https://api.buywhere.ai'
});

// US-specific rendering
PriceWidget.render('#widget', {
  query: 'macbook air m3',
  type: 'comparison',
  country: 'US',
  loader
});

Rate Limits

PlanRequests/minuteDaily Limit
Free101,000
Pro6050,000
Enterprise600Unlimited

Platform Integrations

WordPress

Method 1: Custom HTML Block (Gutenberg)

Add a custom HTML block and paste:

<div class="price-widget-container">
  <h3>Compare Prices</h3>
  <div id="bw-price-badge"></div>
</div>

<script src="https://cdn.buywhere.ai/widgets/price-widget@1.0.js" defer></script>
<script>
  document.addEventListener('DOMContentLoaded', function() {
    PriceWidget.render('#bw-price-badge', {
      query: 'YOUR_PRODUCT_NAME',
      type: 'badge',
      country: 'US'
    });
  });
</script>

Method 2: Theme Template File

Add to your theme's functions.php:

function buywhere_price_widget_shortcode($atts) {
    $atts = shortcode_atts(array(
        'query' => '',
        'type' => 'badge',
        'country' => 'US'
    ), $atts, 'buywhere_price');
    
    $uniqid = 'bw-' . uniqid();
    ob_start();
    ?>
    <div id="<?php echo $uniqid; ?>"></div>
    <script src="https://cdn.buywhere.ai/widgets/price-widget@1.0.js" defer></script>
    <script>
      document.addEventListener('DOMContentLoaded', function() {
        PriceWidget.render('#<?php echo $uniqid; ?>', {
          query: <?php echo json_encode($atts['query']); ?>,
          type: <?php echo json_encode($atts['type']); ?>,
          country: <?php echo json_encode($atts['country']); ?>
        });
      });
    </script>
    <?php
    return ob_get_clean();
}
add_shortcode('buywhere_price', 'buywhere_price_widget_shortcode');

Usage in WordPress editor: [buywhere_price query="iphone 15 pro" type="badge" country="US"]


Shopify

Method 1: Custom Liquid Section

Create a new section file sections/buywhere-price.liquid:

{%- if product -%}
<div class="buywhere-price-widget" data-product="{{ product.title | escape }}">
  <div id="bw-{{ section.id }}"></div>
</div>

<script src="https://cdn.buywhere.ai/widgets/price-widget@1.0.js" defer></script>
<script>
  document.addEventListener('DOMContentLoaded', function() {
    PriceWidget.render('#bw-{{ section.id }}', {
      query: {{ product.title | json }},
      type: '{{ section.settings.widget_type }}',
      country: '{{ section.settings.country }}'
    });
  });
</script>
{%- endif -%}

{% schema %}
{
  "name": "BuyWhere Price",
  "settings": [
    {
      "type": "select",
      "id": "widget_type",
      "label": "Widget Type",
      "options": [
        { "value": "badge", "label": "Badge" },
        { "value": "card", "label": "Card" },
        { "value": "comparison", "label": "Comparison" }
      ]
    },
    {
      "type": "select",
      "id": "country",
      "label": "Country",
      "options": [
        { "value": "SG", "label": "Singapore" },
        { "value": "US", "label": "United States" },
        { "value": "MY", "label": "Malaysia" }
      ]
    }
  ],
  "presets": [
    {
      "name": "BuyWhere Price"
    }
  ]
}
{% endschema %}

Method 2: Product Page Snippet

Add to snippets/product-price-comparison.liquid:

{%- if product -%}
<div class="buywhere-price-section">
  <p class="buywhere-label">Compare prices:</p>
  <div id="buywhere-price-{{ product.id }}" data-product="{{ product.title | escape }}"></div>
</div>

<style>
  .buywhere-price-section {
    margin: 1.5rem 0;
    padding: 1rem;
    background: #f9f9f9;
    border-radius: 8px;
  }
  .buywhere-label {
    font-weight: 600;
    margin-bottom: 0.5rem;
  }
</style>

<script src="https://cdn.buywhere.ai/widgets/price-widget@1.0.js" defer></script>
<script>
  document.addEventListener('DOMContentLoaded', function() {
    PriceWidget.render('#buywhere-price-{{ product.id }}', {
      query: {{ product.title | json }},
      type: 'badge',
      country: 'US'
    });
  });
</script>
{%- endif -%}

Include in your product template: {% render 'product-price-comparison' %}


Squarespace

Method 1: Code Block (Any Page)

Add a Code Block to your page and paste:

<div id="buywhere-widget"></div>

<script src="https://cdn.buywhere.ai/widgets/price-widget@1.0.js" defer></script>
<script>
  document.addEventListener('DOMContentLoaded', function() {
    PriceWidget.render('#buywhere-widget', {
      query: 'YOUR_PRODUCT_NAME',
      type: 'badge',
      country: 'US'
    });
  });
</script>

Method 2: Squarespace Developer Platform (Developer Mode)

Add to your page.conf files collection:

// In your page's JSON template
{
  "title": "Product Page",
  "widgets": [
    {
      "type": "custom",
      "html": "<div id='buywhere-price-widget'></div>"
    }
  ]
}

Then add the script to Settings > Advanced > Code Injection > Footer:

<script src="https://cdn.buywhere.ai/widgets/price-widget@1.0.js" defer></script>
<script>
  document.addEventListener('DOMContentLoaded', function() {
    // Auto-detect product name from page title
    var productName = document.querySelector('.ProductItem-details h1')?.textContent || '';
    if (productName && document.getElementById('buywhere-price-widget')) {
      PriceWidget.render('#buywhere-price-widget', {
        query: productName.trim(),
        type: 'badge',
        country: 'US'
      });
    }
  });
</script>

Wix

Using Wix Editor:

  1. Add an Embed element to your page
  2. Set to "Code" mode
  3. Paste:
<div id="buywhere-price-widget"></div>

<script src="https://cdn.buywhere.ai/widgets/price-widget@1.0.js" defer></script>
<script>
  document.addEventListener('DOMContentLoaded', function() {
    PriceWidget.render('#buywhere-price-widget', {
      query: 'YOUR_PRODUCT_NAME',
      type: 'badge',
      country: 'US'
    });
  });
</script>

BigCommerce

Method 1: Storefront Global Footer

Go to Storefront > Footer Scripts and add:

<script src="https://cdn.buywhere.ai/widgets/price-widget@1.0.js" defer></script>
<script>
  // Auto-initialize on product pages
  document.addEventListener('DOMContentLoaded', function() {
    if (document.querySelector('.product-title')) {
      var productName = document.querySelector('.product-title').textContent;
      var widgetContainer = document.createElement('div');
      widgetContainer.id = 'buywhere-price-widget';
      document.querySelector('.product-title').after(widgetContainer);
      
      PriceWidget.render('#buywhere-price-widget', {
        query: productName.trim(),
        type: 'card',
        country: 'US'
      });
    }
  });
</script>

Method 2: Custom Template

Add to your Stencil theme's templates/pages/product.html:

<div class="buywhere-price-comparison">
  <h3>Compare Prices</h3>
  <div id="bw-price-widget"></div>
</div>

<script>
  var productName = {{ product.title | json }};
  PriceWidget.render('#bw-price-widget', {
    query: productName,
    type: 'comparison',
    country: 'US'
  });
</script>

Example Implementations

E-commerce Product Page

<div class="product-container">
  <h1>Sony WH-1000XM5</h1>
  <p>Premium wireless headphones with noise cancellation</p>
  
  <div id="price-comparison"></div>
  
  <script>
    PriceWidget.render('#price-comparison', {
      query: 'Sony WH-1000XM5 Wireless Headphones',
      type: 'comparison',
      country: 'SG',
      maxMerchants: 5
    });
  </script>
</div>

Blog Post Price Alert

<aside class="price-sidebar">
  <h3>Check Prices</h3>
  <div id="price-card"></div>
</aside>

<script>
  PriceWidget.render('#price-card', {
    query: 'iphone 15 pro',
    type: 'card',
    country: 'US'
  });
</script>

Comparison Table Row

<table>
  <tr>
    <td>Sony WH-1000XM5</td>
    <td><div id="price-1"></div></td>
  </tr>
  <tr>
    <td>Apple AirPods Max</td>
    <td><div id="price-2"></div></td>
  </tr>
</table>

<script>
  ['Sony WH-1000XM5', 'Apple AirPods Max'].forEach((query, i) => {
    PriceWidget.render(`#price-${i + 1}`, {
      query,
      type: 'badge',
      country: 'SG',
      maxPlatforms: 3
    });
  });
</script>

Troubleshooting

Widget not appearing

  1. Check the container element exists in the DOM
  2. Verify the script loaded successfully
  3. Check browser console for errors
  4. Ensure query is not empty

"Failed to load price data" error

  1. Verify your API key is valid
  2. Check network tab for failed requests
  3. Ensure CORS is configured if using a proxy

Wrong products shown

  1. Use productId instead of query for exact matches
  2. Try more specific search terms
  3. Check country/region settings match your target market

Styling conflicts

  1. Use more specific CSS selectors
  2. Wrap widget in a container with unique class
  3. Use !important sparingly for overrides

Custom Widgets

The Price Widget supports custom widget registration for specialized display requirements.

Registering a Custom Widget

PriceWidget.registerWidgetType('myWidget', {
  render: (container, priceData, options) => {
    // container: DOM element to render into
    // priceData: { productName, prices[], lowestPrice, highestPrice, priceDiff }
    // options: render options passed to PriceWidget.render()
    
    const el = document.createElement('div');
    el.className = 'bw-widget bw-widget-custom';
    el.innerHTML = `
      <h4>${priceData.productName}</h4>
      <p>Lowest: ${priceData.lowestPrice.price} at ${priceData.lowestPrice.merchant}</p>
    `;
    container.appendChild(el);
  }
});

// Use your custom widget
PriceWidget.render('#container', {
  query: 'Sony WH-1000XM5',
  type: 'myWidget'
});

Listing Registered Widgets

const widgets = PriceWidget.getRegisteredWidgets();
// Returns: [['myWidget', { render: fn }], ...]

Utility Functions

The widget exports helper functions for advanced integrations:

formatPrice

const { formatPrice } = PriceWidget;

formatPrice(349.00, 'S$');  // "S$ 349.00"
formatPrice(99.99, '$');    // "$ 99.99"

Element Creation Functions

Create widget elements directly without rendering:

const { createPriceBadgeElement, createPriceCardElement, createPriceComparisonElement, createSkeletonLoader } = PriceWidget;

const badge = createPriceBadgeElement(priceData, { maxPlatforms: 3 });
const card = createPriceCardElement(priceData);
const comparison = createPriceComparisonElement(priceData, { maxMerchants: 8, showRating: true });
const skeleton = createSkeletonLoader('card');

document.getElementById('container').appendChild(badge);

Loader Utilities

Access mock data and merchant lists directly:

const loader = new PriceWidget.Loader();

// Generate mock data for development
const mockData = loader.getMockPriceData('Sony WH-1000XM5', { country: 'SG' });

// Get merchant list for a region
const seaMerchants = loader.getMerchantList('sea');  // ['Shopee', 'Lazada', ...]
const usMerchants = loader.getMerchantList('us');     // ['Amazon.com', 'Walmart', ...]

TypeScript Support

Type Definitions

interface PriceData {
  productName: string;
  prices: PriceItem[];
  lowestPrice: PriceItem;
  highestPrice: PriceItem;
  priceDiff: string;
}

interface PriceItem {
  merchant: string;
  price: string;
  currency: string;
  url: string;
  in_stock: boolean;
  rating?: number;
  last_updated: string;
}

interface WidgetOptions {
  query: string;
  productId?: number;
  type?: 'badge' | 'card' | 'comparison' | string;
  country?: string;
  region?: string;
  currency?: string;
  loader?: PriceWidgetLoader;
  showLoading?: boolean;
  maxPlatforms?: number;
  maxMerchants?: number;
  showPriceDiff?: boolean;
  showRating?: boolean;
  showHistory?: boolean;
}

React TypeScript Example

import { useEffect, useRef } from 'react';
import PriceWidget, { PriceData, WidgetOptions } from '@/lib/price-widget';

interface PriceBadgeProps {
  query: string;
  country?: string;
  className?: string;
}

export function PriceBadge({ query, country = 'SG', className }: PriceBadgeProps) {
  const containerRef = useRef<HTMLDivElement>(null);

  useEffect(() => {
    if (!containerRef.current) return;

    let mounted = true;
    const container = containerRef.current;

    PriceWidget.render(container, {
      query,
      type: 'badge',
      country,
      className,
    }).then(() => {
      if (!mounted) {
        container.innerHTML = '';
      }
    });

    return () => {
      mounted = false;
    };
  }, [query, country, className]);

  return <div ref={containerRef} />;
}

Support