Projects
List the projects in your organization and read a project's program health.
GET/api/v2/projects#
List all projects in your organization.
Request#
curl -H "Authorization: Bearer tga_<key>" \
https://www.trackagoat.com/api/v2/projectsQuery parameters#
| Parameter | Type | Default | Description |
|---|---|---|---|
limit | number | 50 | Items per page (max 100) |
cursor | string | None | Pagination cursor from meta.nextCursor |
Response#
{
"data": [
{
"id": "uuid",
"name": "Q1 Campaign",
"readme": "Spring creator program: focus on engagement rate",
"org_id": "uuid",
"created_at": "2026-01-01T00:00:00Z",
"updated_at": "2026-01-10T00:00:00Z"
}
],
"error": null,
"meta": { "hasMore": false, "nextCursor": null }
}Response fields#
| Field | Type | Description |
|---|---|---|
id | uuid | Project ID |
name | string | Project name |
readme | string | null | The project README: team notes and agent context (Markdown) |
org_id | uuid | Owning organization ID |
created_at | ISO 8601 | Creation timestamp |
updated_at | ISO 8601 | Last update timestamp |
PATCH/api/v2/organizations//projects/#
Rename, archive or un-archive a project, or replace its README. Requires the admin scope, which is only effective while the key's owner is a current org admin.
| Field | Type | Description |
|---|---|---|
name | string | Project name (max 200 chars). |
is_archived | boolean | Archive or un-archive. Un-archiving needs a free project slot (402 limit_reached otherwise). |
readme | string | null | The project README (Markdown). Replaces the whole document; null clears it. |
expected_readme_version | string | Optional, only with readme: the readme_version from your last read (GET /api/v2/notes/project/{id} or a previous PATCH response). If the README changed since, nothing is written and the response is 409 notes_conflict with meta.current_readme_version. Without it, readme overwrites whatever is there. |
The response is the updated project plus its readme_version. To add to the README without replacing it, use POST /api/v2/notes/project/{id} with mode: "append".
GET/api/v2/projects//program-health#
The Program Health dashboard as one JSON document, built for agents and scripts: a health verdict and the signals behind it, KPIs, creators who stopped posting, goal compliance, alerts and top / under-performers. The numbers come from the same computation as the dashboard. Requires the read scope. Also available as the MCP tool get_program_health.
curl -H "Authorization: Bearer tga_<key>" \
"https://www.trackagoat.com/api/v2/projects/<projectId>/program-health?range=7d"Query parameters#
| Parameter | Type | Default | Description |
|---|---|---|---|
campaign_id | uuid | none | Only creators and videos in this campaign (sub-campaigns included). Must belong to the project. |
range | 7d | 14d | 30d | 90d | 30d | Window for the period KPIs, ending today in your org's time zone, compared with the equal-length window before it. The dashboard uses 30d. |
Response#
{
"data": {
"project_id": "uuid",
"campaign": null,
"range": "7d",
"window": { "from": "2026-10-02", "to": "2026-10-08", "prior_from": "2026-09-25", "prior_to": "2026-10-01", "days": 7, "tz": "America/New_York" },
"health": {
"status": "at-risk",
"signals": [
{ "signal": "views", "level": "bad", "value": -32, "detail": "Views on posts from the current window vs the prior window: -32%." },
{ "signal": "scrape_freshness", "level": "ok", "value": 96, "detail": "48 of 50 active creators scraped in the last 28h (96%)." }
]
},
"kpis": {
"views_on_window_posts": { "current": 412000, "prior": 605000, "pct_change": -32, "note": "…" },
"posts_in_window": { "current": 61, "prior": 70, "pct_change": -13 },
"posts_this_week": { "this_week": 22, "last_week": 25, "pct_change": -12, "note": "…" },
"active_creators": { "current": 50, "a_week_ago": 48, "added_7d": 2, "disabled_accounts_7d": 0 },
"scrape_freshness": { "fresh": 48, "total": 50, "pct_fresh": 96 },
"engagement_rate": { "latest_full_week": 6.1, "prior_full_week": 6.4, "pct_change": -5 }
},
"stale_creators": [{ "creator_id": "uuid", "handle": "goatcooks", "display_name": "Goat Cooks", "days_since_post": 19, "last_posted_at": "2026-09-19T15:02:00Z" }],
"goal_compliance": { "on_track": 31, "at_risk": 6, "missed": 2, "at_risk_goals": [] },
"alerts": {
"roster_churn": { "is_alert": false, "added_7d": 2, "disabled_7d": 0 },
"engagement_rate_drop": { "is_alert": false, "pct_change": -5 },
"sudden_view_drops": [],
"tracking_gaps": { "stale_scrapes": [], "stale_discovery": [], "missing_videos": [] },
"private_accounts": [],
"disabled_creators": [],
"over_posting": []
},
"top_performers": [{ "creator_id": "uuid", "handle": "goatcooks", "display_name": "Goat Cooks", "views": 98000, "posts": 4, "engagement_rate": 7.2 }],
"underperformers": [],
"active_campaigns": [{ "campaign_id": "uuid", "name": "Fall push", "views": 230000, "videos": 18, "creators": 9 }]
},
"error": null,
"meta": { "computed_at": "2026-10-08T16:20:00Z" }
}Response fields#
| Field | Description |
|---|---|
health.status | healthy, at-risk or degraded: the same verdict as the dashboard's pulse. Each of four signals (active creators, views, posts week over week, scrape freshness) is ok, warn or bad. Two bad signals make the program degraded; one bad or two warn make it at-risk. |
kpis.views_on_window_posts | Current lifetime views of the videos posted in the window vs those posted in the prior window. Recent posts are still gaining views, so a short range understates the current window. |
kpis.posts_this_week | Posts since Monday (org time zone) up to the latest discovery run, vs last week up to the same point. Independent of range. |
stale_creators | Creators whose last post is 14 or more days old, most stale first (up to 25). |
goal_compliance | Goals on track (or completed), at risk (or behind pace) and missed in their current period, with up to 8 at-risk goals. |
alerts.sudden_view_drops | Creators whose last 3 posts average well below their 30-day median (drop_ratio). |
alerts.tracking_gaps | stale_scrapes: no profile scrape in 28h. stale_discovery: profile fresh but new-video discovery hasn't run in 48h. missing_videos: creators with videos on the platform that aren't tracked. |
alerts.private_accounts | Active accounts that went private. They produce no posts while private. |
alerts.disabled_creators | Creators whose primary account was disabled after repeated scrape failures (disabled_reason). |
alerts.over_posting | Days a creator posted so often in the window that some posts were likely suppressed. null if the check couldn't run (not "none"). |
alerts.comment_flags | Comments posted in the last 7 days that ask whether a post is an ad or use words like scam or unsafe (count, more, newest 3 comments). null on plans without comment collection. |
top_performers / underperformers | Most views on posts made in the window (top 10); bottom-quartile engagement rate with under one post a week. |
handle is the creator's primary-account handle (TikTok first, then the oldest account). Lists are capped (10 to 25 entries) to keep the response small.
| HTTP | Cause |
|---|---|
| 400 | Invalid range or campaign_id. |
| 403 | Key lacks the read scope. |
| 404 | Project not in your organization, or campaign_id not in the project. |
GET/api/v2/projects//comments#
Comments collected on the project's tracked videos (Comment Insights), most liked first by default. Comments are placed in the period by when they were posted. Requires the read scope and a paid plan. Also available as the MCP tool list_comments.
curl -H "Authorization: Bearer $TRACKAGOAT_API_KEY" \
"https://www.trackagoat.com/api/v2/projects/<projectId>/comments?signal=flagged&limit=20"Query parameters#
| Parameter | Description |
|---|---|
from, to | Days (YYYY-MM-DD, organization time zone). Default: the last 30 days including today. |
campaign_id | Only videos in this campaign, sub-campaigns included. |
creator_id | Only this creator's videos. |
platform | tiktok or instagram. |
signal | app_question, signup_intent, ad_disclosure, risk, pay_talk, brand_mention, competitor, referral_spam, or flagged (ad_disclosure or risk). |
sentiment | positive, neutral or negative. |
q | Case-insensitive text search. |
sort | top (most liked, default), newest or oldest. |
limit | 1 to 100, default 50. |
cursor | meta.nextCursor from the previous page. |
Response#
data is an array of comments: id, text, like_count, author_unique_id, author_nickname, created_at_tiktok (when it was posted, despite the name, on every platform), scraped_at, sentiment, sentiment_score, sentiment_model, signals, video_id, video_url, platform, caption, video_posted_at, creator_id, creator_handle. meta.hasMore and meta.nextCursor page through the rest.
GET/api/v2/projects//comment-signals#
The numbers behind the Comment Insights page for a period: comments collected and what share of the platform's comments that is (summary.collected_comment_total of summary.platform_comment_total, over videos posted in the period), sentiment counts, signal counts, creators (comments, comments_per_1k_views from the platform's full comment count, positive_share once a creator has 20 scored comments, signal counts, top_comment) and timeseries (scored and positive comments per bucket). Takes from, to, campaign_id, platform and bucket (auto, day, week, month). Requires the read scope and a paid plan. Also available as the MCP tool get_comment_signals_summary.
creators comes most comments first. To rank it by another column, pass creator_sort: comments_captured, comments_per_1k_views, positive_share, app_question_count, signup_intent_count, flagged_count, brand_mention_count or competitor_count (highest first; creators without a value, such as a positive_share under 20 scored comments, last). creator_limit (1 to 500) keeps the top N; meta.creator_total is the full count.
# Which five creators get the most questions about the app?
curl -H "Authorization: Bearer $TRACKAGOAT_API_KEY" \
"https://www.trackagoat.com/api/v2/projects/<projectId>/comment-signals?creator_sort=app_question_count&creator_limit=5"GET/api/v2/projects//comment-flags#
The weekly flag digest on demand, for the last days (organization time zone, including today; default 7, up to 30). Requires the read scope and a paid plan. Also available as the MCP tool get_comment_flags.
| Parameter | Description |
|---|---|
days | Window length, 1 to 30. Default 7. |
campaign_id | Only videos in this campaign, sub-campaigns included. |
platform | tiktok or instagram. |
limit | Flagged comments to return, most liked first. 1 to 50, default 10. |
data has ad_disclosure_count (comments asking whether a post is an ad), risk_count (words like scam or unsafe) and flagged_count (either) for comments posted in the window; comments, the most-liked flagged comments in the same shape as /comments (more_comments is true when there are more: page through them with /comments?signal=flagged); and positive_share_drops, creators whose share of positive comments fell 20 points or more against the 30 days before the window (baseline_range), with at least 20 scored comments in both (recent_positive_share, prior_positive_share, drop_points). The Monday notification counts comments collected in the past week; this endpoint places comments by when they were posted, like the rest of Comment Insights.
Errors for /comments, /comment-signals and /comment-flags:
| HTTP | Cause |
|---|---|
| 400 | Invalid parameter or cursor. |
| 403 | Key lacks the read scope, or plan_feature_unavailable (meta.feature: "comment_insights"): the organization's plan doesn't collect comments. |
| 404 | Project not in your organization, or campaign_id not in the project. |
GET/api/v2/comment-settings#
The API key's organization's Comment Insights settings (Org settings → Comments). Works on every plan, so it's also how to check whether the plan collects comments. Requires the read scope and an org admin (403 insufficient_role otherwise), like the settings page. Also available as the MCP tool get_comment_settings.
{
"data": {
"org_id": "…",
"org_name": "Acme",
"min_comment_count": 5,
"brand_terms": [],
"effective_brand_terms": ["Acme"],
"competitor_terms": ["Brand A", "branda.com"],
"plan": { "comment_insights_enabled": true, "max_comment_videos_per_day": 40 },
"updated_at": "2026-10-10T15:00:00Z"
},
"error": null,
"meta": null
}min_comment_count: a video's comments are collected once it is 3 days old and the platform shows at least this many. effective_brand_terms is what the brand_mention signal matches: brand_terms, or the organization's name when none are set. plan.max_comment_videos_per_day is 0 on plans without comment collection.
PATCH/api/v2/comment-settings#
Change the settings. Requires the write scope and an org admin (403 insufficient_role otherwise), like the settings page. Also available as the MCP tool update_comment_settings.
curl -X PATCH -H "Authorization: Bearer $TRACKAGOAT_API_KEY" -H "Content-Type: application/json" \
-d '{"competitor_terms": ["Brand A", "branda.com"]}' \
https://www.trackagoat.com/api/v2/comment-settings| Field | Description |
|---|---|
min_comment_count | 1 to 100,000. Lower means more videos are collected, and more cost. |
brand_terms | Up to 20 terms. Replaces the list; [] falls back to the organization's name. |
competitor_terms | Up to 50 terms. Replaces the list; [] clears it. |
Terms are trimmed, de-duplicated ignoring case, and 60 characters or fewer; they match whole words and phrases, case-insensitively. Mentions are matched when comments are read, so new terms apply to comments already collected. Unknown fields are rejected with 400. Returns the settings in the GET shape.