Bloss Docs

Campaign spend by tag

Developers
GET
/campaign/tags

Per-tag breakdown of a campaign's spend and reach. For each of your brand's tags, returns the videos, views, spend (Base + CPM), and CPM attributable to that tag across the campaign's cycles. Spend is attributed by pair tag (pairs carry the payout). A pair with multiple tags counts in each tag's bucket, so per-tag totals can exceed the campaign total. Every brand tag is returned, zero-filled when it has no activity. Use the returned tagId values as tagIds on GET /campaign/summary and GET /campaign/daily.

groupIds narrows the population the breakdown describes — "how did this squad's spend split across tags?". The catalog itself is unaffected: every brand tag still comes back, zero-filled where that group has no activity.

Authorization

headerAuthorizationBearer <token>

Provide your API key as Authorization: Bearer <key>.

Query Parameters

cycleIds*string

Comma-separated cycle ids to include in the campaign.

groupIds?string

Comma-separated creator-group ids. Counts only the activity of creators in at least one of these groups. Get group ids from GET /groups; __ungrouped__ selects creators in no group.

Response Body

Per-tag spend and reach.

application/json
  1. response
currency?string
tags?array<>
curl -X GET 'https://example.com/api/v0/campaign/tags?cycleIds=cyc_abc%2Ccyc_def&groupIds=grp_abc%2Cgrp_def'
{  "currency": "string",  "tags": [    {      "tagId": "string",      "name": "string",      "color": "string",      "videos": 0,      "views": 0,      "spend": 0,      "cpm": 0    }  ]}

Was this page helpful?

Campaign daily breakdown GET

Per-day videos posted, views generated, and spend (Base + CPM) across the campaign's cycles. Defaults to the full span the cycles cover, in your brand's timezone. Summing the days reproduces `GET /campaign/summary` for the same cycles: `videos` counts each video once on the day it was posted, `views` is each video's own day-over-day gain, and `spend` is the day-over-day increase in that day's cycle pay. `spend` is null on a day no requested cycle covers, and can be negative on a day a pair's pay was revised down. When `tagIds` is supplied, videos/views are scoped to that tag set and `spend` is null for every day — per-day spend cannot be attributed to a tag (pay is a cycle-level total on the pair). Use `GET /campaign/tags` for total spend by tag. `groupIds` scopes videos/views by the video's creator and also nulls `spend`, for a different reason: the daily spend series is differenced out of a pre-aggregated brand-wide table with no creator dimension. For a group's total spend, use `GET /campaign/summary` with the same filter.

Campaign pairs (per-post rows) GET

One row per video *pair* — a creator's post, or the 1–3 cross-posted videos that share a single payout — for the given cycles. This is the layer underneath `GET /campaign/summary` and `GET /campaign/tags`: per-pair `totalSpend` and `views` sum to the campaign figures those endpoints report for the same cycles. Each row carries the creator, the pair's tag ids, its money (`basePay` + `cpmBonus` = `totalSpend`), and a `videos` array with the platform, post URL, posted-at, and engagement of every video in the pair. Engagement comes from the latest snapshot inside the cycle window — the same rows the campaign totals count. A video with no in-cycle snapshot has `snapshotDate: null` and zeroed metrics, and is not counted by any campaign endpoint; `countedVideos` is how many of the pair's videos did count. `tagIds` filters which *pairs* come back (pair tags carry the money); each returned pair still reports all of its own videos. `groupIds` filters by the pair's **creator** instead — the creator groups from `GET /groups`. Filters compose as an AND (`groupIds` plus `tagIds` means both), while ids *within* one filter are ANY. Every row also carries `creator.groupIds`, so one unfiltered pass can be bucketed by group client-side. Results are ordered by `(updatedAt, pairId)` ascending and paged with the opaque `nextCursor`. Pass `updatedSince` with the largest `updatedAt` you have already stored to pull only what changed since — `updatedAt` moves whenever the pair is re-paired, re-assigned, or its payout is recalculated (which is what a view refresh triggers).