02 Get your data in /Integrations
Brandwatch Consumer Research
Send the mentions matched by a Brandwatch Consumer Research Query to Dimension Labs with the universal API.
- BC Brandwatch Consumer ResearchTickets, 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 mentions your Brandwatch Consumer Research Query already collects: forum and blog posts, reviews, news and social posts about your brand, each one as a customer message with Brandwatch’s sentiment and categories alongside. You run it on your side: a small script pulls from the Consumer Research API and posts each mention to the universal API.
What you need
Section titled “What you need”- A Brandwatch Consumer Research account and a Query that collects the mentions you want.
- A Consumer Research user with the Regular or Admin role. Brandwatch says only those users can use the API.
- A Dimension Labs API key for a new Universal API source. See Your API key.
Sources Brandwatch can’t pass in full. Brandwatch’s Data Restrictions page says some data packs limit what the API returns:
- X (Twitter): the full text and most metadata are stripped. Brandwatch suggests taking the post id from
guidand fetching the text from the X API yourself; see the X guide, and its note on X’s terms. - Online News: only a 256-character snippet is available.
- LinkedIn: no snippet and no full text.
Brandwatch’s changelog also announced Reddit restrictions from January 2026. Ask your account manager which restrictions apply to your data packs. Mentions with no text are skipped by the code below.
1. Get API access in Brandwatch
Section titled “1. Get API access in Brandwatch”Brandwatch’s steps are in Authentication.
- Get an access token with the API user’s username and password, once:
curl -X POST --data-urlencode "password=$BRANDWATCH_PASSWORD" \ "https://api.brandwatch.com/oauth/token?username=api.user@yourcompany.com&grant_type=api-password&client_id=brandwatch-api-client"- The answer’s
access_tokenis valid for one year by default (expires_inis in seconds). Keep it in a secrets manager and renew it before it expires. If your user can switch between organizations, addplatform_client_idto the request; Brandwatch support gives you the ids. - Send it as
Authorization: Bearer ACCESS_TOKEN. - Find your Project ID and Query ID with Brandwatch’s Retrieving Projects and Retrieving Queries calls.
2. Pull new mentions
Section titled “2. Pull new mentions”curl -G "https://api.brandwatch.com/projects/PROJECT_ID/data/mentions/fulltext" \ -H "Authorization: Bearer $BRANDWATCH_TOKEN" \ --data-urlencode "queryId=QUERY_ID" \ --data-urlencode "startDate=2026-10-01T00:00:00.000+0000" --data-urlencode "endDate=2026-10-26T00:00:00.000+0000" \ --data-urlencode "pageSize=1" --data-urlencode "page=0"/data/mentionsreturns asnippetof each mention, the part that best matches the Query./data/mentions/fulltextreturns the full text, which is what you want to enrich.- Find the text field once. Brandwatch’s docs don’t name the field that holds the full text in a
fulltextanswer. Run the call above withpageSize=1, look at the mention, and setBRANDWATCH_TEXT_FIELDto the name of the field that holds the whole post. - Dates:
startDateandendDateare required and filter on when the mention was posted. Format:2026-10-01T00:00:00.000+0000; encode the+as%2B(--data-urlencodeand the code do this). - Incremental: each mention has
added, the time it was added to the Query, which can be well after it was posted. The code reads the last 24 hours of posting dates (BRANDWATCH_LOOKBACK_HOURS) and keeps mentions added since the last run, with the 5-minute buffer Brandwatch’s polling tutorial uses. A Query backfill resetsadded, so backfilled mentions are sent again; the duplicates are removed. - Pagination:
pageSizegoes up to 5,000, andpagecounts from 0. Page-based reads stop at 10,000 mentions, so the code splits a busier date range in two. - Rate limits (Brandwatch’s): 30 requests every 10 minutes per client by default, with a
429past that. Thex-rate-limitandx-rate-limit-usedheaders show your usage. Brandwatch asks for one request at a time rather than in parallel, and your account manager can discuss a higher limit.
3. Map the fields
Section titled “3. Map the fields”Each mention is one incoming message under its own sessionId. Brandwatch gives no reliable flag for posts by your own accounts, so exclude your own accounts in the Query itself.
| Dimension Labs | Brandwatch | Notes |
|---|---|---|
text | the full-text field (see step 2) | Skip mentions with no text. |
userId | resourceId | Author ids differ by network, so use the mention’s id. Don’t send author or fullname. |
incoming | Always true. | |
sessionId | resourceId | Brandwatch’s unique id for the mention. Prefix it: brandwatch-c54d12f5107496bac30b5ddc3478bbab. |
conversationId | resourceId | Same value as sessionId. |
dimensionlabs_timestamp | date | When the mention was posted, like 2020-06-29T22:45:43.000+0000. Convert with toMillis. |
platformJson | queryId, contentSource, domain, sentiment, language, countryCode, threadId, threadEntryType, guid, tags, categories | contentSource tells the networks apart. threadId (X and Facebook only) groups mentions from one thread. |
Personal data. Don’t send author, fullname, avatarUrl or url, which can hold names and handles. Mention text can hold names too: redact it before you send. See PII redaction.
4. Send it
Section titled “4. Send it”// brandwatch.mjs. Environment: BRANDWATCH_TOKEN, BRANDWATCH_PROJECT_ID, BRANDWATCH_QUERY_ID,// BRANDWATCH_TEXT_FIELD (the field that holds the full text in a fulltext response; see step 2), DL_API_KEY.// Optional: BRANDWATCH_LOOKBACK_HOURS (default 24).import { sendToDimensionLabs, toMillis } from './dimensionlabs.mjs';
const HOUR = 60 * 60 * 1000;const BUFFER = 5 * 60 * 1000; // Brandwatch's polling tutorial uses a 5-minute buffer on `added`const sleep = (ms) => new Promise((r) => setTimeout(r, ms));const API = `https://api.brandwatch.com/projects/${process.env.BRANDWATCH_PROJECT_ID}/data/mentions/fulltext`;const bwTime = (ms) => new Date(ms).toISOString().replace('Z', '+0000'); // 2026-10-01T00:00:00.000+0000
// Retries Brandwatch's rate limit (429: 30 calls per 10 minutes by default) and server errors (5xx).async function get(params) { for (let attempt = 0; ; attempt++) { const res = await fetch(`${API}?${new URLSearchParams(params)}`, { headers: { Authorization: `Bearer ${process.env.BRANDWATCH_TOKEN}` } }); if (res.ok) return res.json(); if ((res.status !== 429 && res.status < 500) || attempt === 5) throw new Error(`Brandwatch returned ${res.status}: ${await res.text()}`); await sleep((Number(res.headers.get('retry-after')) || 60 * 2 ** attempt) * 1000); }}
// Every mention posted between `start` and `end`. Page-based reads stop at 10,000 mentions, so a busier window is split in two.async function* mentions(start, end) { const base = { queryId: process.env.BRANDWATCH_QUERY_ID, startDate: bwTime(start), endDate: bwTime(end), pageSize: '5000', orderBy: 'date', orderDirection: 'asc' }; const first = await get({ ...base, page: '0' }); if (first.resultsTotal > 10000 && end - start > HOUR) { const mid = start + Math.floor((end - start) / 2); yield* mentions(start, mid); yield* mentions(mid, end); return; } yield first.results ?? []; for (let page = 1; page * 5000 < Math.min(first.resultsTotal ?? 0, 10000); page++) { yield (await get({ ...base, page: String(page) })).results ?? []; }}
// Sends every mention added to the Query at or after `since` (Unix ms). Returns the new `since`.export async function syncBrandwatch({ since, historical = false }) { const field = process.env.BRANDWATCH_TEXT_FIELD; if (!field) throw new Error('Set BRANDWATCH_TEXT_FIELD to the field that holds the full text (see step 2 of the guide).'); let newest = since; const start = historical ? since : since - Number(process.env.BRANDWATCH_LOOKBACK_HOURS || 24) * HOUR; // mentions are often added late for await (const batch of mentions(start, Date.now())) { for (const m of batch) { const added = toMillis(m.added); if (!historical && added < since - BUFFER) continue; // seen on an earlier run newest = Math.max(newest, added); const text = String(m[field] ?? '').trim(); if (!text) continue; // X posts and LinkedIn data come without text const ts = toMillis(m.date); const sessionId = `brandwatch-${m.resourceId}`; await sendToDimensionLabs({ text, userId: sessionId, incoming: true, sessionId, conversationId: sessionId, dimensionlabs_timestamp: ts, platformJson: { queryId: m.queryId, contentSource: m.contentSource, domain: m.domain, sentiment: m.sentiment, language: m.language, countryCode: m.countryCode, threadId: m.threadId, threadEntryType: m.threadEntryType, guid: m.guid, tags: m.tags, categories: m.categories }, }, { historical: historical || ts < Date.now() - HOUR }); } } return newest;}5. Run it
Section titled “5. Run it”Save the code as brandwatch.mjs in a project set up as in Set up your project, and run it with the scheduled sync runner: import { syncBrandwatch as sync } from './brandwatch.mjs';.
- History first:
node run.mjs 2026-01-01sends every mention posted since that date to/trackhistorical. Then start a backfill from the Backfill tab. - Then every 15 minutes: the scheduled job sends new mentions 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.
The default limit of 30 requests every 10 minutes is the constraint. A 15-minute run makes one or two calls; a history load of a busy Query makes many, and the code waits whenever Brandwatch returns a 429. If you have mentions in a file instead, upload it and check it first with the file checker, then run a backfill.
Brandwatch reference
Section titled “Brandwatch reference”- Authentication
- Rate limiting
- Best practices & our terms
- Retrieving Mentions
- Mention Field Definitions
- Data Restrictions
- Paging through historical Mentions
- Polling for Mentions
- Reddit Data Restrictions (changelog)
Checked against Brandwatch’s documentation on 2026-10-26.