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:
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
| Status | Meaning |
|---|---|
401 | Missing or invalid Bearer token, or the key was revoked |
402 | The workspace subscription is inactive — API access is paused |
403 | The 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.
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.
{
"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.
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).
{
"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.
{
"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.
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:
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:
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:
{
"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):
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:
{
"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:
{
"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-signature—sha256=<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:
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
2xxstatus 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.