Developer docs

ViewTracker API & webhooks

Authenticate with an API key, manage tracking, creators and campaigns over REST, read statistics, and receive signed video.updated webhooks.

The public API lets you manage your workspace programmatically — add videos and accounts to tracking, create creators and campaigns, and read statistics — so dashboards, automations and AI agents can drive ViewTracker without the UI. Outbound webhooks push signed video.updated events to your servers as new snapshots land. Both are available on plans with API access (Ultra and Scale) and are managed from Settings.

Authentication

Create an API key in Settings → Public API keys. The full secret (prefixed vt_live_) is shown once at creation — store it somewhere safe. Only a SHA-256 hash is kept on our side, so a lost key can't be recovered, only revoked and replaced.

Send the key as a Bearer token with every request:

bash
curl https://viewtracker.ai/api/v1/videos \
  -H "Authorization: Bearer vt_live_..."

Keys are scoped to one workspace. Revoking a key rejects requests immediately.

Error responses

StatusMeaning
401Missing or invalid Bearer token, or the key was revoked
402The workspace subscription is inactive — API access is paused
403The workspace plan does not include API access (requires Ultra or higher)

Errors are returned as { "error": "message" } JSON.

Pagination

Both list endpoints use cursor pagination. Pass limit (1–100, default 50) and the nextCursor value from the previous response as cursor. When nextCursor is null, you have reached the end.

bash
curl "https://viewtracker.ai/api/v1/videos?limit=100&cursor=vid_01hy..." \
  -H "Authorization: Bearer vt_live_..."

GET /api/v1/videos

Lists tracked videos, newest first, with the latest recorded metrics.

json
{
  "data": [
    {
      "id": "vid_01hy3f...",
      "platform": "tiktok",
      "externalId": "7301234567890123456",
      "url": "https://www.tiktok.com/@handle/video/7301234567890123456",
      "caption": "Launch day!",
      "postedAt": "2026-07-01T09:30:00.000Z",
      "status": "active",
      "views": 412034,
      "likes": 20311,
      "comments": 1174,
      "lastRefreshedAt": "2026-07-29T07:12:44.000Z"
    }
  ],
  "nextCursor": "vid_01hy2a..."
}

views, likes and comments come from the most recent snapshot and are null until the first sync completes.

POST /api/v1/videos

Adds a public video to tracking — the same operation as the dashboard's "Add tracking", under the same plan quotas.

bash
curl -X POST https://viewtracker.ai/api/v1/videos \
  -H "Authorization: Bearer vt_live_..." \
  -H "Content-Type: application/json" \
  -d '{"url": "https://www.tiktok.com/@handle/video/7301234567890123456"}'

Optional fields: platform (hint for bare IDs), addCreatorToRoster (boolean — create a creator from the video's author after the first sync). Returns 201 with the new video id; 409 if the video is already tracked (the existing id is included); 429 when a rolling-window or catalog quota blocks the add; 403 when the plan limit is reached.

GET /api/v1/videos/{id}/history

Snapshot history for one video — the raw series behind the charts. days selects the window (1–365, default 30).

json
{
  "videoId": "vid_01hy3f...",
  "days": 30,
  "data": [
    { "capturedAt": "2026-07-28T07:00:00.000Z", "views": 406913, "likes": 20100, "comments": 1150, "shares": 890, "saves": 410 },
    { "capturedAt": "2026-07-29T07:00:00.000Z", "views": 412034, "likes": 20311, "comments": 1174, "shares": 903, "saves": 421 }
  ]
}

GET /api/v1/accounts

Lists tracked profiles and channels with their latest follower counts.

json
{
  "data": [
    {
      "id": "acc_01hx9d...",
      "platform": "instagram",
      "externalId": "17841400000000000",
      "handle": "brandname",
      "displayName": "Brand Name",
      "profileUrl": "https://www.instagram.com/brandname/",
      "status": "active",
      "followers": 88012,
      "lastRefreshedAt": "2026-07-29T06:58:02.000Z"
    }
  ],
  "nextCursor": null
}

POST /api/v1/accounts

Starts tracking a public profile or channel; recent videos are imported automatically.

bash
curl -X POST https://viewtracker.ai/api/v1/accounts \
  -H "Authorization: Bearer vt_live_..." \
  -H "Content-Type: application/json" \
  -d '{"handle": "brandname", "platform": "instagram", "videoLimit": 25}'

Pass either a profile url or a handle + platform pair. videoLimit caps how many recent videos are imported (default 10). Returns 201 with the new account id, 409 for duplicates, 403 at the plan limit.

Creators — GET & POST /api/v1/creators

GET lists the creator roster (cursor pagination as above). POST creates a creator for campaigns and payouts:

bash
curl -X POST https://viewtracker.ai/api/v1/creators \
  -H "Authorization: Bearer vt_live_..." \
  -H "Content-Type: application/json" \
  -d '{"displayName": "Alex Rivera", "email": "alex@example.com"}'

displayName is required; email doubles as the initial payout email.

Campaigns — GET & POST /api/v1/campaigns

GET lists campaigns with status and tracked-video counts. POST creates a draft campaign:

bash
curl -X POST https://viewtracker.ai/api/v1/campaigns \
  -H "Authorization: Bearer vt_live_..." \
  -H "Content-Type: application/json" \
  -d '{"name": "Summer launch", "description": "July creator push"}'

New campaigns start in draft; assign creators, videos and payout rules from the campaign page.

GET /api/v1/stats

One-call workspace summary for dashboards and chat integrations:

json
{
  "trackedVideos": 412,
  "activeVideos": 388,
  "trackedAccounts": 37,
  "creators": 24,
  "campaigns": 6,
  "totalViews": 128443210,
  "totalLikes": 6120034,
  "totalComments": 341202
}

Totals are sums of each video's latest snapshot.

Auto-track hook

Publish with a scheduler and have the posted link start tracking automatically. Copy the ready-made hook URL from Settings → Auto-track hook and paste it into the tool's "post published" webhook (in Postiz: Settings → Webhooks → Add a webhook):

text
POST https://viewtracker.ai/api/hooks/track/vt_hook_...

The URL stays available in Settings and can be regenerated or disabled at any time — regenerating invalidates the old URL immediately. (A vt_live_ API key also works in place of the token.)

The payload shape does not matter — every string in the event is scanned, and each recognizable public video URL (TikTok, Reels, Shorts, Facebook Reels) is added to tracking through the normal plan quotas.

The response reports what happened:

json
{
  "ok": true,
  "added": [{ "id": "vid_01hy...", "url": "https://www.tiktok.com/@handle/video/730..." }],
  "skipped": [{ "url": "https://...", "reason": "already tracked" }]
}

Up to 20 video URLs are processed per event; duplicates are skipped silently, and a quota block stops the rest of the batch. Non-video links (profile pages, article links) are ignored. Tools that validate the URL with a GET ping receive a friendly 200.

External tools using Send test receive an explicit “connection: verified” / “test: passed” response even when their test payload contains no video URL. A connection test never creates a tracked video and does not necessarily save or enable the webhook. Save the webhook and subscribe it to the channels whose publish events should be sent. In Postiz, select All integrations or the specific channel, then click Save. Real publish events still need a supported public video URL.

Treat the hook URL as a secret — anyone who has it can add videos to your workspace. If it leaks, regenerate it in Settings.

Webhooks

Add an HTTPS endpoint in Settings → Outbound webhooks. The signing secret (prefixed whsec_) is shown once at creation. Every time a tracked video gets a new snapshot, we POST a video.updated event:

json
{
  "id": "evt_1753772400000_vid_01hy3f...",
  "event": "video.updated",
  "createdAt": "2026-07-29T07:00:00.000Z",
  "data": {
    "videoId": "vid_01hy3f...",
    "views": 412034,
    "likes": 20311,
    "comments": 1174,
    "shares": 903,
    "viewsDelta": 5121
  }
}

Requests carry two headers:

  • x-viewtracker-event — the event name (video.updated)
  • x-viewtracker-signaturesha256=<hex>, an HMAC-SHA256 of the raw request body using your signing secret

Verifying signatures

Compute the HMAC over the exact raw body bytes and compare in constant time:

ts
import { createHmac, timingSafeEqual } from "node:crypto";

export function verifyViewTrackerSignature(
  rawBody: string,
  signatureHeader: string, // "sha256=..."
  secret: string, // whsec_...
): boolean {
  const expected = createHmac("sha256", secret).update(rawBody).digest("hex");
  const received = signatureHeader.replace(/^sha256=/, "");
  return (
    received.length === expected.length &&
    timingSafeEqual(Buffer.from(received, "hex"), Buffer.from(expected, "hex"))
  );
}

Parse the JSON only after the signature checks out.

Delivery and retries

  • Respond with any 2xx status within 15 seconds to acknowledge a delivery.
  • Failed deliveries are retried up to 5 attempts total, spaced at least a minute apart.
  • Deliveries pause while an endpoint is disabled or the workspace is read-only, and resume when it recovers.
  • Endpoints must be public HTTPS URLs — private and internal addresses are rejected.

Use the Send test button next to an endpoint in Settings to deliver a signed sample event on demand.

Fair use

Write endpoints share the same quotas as the dashboard — plan limits, rolling add windows and catalog caps apply equally to API adds. If you need higher limits or additional endpoints, contact hello@viewtracker.ai.