Getting Started with Authentic Discovery API

This sections helps you find the best approach to implement the Authentic Discovery API.

Use the table below to identify the correct Authentic DiscoveryTM API approach for your site. Implementing duplicate markup can confuse search engines and negatively impact your SEO performance. It is highly recommended that you consult with Bazaarvoice Support to ensure a clean JSON-LD integration and avoid SEO conflicts.

Current Client SetupWhat to DOWhat NOT to Do
Bazaarvoice Hosted Display (using Bazaarvoice JavaScript widget for reviews, no custom schema)Integrate the server-side API request specifically for automated crawlers. Ensure you include data-bv-seo="false" within your Bazaarvoice container elements; this prevents the client-side JavaScript from generating redundant structured data when a bot is detected.Do not serve the API response to human visitors. Do not leave data-bv-seo unset - you'll get duplicate schema.
Bazaarvoice API Display (call Bazaarvoice API yourself and render reviews in your own UI)Add the Authentic Discovery API call for bots. Remove or replace any existing server-side review/rating JSON-LD you were generating yourself.Do not retain old hand-crafted AggregateRating or Review JSON-LD blocks — the API will now supply these. Duplicates cause SEO penalties.
BVSEO SDK (older Bazaarvoice server-side SEO solution)Remove the BVSEO SDK entirely. Replace it with the Authentic Discovery API pattern described in this guide.Do not run both simultaneously.
No existing SEO schemaStart fresh with the implementation guide. Simplest path.Nothing to clean up — just follow the steps in the implementation guide.

Before You Start

Confirm all three of these first — most implementation problems trace back to a missing item here.

  1. An eligible Bazaarvoice account on the current Conversations platform (the legacy PRR platform is not supported).
  2. Your exact Client ID (check with your Bazaarvoice account team if unsure).
  3. Product IDs that match your Bazaarvoice catalog. A mismatch returns empty or incorrect markup — this is the single most common cause of "it’s not working".

Step 1 — Create an Authentic Discovery API key

  1. Create an Authentic Discovery API key reserved for GEO/SEO use.
  2. Sign in to the Bazaarvoice Portal.
  3. Select the correct client instance from the Instance Selector (upper-right).
  4. Go to Settings ⚙ → API, then click Request API key.
  5. Under Product Selection, select the Authentic Discovery API and follow the next steps.
  6. Submit the form. The key arrives by email.
  7. Ask your Technical Administrator to approve the key. It will not work until they do.
📘

You’ll use this key as the Bv-Passkey header in Step 2.

Step 2 — Call the API from your server

When a product page is requested, have your server call the API for that product and retrieve the structured data. Start on staging so you can test safely before touching production.

Endpoint

You must pass your Bv-Passkey header, a productId, and a locale (format languageCode_countryCode, e.g. en_US). Try it first with a single product:

curl --location \
  'https://seo-stg.bazaarvoice.com/structured-data/v1/clients/YOUR-CLIENT-ID/ugc?locale=en_US&productId=YOUR-PRODUCT-ID' \
  --header 'Accept: text/javascript' \
  --header 'Bv-Passkey: YOUR-API-KEY'

The server responds with a block of markup that already contains the block and a bv-geo-jsonld-reviews-data block.
In your application, the equivalent server-side call looks like this:

const clientId = 'your-client-id';
const productId = 'product-123';
const apiKey = process.env.BV_API_KEY;
 
const url = `https://seo.bazaarvoice.com/structured-data/v1/clients/`
  + `${clientId}/ugc?locale=en_US&productId=${productId}&siteId=main_site`;
 
const response = await fetch(url, { headers: { 'Bv-Passkey': apiKey } });
const structuredData = await response.text();
📘

NOTE:

By default the call runs on every product-page request. That is perfectly fine to start with. If you later want to reduce the number of calls, see Advanced Configuration below.

⚠️

NOTE:

If you get an empty body, the productId almost certainly doesn’t match your catalog. A 401 means the key isn’t approved yet or isn’t an Authentic Discovery API key. Fix either before moving on.

Step 3 — Embed the response in your page

Insert the response into the of the product page, exactly as received — no modification. It is present in the initial HTML, so crawlers read it before any JavaScript runs.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Product Name - Your Site</title>
 
  <!-- Paste the Authentic Discovery API response here, unmodified -->
  <%= bazaarvoiceStructuredData %>
 
</head>
<body>
  <!-- Your existing Bazaarvoice display containers stay exactly as they are -->
  <div data-bv-show="reviews" data-bv-product-id="YOUR-PRODUCT-ID"></div>
</body>
</html>

Your visible reviews still render the same way, from the same JavaScript, for human visitors. You are only adding a crawler-readable layer alongside them.

Step 4 — Prevent duplicate markup (if you already have SEO)

If you already ship review markup client-side, you’ll now have it twice — which can hurt search performance. Match your current setup:

If your current setup is…Then do this
Hosted Display SEO (client-side)Set data-bv-seo="false" on every Bazaarvoice display div. Keep Hosted Display for the visuals.
Server-side SEO (your own schema)Deliver all your JSON-LD schemas (yours + Bazaarvoice) together from the server. Keep your existing display for visuals.
No SEO todayNothing extra to do — just embed the API’s JSON-LD from Step 3.
BVSEO SDKRemove the BVSEO SDK, then embed the API’s JSON-LD from Step 3.

Step 5 — Verify it works

Confirm a crawler can see the structured data on a live (or staging) product page.

  1. Fetch the raw HTML and confirm the tags are present:
    curl -L -o output.html 'YOUR-PDP-URL'
    # then search output.html for  bv-geo-
  2. Validate the markup: paste your PDP URL (or the raw HTML) into the Google Rich Results Test and confirm zero errors.

If you see the bv-geo- tags with valid, non-empty data and no errors, you’re done — your ratings and reviews are now readable by AI crawlers and search engines.

📘

When an integration looks "clean but broken", 90% of the time it’s

(a) a product-ID mismatch,
(b) an unapproved or non-Authentic Discovery API key, or
(c) leftover client-side SEO creating duplicate markup. Check those three first.

Advanced Configuration

The steps above give you a complete, working implementation. The options below are optional refinements — reach for them once the basics are in place.

Reduce the number of API calls

By default your server calls the API on every product-page request. On high-traffic sites that can be more calls than you need. There are two ways to cut the volume — you can use either or both.

Option A — Cache the response (recommended first)

UGC changes relatively slowly, so the simplest and most robust way to reduce calls is to cache the API response per product and reuse it for a set period. Because the structured data comes from your server, you can cache it wherever you already cache page data — an in-memory store, a CDN, or your application cache.

  • Cache the response keyed by productId + locale (+ siteId if you use multiple sites).
  • Choose a refresh interval that fits how quickly reviews change on your catalog — many clients find a few hours to a day works well. Shorter intervals mean fresher data; longer intervals mean fewer calls.
  • On a cache miss or expiry, call the API, embed the fresh response, and re-cache it.
🟩

Tip:

Caching keeps the data fresh for all visitors and crawlers while collapsing many requests into one periodic call — usually the biggest reduction for the least complexity.

Option B — Only call the API for crawlers (User-Agent filtering)

If you want to skip the call entirely for ordinary human traffic, you can trigger it only when the request comes from a known search engine or AI crawler. This is useful when the structured data is genuinely only needed for crawlers and you’d rather not generate or cache it for every human visit.

  1. When a request hits your server, read the User-Agent header.
  2. Compare it against a list of known search-engine and AI-crawler identifiers (for example Googlebot, GPTBot, ClaudeBot, Amazonbot).
  3. If it matches, call the API and embed the response as in Steps 2–3.
  4. If it doesn’t match, serve the page normally and skip the call — the Bazaarvoice JavaScript still renders the visible reviews for humans.

As best practice, forward the original crawler’s User-Agent so Bazaarvoice can report which bots crawl you:

const ua = request.headers['user-agent'];
if (isKnownCrawler(ua)) {
  const response = await fetch(url, {
    headers: {
      'Bv-Passkey': apiKey,
      'User-Agent': ua,
      'Bv-Forwarded-User-Agent': ua   // lets BV track which bots crawl you
    }
  });
  const structuredData = await response.text();
  // embed structuredData in the <head>
}
⚠️

This is not a way to block malicious bots. Handle bad bots earlier in your stack with robots.txt or a WAF, before the request reaches your product pages. User-Agent filtering here is only about which legitimate requests trigger the API.

🟩

Tip:

The two options combine well: filter so only crawlers trigger the call, and cache the result so repeat crawler visits reuse it. Caching alone (Option A) is the safer default if you’re unsure, because it doesn’t depend on maintaining a crawler list.

Customize What the API Returns

By default the API returns ratings + reviews. Pass a q query parameter (a JSON object) to change that. Supported keys: reviews, ratings, questions, reviewSummary.

Add the AI Review Summary:

&q={"reviews":{},"ratings":{},"reviewSummary":{}}

Add Q&A:

&q={"reviews":{},"ratings":{},"questions":{}}

Filter, limit, offset, or sort a schema (same conventions as the Conversations API)

&q={"reviews":{"filter":["productid:eq:product1"],"limit":"1","sort":"submissiontime:desc"}}
📘

Note:

For the full list of sub-parameters and limits (e.g. limit max 100), please refer the API Reference.

Link reviews to your Product Schema

To make Google merge your product details and Bazaarvoice reviews into a single rich result, give them a shared @id "hook".

  1. Make sure the page already has a standard Product schema.

  2. Pass your product’s canonical URL in the canonical query parameter — Bazaarvoice writes it into the returned schema’s @id.

  3. Make the @id in your Product schema match that canonical value character-for-character.

    A reliable format is the canonical URL plus a #product fragment,

    Example: https://www.example.com/products/running-shoe#product.




Did this page help you?