trackagoat logotrackagoat/Docs

Getting started

  • Welcome
  • Quickstart
  • Core concepts

Guides

  • Creators
  • Videos
  • Campaigns
  • Creator Goals
  • Tracking Inbox
  • Content calendar
  • How scraping works
  • Analytics & metrics
  • Similar creator pools
  • Over-posting & suppression
  • Program Health
  • Sentiment Radar
  • API keys
  • Limits & plan tiers
  • Notifications
  • Payouts

API reference

  • Overview
  • Authentication
  • Errors
  • Projects
  • Creators
  • Videos
  • Campaigns
  • Analytics
  • Aggregate Analytics
  • Payouts
  • Schema

For agents

  • Agent guide
  • Data model
  • MCP & tooling

Platform

  • Brand
  • Changelog
  • Support
DocsAPI reference

Analytics

Unified time-series analytics endpoint — metrics, granularity, compare, and breakdown.

PreviousCampaignsNextAggregate Analytics

On this page

  • GET /api/v1/analytics
  • Query parameters
  • Metrics
  • Response shape
  • Response fields
  • Series point fields
  • Common examples
  • Multi-entity comparison
  • Prior-period comparison
  • Campaign breakdown
  • Custom bucket
  • Error codes
  • CSV export
  • CSV columns
  • Campaign response freshness
  • Tier retention
  • Annotations in the response
  • Annotation kinds
  • Annotation CRUD endpoints
  • GET /api/v1/analytics/annotations
  • POST /api/v1/analytics/annotations
  • PATCH /api/v1/analytics/annotations/:id
  • DELETE /api/v1/analytics/annotations/:id
  • Cache semantics
  • MCP stability

GET /api/v1/analytics

The canonical analytics endpoint. Returns bucketed time-series data for any combination of metric, entity, granularity, and mode. Suitable for agent/MCP consumption and chart rendering.

bash
curl -H "Authorization: Bearer tga_<key>" \
  "https://www.trackagoat.com/api/v1/analytics?entity=creator&entity_id=<uuid>&metric=views&mode=new&granularity=day&from=2026-01-01&to=2026-03-31"

The machine-readable OpenAPI 3.1 schema is available at GET /api/v1/analytics/schema.


Query parameters

ParameterTypeDefaultRequiredDescription
entityvideo | creator | campaign—YesEntity type to query
entity_iduuid—One ofSingle entity. Mutually exclusive with entity_ids.
entity_idsCSV of uuids—One ofUp to 5 entities for side-by-side comparison. Cannot be combined with breakdown.
metricstring—YesMetric ID. See Metrics table.
modenew | cumulative | ratemetric defaultNoAggregation mode. Rate-only metrics always use rate.
granularityday | week | month | year | cumulative | customdayNoTime bucket granularity. cumulative returns a running total per day. custom requires bucket_days.
bucket_daysinteger 1–90—If customDays per bucket when granularity=custom.
fromYYYY-MM-DD30 days agoNoRange start, inclusive.
toYYYY-MM-DDtodayNoRange end, inclusive. Clamped to today.
tzIANA timezoneorg settingNoTimezone for bucketing. Defaults to your org's analytics_tz.
comparenone | prior_periodnoneNoInclude a shifted prior-period series in compare_series.
breakdownnone | video | creatornoneNoBreak down into per-constituent series. Top-N kept; remainder rolled into synthetic "Other".
top_ninteger 1–5010NoMax breakdown constituents before "Other" rollup.
formulaby_views | by_followers—NoFormula variant for engagement_rate_* metrics.
base_metricmetric ID—If growth_rate_popSpecifies which raw metric to compute period-over-period growth for.
formatjson | csvjsonNoResponse format. Use csv to download a flat CSV file instead of JSON.

Metrics

Metric IDUnitModesEntitiesNotes
viewscountnew, cumulativeallTikTok view count
likescountnew, cumulativeallLike count
commentscount

Response shape

json
{
  "data": {
    "series": [
      {
        "entity_ref": { "type": "creator", "id": "uuid", "label": "@handle" },
        "metric": "views",
        "unit": "count",
        "mode": "new",
        "granularity": "day",
        "timezone": "America/New_York",


























Response fields

FieldTypeDescription
seriesarrayPrimary metric series, one entry per entity.
compare_seriesarrayPrior-period series when compare=prior_period. Bucket starts are shifted to align with the main period for easy chart overlay.
breakdown_seriesarrayPer-constituent breakdown when breakdown=video|creator. Last entry may be the synthetic "Other" rollup with entity_ref.id = "__other__".
baselinesarrayDates where tracking started or reset. Use as chart annotation markers to suppress spurious delta spikes.

Series point fields

FieldTypeDescription
bucket_startYYYY-MM-DDStart date of the bucket. For week, this is Monday. For month, the first of the month.
valuenumber | nullMetric value. null indicates missing data or divide-by-zero (e.g. 0 views for a rate metric).

Common examples

Multi-entity comparison

bash
curl -H "Authorization: Bearer tga_<key>" \
  "https://www.trackagoat.com/api/v1/analytics?entity=creator&entity_ids=<uuid1>,<uuid2>,<uuid3>&metric=views&mode=new&granularity=week"

Returns three series entries, one per creator. Use entity_ref.label for chart legends.

Prior-period comparison

bash
curl -H "Authorization: Bearer tga_<key>" \
  "https://www.trackagoat.com/api/v1/analytics?entity=campaign&entity_id=<uuid>&metric=views&compare=prior_period&from=2026-04-01&to=2026-04-20"

compare_series contains the prior period (2026-03-12 – 2026-04-01) with bucket starts shifted to match the main period.

Campaign breakdown

bash
curl -H "Authorization: Bearer tga_<key>" \
  "https://www.trackagoat.com/api/v1/analytics?entity=campaign&entity_id=<uuid>&metric=views&breakdown=video&top_n=10"

breakdown_series returns up to 10 per-video series. If the campaign has more than 10 videos, the remainder is aggregated into an entry with entity_ref.id = "__other__" and entity_ref.label = "Other".

Custom bucket

bash
curl -H "Authorization: Bearer tga_<key>" \
  "https://www.trackagoat.com/api/v1/analytics?entity=video&entity_id=<uuid>&metric=views&mode=new&granularity=custom&bucket_days=7&from=2026-01-01&to=2026-03-31"

Error codes

Statusmeta.codeCause
400invalid_paramsMissing required params, unknown metric, invalid UUID, constraint violation (see meta.issues).
401invalid_api_keyMissing or invalid Bearer token.
404—Entity not found or not accessible under your API key's org.
410goneRemoved endpoint (/api/v1/creators/{id}/stats, /api/v1/campaigns/{id}/stats).
400invalid_params

CSV export

Pass format=csv to receive a flat CSV file instead of JSON. The response has Content-Type: text/csv and a Content-Disposition: attachment header with a generated filename.

bash
curl -H "Authorization: Bearer tga_<key>" \
  "https://www.trackagoat.com/api/v1/analytics?entity=creator&entity_id=<uuid>&metric=views&from=2026-01-01&to=2026-03-31&format=csv" \
  -o views.csv

CSV columns

ColumnDescription
series_kindmain, compare, or breakdown
bucket_startYYYY-MM-DD start of the time bucket
entity_idUUID of the entity
entity_labelHuman-readable label (e.g. @handle)
metricMetric ID (e.g. views)
unit, , or

When compare=prior_period or breakdown is active, those series are appended after the main series rows, distinguished by the series_kind column. All other query parameters (granularity, mode, breakdown, compare) are respected exactly as for JSON.


Campaign response freshness

For entity=campaign queries, historical buckets (any day before today in the campaign's org timezone) are served from a pre-aggregated rollup table. The current day's partial bucket is always served from live data.

The rollup is rebuilt nightly and updated within ~5 minutes of any campaign membership change. The response shape is identical whether the data came from the rollup or the live path — callers do not need to handle either case differently.

Tier retention

tier_retention_days in the response is populated from the caller's org tier (it was always null before Phase 6). Use it to pre-check whether a requested date range falls within the available history:

javascript
const { data } = await fetch('/api/v1/analytics?entity=creator&entity_id=<uuid>&metric=views').then(r => r.json());
const maxDays = data.tier_retention_days;  // e.g. 90 for Free, null for unlimited
if (maxDays !== null) {
  const oldestAllowed = new Date();
  oldestAllowed.setDate(oldestAllowed.getDate() - maxDays);

Server-side enforcement is active. Requests with from older than the retention window return HTTP 402 with meta.code = 'retention_exceeded'.


Annotations in the response

Every /api/v1/analytics response includes an annotations array. These are time-ordered event markers within the requested date range.

json
"annotations": [
  {
    "id": "auto:creator_profile_change:abc:2026-03-15T10:00:00Z",
    "source": "auto",
    "kind": "creator_profile_change",
    "scope": "creator",
    "entity_id": "abc-uuid",
    "at": "2026-03-15T10:00:00Z",
    "title": "Profile bio changed",
    "body_md": null,
    "author_id": null,














Pass hide_auto=1 to suppress auto-generated annotations and receive only user-authored ones.

FieldTypeDescription
idstringRow UUID for user annotations; stable synthetic key for auto
sourceuser | autoWho generated this annotation
kindstringEvent category — see table below
scopecreator | campaign | videoEntity type the annotation is attached to

Annotation kinds

KindAuto source
userManually authored
creator_profile_changecreator_events with event_category=profile_change
creator_milestonecreator_events with event_category=milestone
campaign_createdcampaigns.created_at
readme_changedentity_events with event_type=readme_changed

Annotation CRUD endpoints

GET /api/v1/analytics/annotations

List annotations for a specific entity within an optional date range.

bash
curl -H "Authorization: Bearer tga_<key>" \
  "https://www.trackagoat.com/api/v1/analytics/annotations?scope=creator&entity_id=<uuid>&from=2026-01-01&to=2026-04-01"

Query parameters: scope (required), entity_id (required), from, to.

POST /api/v1/analytics/annotations

Create a user-authored annotation.

bash
curl -X POST -H "Authorization: Bearer tga_<key>" \
  -H "Content-Type: application/json" \
  -d '{"scope":"creator","entity_id":"<uuid>","annotation_at":"2026-03-20T14:30:00Z","title":"Launched series","body_md":"**Note:** weekly format"}' \
  "https://www.trackagoat.com/api/v1/analytics/annotations"

Body fields: scope, entity_id, annotation_at (ISO datetime), title (1–200 chars), body_md (optional, ≤8000 chars).

PATCH /api/v1/analytics/annotations/:id

Update an existing user annotation. Partial updates — only send fields you want to change.

bash
curl -X PATCH -H "Authorization: Bearer tga_<key>" \
  -H "Content-Type: application/json" \
  -d '{"title":"Updated title"}' \
  "https://www.trackagoat.com/api/v1/analytics/annotations/<uuid>"

DELETE /api/v1/analytics/annotations/:id

Delete an annotation permanently.

bash
curl -X DELETE -H "Authorization: Bearer tga_<key>" \
  "https://www.trackagoat.com/api/v1/analytics/annotations/<uuid>"

Returns {"data":{"id":"<uuid>"},"error":null} on success. Returns 404 if not found.


Cache semantics

Every JSON response includes:

json
"meta": { "cache_ttl_seconds": 300, "cached": true }
  • cache_ttl_seconds — always 300 (5 minutes). This is the maximum TTL; tag-based invalidation can evict entries sooner.
  • cached — true when data was served from a warm cache hit; false on a cold resolver run (first request, or immediately after invalidation).

What triggers cache invalidation:

EventTags evicted
Scraper writes video statsanalytics:org:{orgId}
Scraper updates creator profileanalytics:org:{orgId}
Scraper rebuilds campaign rollupanalytics:org:{orgId}:campaign:{campaignId}
Campaign item added or removedanalytics:org:{orgId}:campaign:{campaignId}
Annotation created/updated/deletedanalytics:org:{orgId}:{entityType}:{entityId}
Org analytics_tz changedanalytics:org:{orgId} (full org eviction)

For agents and automated consumers: treat cached: false as a signal that this is fresh data. A cached: true response is at most 5 minutes old and was likely invalidated-then-refetched at a real event boundary.


MCP stability

MCP-stable contract

The response shape of /api/v1/analytics is MCP-stable as of OpenAPI v1.4.0. Key names and types will not change without a major version bump. Additive fields will be introduced without breaking existing consumers.

The machine-readable OpenAPI 3.1 schema is served at GET /api/v1/analytics/schema (version 1.4.0).

new, cumulative
all
Comment count
sharescountnew, cumulativeallShare count
savescountnew, cumulativeallSave count
followerscountnew, cumulativecreatorFollower count (not monotonic — real drops are valid)
video_countcountnew, cumulativecreator, campaignTotal videos posted
engagement_rate_by_viewspercentrateall(likes+comments+shares+saves)/views×100
engagement_rate_by_followerspercentratecreator, campaign(likes+comments+shares+saves)/followers×100
posts_per_weekper_weekratecreator, campaignPosts on that day × 7
avg_views_per_postcountratecreator, campaignViews gained / cumulative videos through that day
growth_rate_poppercentrateallPeriod-over-period Δ%. Requires base_metric=<metric_id>.
"points": [
{ "bucket_start": "2026-01-01", "value": 12400 },
{ "bucket_start": "2026-01-02", "value": null }
]
}
],
"compare_series": [],
"breakdown_series": [],
"baselines": [
{
"entity_id": "video-uuid",
"snapshot_at": "2026-01-01T08:00:00Z",
"reason": "tracking_started"
}
],
"annotations": [],
"partial_bucket": {
"bucket_start": "2026-04-01",
"bucket_end": "2026-04-30",
"as_of": "2026-04-20T14:30:00.000Z"
},
"first_data_date": "2026-01-02",
"tier_retention_days": null
},
"error": null,
"meta": { "cache_ttl_seconds": 300, "cached": false }
}
annotations
array
Time-ordered event markers (user-authored and auto-generated) within the requested date range. Empty array when no annotations exist. Pass hide_auto=1 to suppress auto annotations.
partial_bucketobject | nullDescribes the last in-progress bucket when the range ends on or after today. bucket_end is the final day of that bucket (e.g. last day of the month for granularity=month).
first_data_datestring | nullEarliest non-baseline date across all queried entities. Use for "All time" date presets.
tier_retention_daysinteger | nullHistory window enforced by the org's plan tier. null means unlimited (Ultra). Use this to pre-check whether a date range is in-window before issuing a long-range query. Requests outside this window return HTTP 402 (retention_exceeded).
breakdown != 'none' requested for a rate-only metric (e.g. engagement_rate_by_views). Error path is breakdown; message: "Breakdown is not supported for '<metric>'. Use a count-based metric (views, likes, etc.)"
count
percent
per_week
modenew, cumulative, or rate
valueNumeric value, or empty string for null (divide-by-zero / missing data)
is_partialtrue if this bucket is still in progress
// clamp your from date to oldestAllowed
}
"author_name": null
},
{
"id": "550e8400-...",
"source": "user",
"kind": "user",
"scope": "creator",
"entity_id": "abc-uuid",
"at": "2026-03-20T14:30:00Z",
"title": "Launched new video series",
"body_md": "## Notes\nStarted the weekly Q&A format.",
"author_id": "user-uuid",
"author_name": null
}
]
entity_id
uuid
Entity the annotation belongs to
atISO datetimeTimestamp of the event
titlestringShort label (max 200 chars)
body_mdstring | nullOptional markdown body (max 8000 chars)
entity_labelstring?Present when cascade-down resolved the annotation from a parent entity