Skip to main content

TikTok Analytics API Overview

Build TikTok analytics into your app with our developer-friendly API. Track any public TikTok video and get hourly performance snapshots without dealing with TikTok’s official API restrictions.
No TikTok Business Account or API approval required. Start building in minutes.

Why Developers Choose Us

TikTok’s Requirements:

  • 🔴 Business account verification
  • 🔴 App review (2-8 weeks wait)
  • 🔴 Strict content policies
  • 🔴 Limited to own videos
  • 🔴 Rate limits + quotas
  • 🔴 Complex OAuth flow

Quick Integration

Get started with 3 lines of code:
const response = await fetch('https://contentstats.io/api/v1/videos/track', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'X-API-Key': process.env.CONTENTSTATS_API_KEY
  },
  body: JSON.stringify({
    video_link: 'https://www.tiktok.com/@user/video/123',
    duration_days: 7
  })
});

const data = await response.json();
console.log('Video ID:', data.id);

API Endpoints

Track Video

POST /api/v1/videos/track
Start tracking a TikTok video. Request:
{
  "video_link": "https://www.tiktok.com/@user/video/1234567890",
  "duration_days": 7
}
Response:
{
  "id": "cm5abc123",
  "video_link": "https://www.tiktok.com/@user/video/1234567890",
  "platform": "tiktok",
  "status": "monitoring",
  "estimated_cost": 2.52
}

Get Video Data

GET /api/v1/videos/{id}
Retrieve snapshots. Response:
{
  "id": "cm5abc123",
  "snapshots": [
    {
      "snapshot_time": "2024-01-29T10:00:00Z",
      "views": "1250000",
      "likes": "185000",
      "comments": "4200",
      "shares": "12500",
      "saves": "8900"
    }
  ]
}

List Videos

GET /api/v1/videos?platform=tiktok
Get all tracked videos filtered by platform.

Stop Tracking

POST /api/v1/videos/{id}/stop
Stop tracking early to save credits.

Full API Reference

View complete API documentation with all endpoints

Integration Examples

React Integration

import { useState, useEffect } from 'react';

interface TikTokSnapshot {
  views: string;
  likes: string;
  comments: string;
  shares: string;
  saves: string;
  snapshot_time: string;
}

function TikTokTracker() {
  const [videoId, setVideoId] = useState<string>('');
  const [snapshots, setSnapshots] = useState<TikTokSnapshot[]>([]);
  
  const trackVideo = async (url: string) => {
    const response = await fetch('/api/track-tiktok', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ video_link: url, duration_days: 7 })
    });
    
    const data = await response.json();
    setVideoId(data.id);
  };
  
  useEffect(() => {
    if (!videoId) return;
    
    const fetchSnapshots = async () => {
      const response = await fetch(`/api/videos/${videoId}`);
      const data = await response.json();
      setSnapshots(data.snapshots);
    };
    
    // Poll every 5 minutes for new snapshots
    const interval = setInterval(fetchSnapshots, 5 * 60 * 1000);
    fetchSnapshots();
    
    return () => clearInterval(interval);
  }, [videoId]);
  
  return (
    <div>
      <input 
        placeholder="TikTok URL" 
        onBlur={(e) => trackVideo(e.target.value)}
      />
      
      {snapshots.map((snap, i) => (
        <div key={i}>
          <p>Views: {snap.views}</p>
          <p>Likes: {snap.likes}</p>
          <p>Time: {new Date(snap.snapshot_time).toLocaleString()}</p>
        </div>
      ))}
    </div>
  );
}

Next.js API Route

// app/api/track-tiktok/route.ts
import { NextRequest, NextResponse } from 'next/server';

export async function POST(request: NextRequest) {
  const { video_link, duration_days } = await request.json();
  
  const response = await fetch('https://contentstats.io/api/v1/videos/track', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'X-API-Key': process.env.CONTENTSTATS_API_KEY!
    },
    body: JSON.stringify({ video_link, duration_days })
  });
  
  const data = await response.json();
  return NextResponse.json(data);
}

Python Flask Integration

from flask import Flask, request, jsonify
import requests
import os

app = Flask(__name__)

@app.route('/api/track-tiktok', methods=['POST'])
def track_tiktok():
    data = request.get_json()
    
    response = requests.post(
        'https://contentstats.io/api/v1/videos/track',
        headers={
            'Content-Type': 'application/json',
            'X-API-Key': os.environ['CONTENTSTATS_API_KEY']
        },
        json={
            'video_link': data['video_link'],
            'duration_days': data.get('duration_days', 7)
        }
    )
    
    return jsonify(response.json())

@app.route('/api/videos/<video_id>', methods=['GET'])
def get_video(video_id):
    response = requests.get(
        f'https://contentstats.io/api/v1/videos/{video_id}',
        headers={'X-API-Key': os.environ['CONTENTSTATS_API_KEY']}
    )
    
    return jsonify(response.json())

Data Structure

Snapshot Object

interface Snapshot {
  id: string;
  snapshot_id: string;
  snapshot_time: string; // ISO 8601
  views: string; // BigInt as string
  likes: string;
  comments: string;
  shares: string | null;
  saves: string | null;
  provider: string;
}

Rate Limiting

PlanRate LimitConcurrent Videos
Free60 req/min10
Pro300 req/min100
EnterpriseCustomUnlimited
Rate limit headers included in every response:
  • X-RateLimit-Limit
  • X-RateLimit-Remaining
  • X-RateLimit-Reset

Error Handling

try {
  const response = await fetch('https://contentstats.io/api/v1/videos/track', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'X-API-Key': process.env.CONTENTSTATS_API_KEY
    },
    body: JSON.stringify({ video_link, duration_days: 7 })
  });
  
  if (!response.ok) {
    const error = await response.json();
    
    switch (response.status) {
      case 401:
        throw new Error('Invalid API key');
      case 402:
        throw new Error(`Insufficient balance: ${error.balance}`);
      case 404:
        throw new Error('Video not found or private');
      case 429:
        throw new Error('Rate limit exceeded');
      default:
        throw new Error(error.error || 'Unknown error');
    }
  }
  
  const data = await response.json();
  return data;
} catch (error) {
  console.error('TikTok tracking failed:', error.message);
}

Webhook Integration (Coming Soon)

Subscribe to events:
{
  "events": ["snapshot.created", "tracking.completed"],
  "url": "https://your-app.com/webhooks/contentstats",
  "secret": "whsec_your_secret"
}

Best Practices

Cache video data locally to reduce API calls:
const cache = new Map();

async function getVideo(id) {
  if (cache.has(id)) {
    return cache.get(id);
  }
  
  const data = await fetchVideo(id);
  cache.set(id, data);
  
  return data;
}
Validate TikTok URLs before tracking:
function isValidTikTokUrl(url) {
  const patterns = [
    /^https:\/\/(www\.)?tiktok\.com\/@[\w.-]+\/video\/\d+/,
    /^https:\/\/vm\.tiktok\.com\/[\w]+/,
    /^https:\/\/(www\.)?tiktok\.com\/t\/[\w]+/
  ];
  
  return patterns.some(pattern => pattern.test(url));
}
Don’t poll faster than hourly — snapshots update every hour:
// Bad: Polling every minute
setInterval(fetchSnapshots, 60 * 1000); // ❌

// Good: Polling every hour
setInterval(fetchSnapshots, 60 * 60 * 1000); // ✅
Check balance before tracking to avoid errors:
const usage = await fetch('https://contentstats.io/api/v1/usage', {
  headers: { 'X-API-Key': apiKey }
}).then(r => r.json());

if (usage.balance_usd < estimatedCost) {
  throw new Error('Insufficient balance');
}

Testing

Test with different video types:
const testCases = [
  {
    name: 'Viral video',
    url: 'https://www.tiktok.com/@charlidamelio/video/...',
    expected: { platform: 'tiktok', status: 'monitoring' }
  },
  {
    name: 'Small creator',
    url: 'https://www.tiktok.com/@user/video/...',
    expected: { platform: 'tiktok', status: 'monitoring' }
  }
];

for (const test of testCases) {
  const result = await trackVideo(test.url);
  console.assert(result.platform === test.expected.platform);
}

Performance Tips

  • Batch requests: Track multiple videos in parallel
  • Use webhooks: Get notified instead of polling (coming soon)
  • Cache aggressively: Snapshots don’t change after creation
  • Filter early: Use query parameters to reduce response size
Explore analytics APIs for other platforms:

Next Steps