# Videos

> Add, list, update, and remove tracked videos. Trigger scrapes and manage tracking status.

Since Trackagoat 2.0, videos are multi-platform. Each video carries a `platform`, belongs to an `account`, and records the platform's own `platform_video_id`. Add videos from TikTok or Instagram; the platform is auto-detected from the URL.

## GET /api/v2/videos

List tracked videos in your organization. Videos with `tracking_status = "excluded"` are never returned.

### Request

```bash
curl -H "Authorization: Bearer tga_<key>" \
  "https://www.trackagoat.com/api/v2/videos?project_id=<uuid>&platform=instagram"
```

### Query parameters

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `project_id` | uuid | None | Filter to a specific project |
| `creator_id` | uuid | None | Filter to a specific creator |
| `account_id` | uuid | None | Filter to a specific account |
| `platform` | `tiktok` \| `instagram` \| `youtube` | None | Filter to one platform |
| `content_group_id` | uuid | None | Filter to videos in a specific [content group](https://www.trackagoat.com/docs/api-reference/content-groups.md) |
| `limit` | number | 50 | Items per page (max 100) |
| `cursor` | string | None | Pagination cursor |

### Response fields

| Field | Type | Description |
|-------|------|-------------|
| `id` | uuid | Video ID |
| `project_id` | uuid | Project this video belongs to |
| `creator_id` | uuid \| null | Creator this video belongs to (null for direct adds) |
| `account_id` | uuid \| null | Account this video was posted from (null for direct adds) |
| `platform` | `tiktok` \| `instagram` \| `youtube` | Platform the video is on |
| `platform_video_id` | string | The platform's internal video ID |
| `url` | string | Full video URL |
| `title` | string \| null | Short title. TikTok: the caption's first line (up to 100 characters). Instagram: null |
| `caption` | string \| null | The post's text as posted on the platform (YouTube: the video description). Filled by scraping, read-only |
| `description` | string \| null | **Deprecated.** An old free-text field that nothing fills and that can't be written any more. Any text it had was copied into `readme`. Still returned so the response shape doesn't change; it will be removed |
| `readme` | string \| null | Team notes / agent context: Markdown notes (rendered in the Notes tab) |
| `metadata` | object | Agent-writable structured data |
| `thumbnail_url` | string \| null | Proxied thumbnail URL |
| `view_count` | number | Current view count (latest snapshot) |
| `like_count` | number | Current like count |
| `comment_count` | number | Current comment count |
| `share_count` | number | Current share count |
| `tracking_status` | string | One of: `auto_tracked`, `pending_review`, `included`, `excluded`, `direct_add` |
| `posted_at` | ISO 8601 \| null | When the video was originally posted on the platform |
| `last_scraped_at` | ISO 8601 \| null | When stats were last refreshed |
| `created_at` | ISO 8601 | When video was added to trackagoat |
| `updated_at` | ISO 8601 | Last update |

---

## GET /api/v2/videos/{id}

Get one video with its latest stats snapshot. Returns every field listed above, plus `save_count`, `media_type`, `duration_seconds`, the tracking flags `tracking_disabled`, `tracking_disabled_reason`, `tracking_exempted` and `plan_limit_paused_at` (set when the video was paused for your plan's [tracked-video limit](https://www.trackagoat.com/docs/guides/limits.md#tracked-videos): its stats stop updating, but it still counts as posted), and the notes: `readme` and its `readme_version` (for [`POST /api/v2/videos/{id}/notes`](#post-apiv2videosidnotes)). Unlike the list, an `excluded` video is still returned when you ask for it by ID. Use the [Analytics endpoint](https://www.trackagoat.com/docs/api-reference/analytics.md) (`entity=video`) for history.

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

Returns **404** if the video doesn't exist or belongs to another organization.

---

## POST /api/v2/projects/{projectId}/videos

Add one video URL or a bulk array (up to 50). Requires the `write` scope. The platform and video ID are auto-detected from the URL. Dispatches a video-stats scrape automatically.

```bash
curl -X POST \
  -H "Authorization: Bearer tga_<key>" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"url": "https://www.tiktok.com/@charlidamelio/video/7123456789"}' \
  "https://www.trackagoat.com/api/v2/projects/<projectId>/videos"
```

Bulk (up to 50), mixing platforms freely:
```bash
-d '{"videos": [{"url": "https://www.tiktok.com/@user/video/111"}, {"url": "https://www.instagram.com/reel/ABC123/"}]}'
```

| HTTP | Cause |
|------|-------|
| 201 | Video(s) added. At your plan's tracked-video limit, your oldest tracked videos are paused to make room; `meta.paused_count` says how many. |
| 402 | Tracked-video limit reached and every tracked video is exempt, so none could be paused. |
| 422 | Could not detect the platform / video ID from the URL. |

---

## PATCH /api/v2/videos/{id}

Update a video's metadata. At least one field is required. Requires the `write` scope.

```bash
curl -X PATCH \
  -H "Authorization: Bearer tga_<key>" \
  -H "Content-Type: application/json" \
  -d '{"readme": "Strong performance. Boosted by trending audio.", "tracking_status": "included"}' \
  https://www.trackagoat.com/api/v2/videos/<uuid>
```

### Body

| Field | Type | Description |
|-------|------|-------------|
| `title` | string | Override the video title. |
| `readme` | string \| null | Team notes / agent context (Markdown). Replaces the whole notes; pass `null` to clear. To add to them without replacing, use `POST /api/v2/videos/{id}/notes` below. |
| `tracking_status` | string | One of `auto_tracked`, `pending_review`, `included`, `excluded`, `direct_add`. Moving a video to a tracked status at your [tracked-video limit](https://www.trackagoat.com/docs/guides/limits.md#tracked-videos) pauses your oldest tracked video (`402` only if none can be paused). |

The caption comes from the platform and can't be changed. Sending the deprecated `description` field returns `400`.

---

## POST /api/v2/videos/{id}/notes

Append to or replace a video's notes without clobbering human edits. Requires the `write` scope. Same body, response and errors as [`POST /api/v2/creators/{id}/notes`](https://www.trackagoat.com/docs/api-reference/creators.md#post-apiv2creatorsidnotes): `mode: "append"` adds a dated section signed with the API key's name; `mode: "replace"` requires `expected_readme_version` from `GET /api/v2/videos/{id}` and returns **409** `notes_conflict` if the notes changed since.

```bash
curl -X POST \
  -H "Authorization: Bearer tga_<key>" \
  -H "Content-Type: application/json" \
  -d '{"mode": "append", "content": "Boosted by trending audio."}' \
  https://www.trackagoat.com/api/v2/videos/<uuid>/notes
```

---

## DELETE /api/v2/videos/{id}

Remove a video from tracking. Requires the `write` scope.

The video is **excluded, not deleted**: its `tracking_status` becomes `excluded` and the row is kept. It stops being scraped, stops counting toward totals and payouts, and disappears from `GET /api/v2/videos` (`GET /api/v2/videos/{id}` still returns it). Keeping the row is what stops it coming back: the next check of the creator's account finds the post again, and if the row were gone it would be re-added under a new id and its views and view-count bonuses paid a second time. To track it again, `PATCH` it with `"tracking_status": "included"`. Calling `DELETE` on an already-removed video succeeds again.

```bash
curl -X DELETE \
  -H "Authorization: Bearer tga_<key>" \
  https://www.trackagoat.com/api/v2/videos/<uuid>
```

Returns `200` with `{ "deleted": true, "id": "<uuid>", "tracking_status": "excluded", "tracking_disabled": false }`. `deleted: true` is kept for existing clients; it means removed from tracking. (Before 2026-10-09 this endpoint deleted the row.)

---

## GET /api/v2/videos/{id}/comments

One video's [Comments tab](https://www.trackagoat.com/docs/guides/comment-insights.md#a-videos-comments-tab): every comment collected for it, not limited to a date range. Requires the `read` scope and a paid plan (`403 plan_feature_unavailable` otherwise). Also available as the MCP tool [`list_video_comments`](https://www.trackagoat.com/docs/agents/mcp.md).

```bash
curl -H "Authorization: Bearer tga_<key>" \
  "https://www.trackagoat.com/api/v2/videos/<uuid>/comments?signal=app_question"
```

| Parameter | Description |
|-----------|-------------|
| `sort` | `top` (most liked, default), `newest` or `oldest`. |
| `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. |
| `limit`, `offset` | Page size (1 to 200, default 100) and comments to skip. |

`data` has `video_id`, `project_id`, `platform`, `video_url`; `coverage` (`platform_comment_count`, the platform's full count; `collected_count`; `last_collected_at`; `min_comment_count`, the org's threshold; `null` if comments can't be collected for the video); `summary` (`positive`, `neutral`, `negative`, `unscored`, and `signal_counts` per signal and `flagged`, over every collected comment whatever the filters); and `comments`: `id`, `text`, `like_count`, `author_unique_id`, `author_nickname`, `created_at_tiktok` (when it was posted, on every platform), `scraped_at`, `sentiment`, `sentiment_score`, `sentiment_model`, `signals`. `signal`, `sentiment` and `q` filter `comments` only. `meta.total` counts the matching comments; `meta.next_offset` is the next page's `offset` (`null` on the last page).

---

## POST /api/v2/videos/{id}/scrape

Refresh this video's stats now. Consumes from the org's daily manual-scrape quota. Requires the `write` scope.

```bash
curl -X POST \
  -H "Authorization: Bearer tga_<key>" \
  https://www.trackagoat.com/api/v2/videos/<uuid>/scrape
```

The response echoes `{ video_id, job_id }`. Returns 429 if the daily scrape limit is exceeded.

---

## POST /api/v2/videos/{id}/tracking-disable

Disable tracking for a video without deleting it. Requires the `write` scope.

```bash
curl -X POST -H "Authorization: Bearer tga_<key>" \
  https://www.trackagoat.com/api/v2/videos/<uuid>/tracking-disable
```

Use `/tracking-enable` to re-enable (it also resumes a video paused for your plan's tracked-video limit, pausing your oldest other video if needed; `402` only if none can be paused), and `/tracking-unexempt` to clear the exemption flag. Each returns the video's tracking flags: `{ id, tracking_disabled, tracking_disabled_at, tracking_disabled_reason, tracking_exempted, plan_limit_paused_at }`, and `/tracking-enable` adds `paused_count`.

---

## POST /api/v2/projects/{projectId}/videos/bulk-tracking-disable

Disable tracking for up to 200 videos in one call. Requires the `write` scope.

```bash
curl -X POST \
  -H "Authorization: Bearer tga_<key>" \
  -H "Content-Type: application/json" \
  -d '{"video_ids": ["<uuid1>", "<uuid2>"]}' \
  "https://www.trackagoat.com/api/v2/projects/<projectId>/videos/bulk-tracking-disable"
```

Use `/bulk-tracking-enable` to re-enable or resume. Pass `"exempt": true` to also mark videos as exempt from auto-expiry and the tracked-video limit. The response's `paused_count` says how many older videos were paused to make room.

---

## Video time-series analytics

There is no per-video `stats` endpoint. Use the unified [Analytics endpoint](https://www.trackagoat.com/docs/api-reference/analytics.md) with `entity=video` for view, engagement, and growth time-series.

```bash
# Daily new views for a video
curl -H "Authorization: Bearer tga_<key>" \
  "https://www.trackagoat.com/api/v2/analytics?entity=video&entity_id=<uuid>&metric=views&mode=new&granularity=day"

# Engagement rate (derived)
curl -H "Authorization: Bearer tga_<key>" \
  "https://www.trackagoat.com/api/v2/analytics?entity=video&entity_id=<uuid>&metric=engagement_rate_by_views&mode=rate"
```

To view combined stats for the same content across platforms, group the videos and read [content group stats](https://www.trackagoat.com/docs/api-reference/content-groups.md#get-apiv2content-groupsidstats).

`engagement_rate_by_followers` is **not supported** for video entities; use a creator or campaign entity instead. See the [Analytics API reference](https://www.trackagoat.com/docs/api-reference/analytics.md) for the full metric and parameter list.

---
Source: https://www.trackagoat.com/docs/api-reference/videos
