02 Get your data in /Integrations
Bazaarvoice
Send Bazaarvoice product reviews to Dimension Labs with the universal API.
- B BazaarvoiceTickets, chats, calls, reviews
- ⟳ Your jobPull new · map fields · every 15 min
- { } Universal APIPOST /track · /trackhistorical
- Dimension LabsRead in full · enriched daily
This brings in the product reviews in your Bazaarvoice client instance: each review as a customer message, with its rating and product, and your brand responses when there are any. One conversation per review. You run it on your side: a small script pulls from the Bazaarvoice Conversations API and posts each message to the universal API.
What you need
Section titled “What you need”- Bazaarvoice’s written approval to store review content. The Conversations API is built to display reviews on your own site and apps. Bazaarvoice’s API Terms of Use say you may not store data retrieved through it, and point to its Displayable Content Export, sold separately, for backend data management. Its rate-limit guide says using the Conversations API to build your own data store is limited to clients with prior approval. Sending reviews to Dimension Labs stores them, so get approval from your Bazaarvoice representative before you run this guide, or use the export described at the end of “Run it”.
- API access enabled for your client instance (if it isn’t, contact your Bazaarvoice representative), and a Portal account with the Technical Contributor or Technical Administrator role and the Ratings and Reviews solution.
- A Conversations API key (passkey), approved by a Technical Administrator.
- A key set up to exclude syndicated content, so it can list all your reviews without a product filter (see step 2). Bazaarvoice support can configure this.
- A Dimension Labs API key for a new Universal API source. See Your API key.
1. Get API access in Bazaarvoice
Section titled “1. Get API access in Bazaarvoice”Bazaarvoice’s steps are in API key management.
- Sign in to Bazaarvoice Portal and pick your client instance in the Instance Selector in the upper-right corner.
- Select Settings, then API under Developer.
- Select Request API key, choose Conversations API, select Next, and fill out the form. You receive the key details by email.
- Your Technical Administrator approves it: in the API Keys section, filter Key details > Status to Pending, open the key, select Enable Key, then Save.
- The passkey goes in the
passkeyquery parameter. Production ishttps://api.bazaarvoice.com; staging (https://stg.api.bazaarvoice.com) has data that doesn’t come from real users.
2. Pull new reviews
Section titled “2. Pull new reviews”curl -H "bv-version: 5.4.1" \ "https://api.bazaarvoice.com/data/reviews.json?apiversion=5.4&passkey=$BV_PASSKEY&Filter=LastModificationTime:gte:1759276800&Sort=LastModificationTime:asc&Limit=100&Offset=0"- All reviews: a request with no
ProductId,AuthorId,CategoryAncestorId,SubmissionIdor reviewIdfilter only works with a key configured to exclude syndicated content. Bazaarvoice recommends its Displayable Content Export for pulling everything, native and syndicated (see “Run it”). - Incremental:
Filter=LastModificationTime:gte:SECONDSreturns reviews changed since a time. Bazaarvoice’s time filters take seconds since 1970, UTC. Sort withSort=LastModificationTime:asc(a sort order is required). - Pagination:
Limit(up to 100, default 10) andOffset(up to 300,000). - Brand responses are in the same review, under
ClientResponses, so there is no second call. - Errors: by default Bazaarvoice returns
200 OKwith the error in the body. Thebv-version: 5.4.1header makes it return real4xxand5xxcodes. The code sends it, and also checksHasErrors. - Rate limits (Bazaarvoice’s): per key, per minute. Your key’s limit is in the Portal and in the
X-Bazaarvoice-QPM-Allottedheader;X-Bazaarvoice-Quota-Allottedis the hourly quota. Bazaarvoice says increases must be approved and may cost extra. - Bazaarvoice’s Terms of Use prohibit XML output: use
reviews.json.
3. Map the fields
Section titled “3. Map the fields”Each review is one incoming message under its own sessionId. Each brand response is an outgoing message in the same session.
| Dimension Labs | Bazaarvoice | Notes |
|---|---|---|
text | Title and ReviewText (incoming), ClientResponses[].Response (outgoing) | Join the title and body with a line break. Ratings-only reviews have no text and are skipped. |
userId | AuthorId | Prefixed bazaarvoice-. Don’t send UserNickname or UserLocation. |
incoming | true for the review, false for a brand response. | |
sessionId | Id | Prefixed bazaarvoice-. |
conversationId | Id | |
dimensionlabs_timestamp | SubmissionTime (review), ClientResponses[].Date (response) | Convert with toMillis. A response within a second of the review is moved a second later. |
platformJson | Rating, ProductId, IsRecommended, ContentLocale, IsSyndicated, IsFeatured, ModerationStatus, CampaignId, Pros, Cons | ProductId is the field to join to your product data. |
Personal data. Reviews carry the author’s nickname and location, and can carry context data such as age. Don’t send them: the code uses AuthorId instead. Reviews can still name people in their text: see PII redaction.
4. Send it
Section titled “4. Send it”// bazaarvoice.mjs. Environment: BV_PASSKEY, DL_API_KEY. Optional: BV_HOST (defaults to production).import { sendToDimensionLabs, toMillis } from './dimensionlabs.mjs';
const HOUR = 60 * 60 * 1000;const sleep = (ms) => new Promise((r) => setTimeout(r, ms));const HOST = process.env.BV_HOST ?? 'https://api.bazaarvoice.com';const MAX_OFFSET = 300000;const at = (d, fallback) => { try { return d ? toMillis(d) : fallback; } catch { return fallback; } };
// Retries Bazaarvoice's rate limits (429) and server errors (5xx), honoring Retry-After when it is sent.async function get(url) { for (let attempt = 0; ; attempt++) { const res = await fetch(url, { headers: { 'bv-version': '5.4.1', Accept: 'application/json' } }); if (res.ok) { const body = await res.json(); if (body.HasErrors) throw new Error(`Bazaarvoice returned errors: ${JSON.stringify(body.Errors)}`); return body; } if ((res.status !== 429 && res.status < 500) || attempt === 5) throw new Error(`Bazaarvoice returned ${res.status}: ${await res.text()}`); await sleep((Number(res.headers.get('retry-after')) || 2 ** attempt) * 1000); }}
async function send(r, historical) { const id = `bazaarvoice-${r.Id}`; const created = toMillis(r.SubmissionTime); const base = { userId: r.AuthorId ? `bazaarvoice-${r.AuthorId}` : id, sessionId: id, conversationId: id, platformJson: { rating: r.Rating, productId: r.ProductId, recommended: r.IsRecommended, locale: r.ContentLocale, syndicated: r.IsSyndicated, featured: r.IsFeatured, moderationStatus: r.ModerationStatus, campaignId: r.CampaignId, pros: r.Pros, cons: r.Cons } }; const text = [r.Title, r.ReviewText].filter((x) => x?.trim()).join('\n'); if (text) { await sendToDimensionLabs({ ...base, text, incoming: true, dimensionlabs_timestamp: created }, { historical: historical || created < Date.now() - HOUR }); } let last = created; for (const c of r.ClientResponses ?? []) { if (!c.Response?.trim()) continue; last = Math.max(at(c.Date, last), last + 1000); await sendToDimensionLabs({ ...base, text: c.Response.trim(), incoming: false, dimensionlabs_timestamp: last, platformJson: { ...base.platformJson, responseType: c.ResponseType, department: c.Department } }, { historical: historical || last < Date.now() - HOUR }); }}
// Sends every review changed at or after `since` (Unix ms). Returns the new `since`.export async function syncBazaarvoice({ since, historical = false }) { let newest = since, from = since; for (;;) { let restart = false; for (let offset = 0; ; offset += 100) { if (offset + 100 > MAX_OFFSET) { restart = true; break; } // past Bazaarvoice's offset limit: start again from the newest seen const q = new URLSearchParams({ apiversion: '5.4', passkey: process.env.BV_PASSKEY, Filter: `LastModificationTime:gte:${Math.floor(from / 1000)}`, Sort: 'LastModificationTime:asc', Limit: '100', Offset: String(offset) }); const { Results = [] } = await get(`${HOST}/data/reviews.json?${q}`); for (const r of Results) { newest = Math.max(newest, at(r.LastModificationTime, newest)); await send(r, historical); } if (Results.length < 100) break; } if (!restart || newest === from) return newest; from = newest; }}A review that changes (an edit, a new brand response, a moderation decision) comes back with a newer LastModificationTime and is sent again under the same sessionId. Anything unchanged is a duplicate, and is removed automatically. Comments other shoppers leave on a review are not sent; Bazaarvoice returns them with Include=Comments if you want to add them.
5. Run it
Section titled “5. Run it”Save the code as bazaarvoice.mjs in a project set up as in Set up your project, and run it with the scheduled sync runner: import { syncBazaarvoice as sync } from './bazaarvoice.mjs';.
- History first:
node run.mjs 2020-01-01sends every review changed since that date to/trackhistorical. Then start a backfill from the Backfill tab. - Then every 15 minutes: the scheduled job sends new reviews and responses to
/track, and they are enriched automatically every day. Anything more than an hour old when the job sees it goes to/trackhistorical, and needs a backfill. - Re-runs are safe: duplicates are removed automatically.
- Rate limits: a big history load can use up your per-minute or hourly allowance. The code waits and retries on
429; check theX-Bazaarvoice-QPM-Allottedheader to see your key’s limit.
If Bazaarvoice doesn’t approve the Conversations API for this, use its Displayable Content Export (DCE), which Bazaarvoice recommends for downloading all native and syndicated content and offers, for an added fee, for backend data management. It gives a full data set every Monday and an incremental one on the other days, using a passkey and a shared secret key from Bazaarvoice. Data sets are available about 24 hours after the DCE key is first approved. Convert the files to a CSV with the converter, check it with the file checker, upload it, then run a backfill.
Bazaarvoice reference
Section titled “Bazaarvoice reference”- Review Display
- Retrieve Reviews
- Platform and API concepts
- API key management
- API rate limit
- Displayable Content Export: Getting started
- API Terms of Use
Checked against Bazaarvoice’s documentation on 2026-10-26.