If you have an existing Tracksuit API integration (set up before July 27, 2026), this is the complete inventory of what's different in v2. Every change that can break an existing integration, in one place. It's the "why your old call won't work as-is" companion to the two pages that tell you what to do about it: the API v1 to v2 endpoint mapping (the line-by-line translation table) and How to migrate from the v1 to the v2 API (the plan and timeline).
👉 Your existing v1 key still works. None of these changes touch authentication. The same Authorization: Bearer YOUR_API_KEY header works against both versions, so there's no new token to request. See How to authenticate the Tracksuit API.
👉 See the changes live. Our interactive API documentation lets you paste in your key, run real v2 requests, copy code snippets, and download the OpenAPI spec. This is the fastest way to see the new shapes before you touch your code.
The breaking changes at a glance
Everything below comes down to seven shifts. If you only skim one thing, skim this.
Area | v1 | v2 |
Base URL |
|
|
Core resource |
| category view |
Naming convention |
|
|
Demographics |
|
|
Results | Whole result in one response | Cursor pagination ( |
Sample quality |
| Reliability indicator only; raw counts removed |
Errors | Bare gateway errors, no consistent body | Uniform JSON envelope ( |
1. Base URL and versioning
The version prefix changes, and that's the one edit every integration must make.
v1:
https://prod.beta.api.gotracksuit.com/v1v2:
https://prod.beta.api.gotracksuit.com/v2
Same host, same key — only the /v1 → /v2 segment and the per-request changes below.
2. The resource model: accountBrandId becomes a category view id
This is the change everything else hangs off. v1 was organized around the account brand, identified by an integer accountBrandId that you passed as a query parameter on some endpoints and a path segment on others. v2 is organized around the category view — the same idea (one brand tracked in a specific category and geography) — but:
it's identified by a string
id, not an integer;you discover it from
GET /category-viewsrather than assuming you already know it;it lives in the path on every data endpoint:
GET /category-views/{id}/funnel,/statements, and so on.
Don't assume your old integer ID carries over as the string ID. Treat the value from GET /category-views as opaque, cache it, and use it verbatim. The full per-endpoint translation is in the endpoint mapping.
3. Endpoints renamed or removed
Every v1 path changes name, moves, or goes away. This is the summary; the endpoint mapping has the full request-by-request detail.
v1 endpoint | v2 | What's breaking |
|
| Renamed. Returns string |
|
| ID moves into the path; request and response both re-shaped (sections 4–5). |
| (removed) | No "all demographics in one call". Reproduce it by calling |
|
| Available demographics, metrics, brands and channels now come from the metadata endpoint. |
| (no equivalent yet) | Category penetration is not a v2 endpoint. |
| Docs are no longer served by the API itself. |
4. Request parameter changes
Beyond moving the ID into the path, the parameters themselves are renamed and re-shaped.
v1 parameter | v2 parameter | What's breaking |
|
| Renamed and re-cased to |
|
| Stringified JSON of |
(none — always 3-month rolling) |
| New, optional. Defaults to |
(none — whole result returned) |
| New pagination controls. See section 6. |
New optional parameters on the metric endpoints. brand_ids (restrict to specific brands from metadata), metrics (restrict to specific funnel stages), and minimum_indicator (drop low-sample rows). All default to "everything", so a v1-equivalent pull that omits them keeps working.
The snake_case switch is easy to miss. Every multi-word parameter and response field is snake_case in v2 (start_period, next_token, page_size). A camelCase parameter from v1 will simply be ignored, not error, so a half-migrated request can silently return the wrong thing.
5. Response shape changes
v2 responses are restructured, not just renamed. Using the funnel as the worked example, a v1 metric row like this:
{ "waveDate": "2025-06-01", "brandId": 21295, "brandName": "Acme", "isAccountBrand": true, "questionType": "PROMPTED_AWARENESS", "filter": "TOTAL", "percentage": 0.44, "population": 1000 }becomes this in v2:
{ "period_start": "2025-06-01T00:00:00", "brand": { "id": "21295", "name": "Acme", "hero": true }, "dimensions": [], "metric": "PROMPTED_AWARENESS", "smoothing": "3mo", "value": { "percentage": 0.44 }, "sample": { "indicator": "Reliable" } }The specific breaking differences:
v1 field | v2 field | What's breaking |
Top-level |
| The response is now a paginated envelope, not a single object with a |
|
| Renamed, re-cased, and now a full timestamp ( |
|
| Flat fields become a nested object. |
|
| Renamed and moved inside the |
|
| Renamed. The available values come from the metadata endpoint — read them from there rather than hard-coding v1's enum. |
|
| A single filter string becomes an array of dimension objects (empty for the total). |
|
| Nested under |
| (removed) | Raw respondent counts are no longer returned — see section 7. |
Field naming and nesting both change. It isn't a find-and-replace of names — you're reading from new nested objects (brand.*, value.*, sample.*) and iterating items[]. Plan to re-map your response parser, not just rename keys.
6. Pagination is now required
v1 returned the whole result set in a single response. v2 is cursor-paginated: each response carries a next_token, and you keep calling with that token until it comes back null.
Build the loop even if today's pulls fit on one page — result sizes grow, and the contract expects it.
Keep every other parameter identical across pages; changing filters, dates or sort mid-pull invalidates the cursor.
Cursors expire after ~30 minutes; the recovery is always to restart from page 1.
Full detail and code in How to handle pagination.
7. Sample quality and removed fields
How you judge whether a data point is trustworthy changes — and the raw numbers you might have used for that are gone.
v1 | v2 | What's breaking |
|
| Renamed, re-bucketed, and moved to per-item |
| (removed) | The underlying respondent counts are no longer exposed. Use the reliability indicator to gauge a data point instead. |
| (removed) | Internal weighting fields are not part of the v2 contract. |
If you displayed or filtered on sample size, switch to the reliability indicator. v2 also adds the minimum_indicator parameter so the API can drop low-sample rows for you. Background in Why your API numbers may differ from the dashboard.
8. A real error format
v1 leaned on bare API Gateway / Lambda errors with no consistent body. v2 returns the same JSON envelope on every error, on every endpoint:
{ "code": "400", "message": "Pagination cursor has expired", "error_code": "cursor_expired" }Branch on the stable error_code, not the human-readable message. If your v1 client only checked HTTP status codes, it'll still work — but you can now do much better. The full catalogue is in How to handle API errors.
What's not changing
Just as important as the breaking list, so you don't over-engineer the migration:
Authentication. Same key, same
Authorization: Bearerheader, works on both versions.The metrics themselves. Awareness, consideration, usage and the rest mean the same thing and come from the same surveys. v2 renames fields and adds new metrics; it doesn't redefine the old ones.
The percentage scale. Values are still a fraction between 0 and 1 (e.g.
0.44), not 0–100. v2 rounds them to two decimal places (to the nearest 0.01 / whole percentage point).The smoothing methodology. v2's default
3morolling average is the same calculation v1 always applied — leavesmoothingoff to match.The data. The same period returns the same story. Validate by pulling v1 and v2 side by side before you cut over — see Why your API numbers may differ from the dashboard.
