Skip to content

Projects

List the projects in your organization and read a project's program health.

Updated Oct 10, 2026 · 7 min read

GET/api/v2/projects#

List all projects in your organization.

Request#

bash
curl -H "Authorization: Bearer tga_<key>" \
  https://www.trackagoat.com/api/v2/projects

Query parameters#

ParameterTypeDefaultDescription
limitnumber50Items per page (max 100)
cursorstringNonePagination cursor from meta.nextCursor

Response#

JSON
{
  "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#

FieldTypeDescription
iduuidProject ID
namestringProject name
readmestring | nullThe project README: team notes and agent context (Markdown)
org_iduuidOwning organization ID
created_atISO 8601Creation timestamp
updated_atISO 8601Last 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.

FieldTypeDescription
namestringProject name (max 200 chars).
is_archivedbooleanArchive or un-archive. Un-archiving needs a free project slot (402 limit_reached otherwise).
readmestring | nullThe project README (Markdown). Replaces the whole document; null clears it.
expected_readme_versionstringOptional, 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.

bash
curl -H "Authorization: Bearer tga_<key>" \
  "https://www.trackagoat.com/api/v2/projects/<projectId>/program-health?range=7d"

Query parameters#

ParameterTypeDefaultDescription
campaign_iduuidnoneOnly creators and videos in this campaign (sub-campaigns included). Must belong to the project.
range7d | 14d | 30d | 90d30dWindow 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#

JSON
{
  "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#

FieldDescription
health.statushealthy, 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_postsCurrent 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_weekPosts since Monday (org time zone) up to the latest discovery run, vs last week up to the same point. Independent of range.
stale_creatorsCreators whose last post is 14 or more days old, most stale first (up to 25).
goal_complianceGoals 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_dropsCreators whose last 3 posts average well below their 30-day median (drop_ratio).
alerts.tracking_gapsstale_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_accountsActive accounts that went private. They produce no posts while private.
alerts.disabled_creatorsCreators whose primary account was disabled after repeated scrape failures (disabled_reason).
alerts.over_postingDays 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_flagsComments 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 / underperformersMost 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.

HTTPCause
400Invalid range or campaign_id.
403Key lacks the read scope.
404Project 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.

bash
curl -H "Authorization: Bearer $TRACKAGOAT_API_KEY" \
  "https://www.trackagoat.com/api/v2/projects/<projectId>/comments?signal=flagged&limit=20"

Query parameters#

ParameterDescription
from, toDays (YYYY-MM-DD, organization time zone). Default: the last 30 days including today.
campaign_idOnly videos in this campaign, sub-campaigns included.
creator_idOnly this creator's videos.
platformtiktok or instagram.
signalapp_question, signup_intent, ad_disclosure, risk, pay_talk, brand_mention, competitor, referral_spam, or flagged (ad_disclosure or risk).
sentimentpositive, neutral or negative.
qCase-insensitive text search.
sorttop (most liked, default), newest or oldest.
limit1 to 100, default 50.
cursormeta.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.

bash
# 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.

ParameterDescription
daysWindow length, 1 to 30. Default 7.
campaign_idOnly videos in this campaign, sub-campaigns included.
platformtiktok or instagram.
limitFlagged 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:

HTTPCause
400Invalid parameter or cursor.
403Key lacks the read scope, or plan_feature_unavailable (meta.feature: "comment_insights"): the organization's plan doesn't collect comments.
404Project 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.

JSON
{
  "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.

bash
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
FieldDescription
min_comment_count1 to 100,000. Lower means more videos are collected, and more cost.
brand_termsUp to 20 terms. Replaces the list; [] falls back to the organization's name.
competitor_termsUp 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.