Competitor tracking
Snapshot a competitor's followers, engagement and latest posts on TikTok, Instagram, YouTube and X in one parallel sweep, then diff it against the last run. 80 to 210 credits per competitor.
You will build the data side of a competitive dashboard: on a schedule, snapshot each competitor's follower counts, engagement rate and latest posts on four platforms, store the snapshot, and compare it with the previous one. Your stored history then reveals sudden follower jumps and new campaigns.
Cost per run
80 to 210 credits per competitor: four profiles at 10 credits each (40), plus four post lists: TikTok (10), Instagram (metered, 10–130), YouTube (metered, 10–20) and X (10). Tracking 10 competitors once a week costs at least 3,200 credits a month.
How do you track a competitor's social accounts?
Call each platform's profile endpoint and its latest-posts endpoint in parallel, store the result keyed by competitor and date, and diff it against the previous snapshot to spot follower growth and new posts. For most consumer brands, four platforms at two calls each, eight in total, is enough.
What you need
| Platform | Profile (10 credits) | Latest posts |
|---|---|---|
| TikTok | GET /v1/tiktok/profile (handle) | GET /v1/tiktok/profile/videos (handle), 10 |
GET /v1/instagram/profile (handle) | GET /v1/instagram/profile/posts (handle), 10–130 | |
| YouTube | GET /v1/youtube/channel (handle) | GET /v1/youtube/channel/videos (handle, includeExtras=true), 10–20 |
| X | GET /v1/twitter/profile (handle) | GET /v1/twitter/user/tweets (handle), 10 |
Every profile comes back as data.author with followers, and every post list as data.items[].post with engagement.likes, engagement.comments and the rest, in the same shape on all four platforms. That makes one engagement formula possible: the code computes the average of (likes + comments) / followers over the latest posts. It skips a post whose likes or comments are null (hidden, or not shown on that list), and a platform whose follower count is null, rather than counting them as zero.
On YouTube, likes and comments are only filled with includeExtras=true. YouTube rounds subscriber counts above 1,000, and the profile says so with followers_approximate: true.
Tracking a B2B competitor
Add GET /v1/linkedin/company (50 credits, a company page URL) for the follower count, and GET /v1/linkedin/company/posts (50 credits, company_id) for the latest posts. GET /v1/linkedin/profile/full (50 credits) returns a company and its latest 10 posts in one call, but it is a beta endpoint: its body follows LinkedIn's own structure rather than the post and profile shapes used here.
The code
# competitor_tracking.py
# Snapshots one competitor on four platforms. Store the result and diff runs.
# Run with: INSIGHTSOCIAL_API_KEY=isk_live_... python competitor_tracking.py
import os
from concurrent.futures import ThreadPoolExecutor
from datetime import date
import requests
KEY = os.environ["INSIGHTSOCIAL_API_KEY"]
BASE = "https://api.insightsocial.app/v1"
COMPETITOR = "duolingo" # the same handle on all four platforms here
def get(path, **params):
res = requests.get(f"{BASE}/{path}", params=params,
headers={"x-api-key": KEY}, timeout=120)
return res.json()
PAIRS = {
"tiktok": ("tiktok/profile", "tiktok/profile/videos", {}),
"instagram": ("instagram/profile", "instagram/profile/posts", {}),
"youtube": ("youtube/channel", "youtube/channel/videos", {"includeExtras": "true"}),
"twitter": ("twitter/profile", "twitter/user/tweets", {}),
}
def own_posts(items):
# Leave out pinned posts (old, out of date order) and reposts of other accounts.
posts = [i["post"] for i in items]
return [p for p in posts
if not p["flags"]["pinned"] and not (p["ext"] or {}).get("retweeted_post")]
def engagement_rate(posts, followers):
"""Average (likes + comments) / followers over the posts that show both counts."""
if not followers:
return None
rates = [(p["engagement"]["likes"] + p["engagement"]["comments"]) / followers
for p in posts
if p["engagement"]["likes"] is not None and p["engagement"]["comments"] is not None]
return round(sum(rates) / len(rates), 6) if rates else None
# Step 1: eight calls in parallel (the key allows 10 in flight).
with ThreadPoolExecutor(max_workers=8) as pool:
futures = {}
for platform, (profile_path, posts_path, extra) in PAIRS.items():
futures[(platform, "profile")] = pool.submit(get, profile_path, handle=COMPETITOR)
futures[(platform, "posts")] = pool.submit(get, posts_path, handle=COMPETITOR, **extra)
results = {k: f.result() for k, f in futures.items()}
# Step 2: build today's snapshot.
snapshot = {"date": date.today().isoformat(), "handle": COMPETITOR, "platforms": []}
spent = 0
for platform in PAIRS:
profile = results[(platform, "profile")]
posts = results[(platform, "posts")]
spent += profile.get("credits_used", 0) + posts.get("credits_used", 0)
if not profile["success"]:
print(f"{platform}: {profile['error']['type']}")
continue
author = profile["data"]["author"]
items = own_posts(posts["data"]["items"]) if posts["success"] else []
latest = items[0] if items else None
snapshot["platforms"].append({
"platform": platform,
"followers": author["followers"],
"followers_approximate": author["followers_approximate"],
"engagement_rate": engagement_rate(items, author["followers"]),
"posts_fetched": len(items),
"latest_post_id": latest["id"] if latest else None,
"latest_post_at": latest["published_at"] if latest else None,
"latest_post": ((latest["content"]["text"] if latest else None) or "")[:80],
})
for row in snapshot["platforms"]:
print(row)
print(f"credits used this run: {spent}")
# Step 3: persist `snapshot` keyed by (handle, date), then compare with the last one:
# follower_delta = today.followers - previous.followers
# new_posts = posts published after previous.latest_post_at
# engagement_jump = today.engagement_rate / trailing average
# Alert when a delta crosses your threshold.What you get back
{'platform': 'tiktok', 'followers': 19300000, 'followers_approximate': None, 'engagement_rate': 0.0091, 'posts_fetched': 9, 'latest_post_id': '7...', 'latest_post_at': '2026-10-02T17:04:11.000Z', 'latest_post': '...'}
{'platform': 'instagram', 'followers': 5600000, 'followers_approximate': None, 'engagement_rate': 0.0047, 'posts_fetched': 11, ...}
{'platform': 'youtube', 'followers': 7640000, 'followers_approximate': True, 'engagement_rate': 0.0012, 'posts_fetched': 30, ...}
{'platform': 'twitter', 'followers': 14900000, 'followers_approximate': None, 'engagement_rate': 0.0003, 'posts_fetched': 16, ...}
credits used this run: 80The figures are illustrative. The rate is one formula applied to four platforms, not four platforms' own definitions, so compare it across competitors on the same platform first. A None rate means no post on the page showed both counts, or the profile had no follower count: keep it None, never 0.
What to change
- Handles per platform. Real brands rarely use one handle everywhere. Keep a
{tiktok, instagram, youtube, twitter}map per competitor. - Your own formula. Add
shareswhere the platform shows them (TikTok, X), or divide byviewsinstead of followers for reach-based engagement. A count the platform does not show isnullon every row; the platform pages list which. - Only fetch new posts. Store the newest post id per platform and pass it back as
stop_at_id, or pass the previous run's date assince. The list then stops at what you already hold. See Choosing endpoints. - Which platforms. Drop a pair to save at least 20 credits, or add LinkedIn for 100 more.
- Store results, refresh on a schedule. Every run is charged, so a dashboard should read from your own store and refresh on a schedule, not on every page view.
Related
Sentiment analysis
Find the conversation about a topic on YouTube and Reddit, pull the comments, and score each comment's sentiment with a model you run. From 200 credits per run.
Creator engagement scoring
Compare one creator's engagement on TikTok, Instagram and YouTube in a single ranked table, calculated with one formula from each platform's recent posts. 60 to 190 credits per creator.