# Track competitor TikTok accounts

Pull each competitor's TikTok profile, find its latest videos and read what viewers say, in three tool calls an agent can repeat every week.

Watching competitors on TikTok comes down to three questions: how big is each account, what is it posting, and how does its audience react? Each step below answers one of them with one capability from the catalog, so an agent can run the whole recipe on a schedule and report what changed since the last run.

Each step lists the cheapest tools that serve it, with each one's price and health. Their input fields can differ from one source to the next, so read a tool's schema with `GET /v1/tools/{id}` before you call it, or hand the tools to a model as functions and let it fill them in.

## Steps

### 1. Read each account's profile

Fetch the follower, following and like counts, the bio and the verification badge for every competitor handle. Keep the counts, so the next run can report growth.

Platform: [TikTok](https://www.orcarouter.ai/tools/tiktok.md) · Capability: [User profile](https://www.orcarouter.ai/tools/tiktok/user-profile.md) · 8 tools, from $0.000495 per call

| Tool | Source | Price | Health |
| --- | --- | --- | --- |
| [TikTok Profile](https://www.orcarouter.ai/tools/tiktok/user-profile/tiktok-user-profile.md) | — | $0.000495 per call | OK |
| [TikTok user profile by username or secUid](https://www.orcarouter.ai/tools/tiktok/user-profile/tiktok-user-profile-1b80cc.md) | — | $0.0011 per call | OK |
| [TikTok creator info and milestones by user id](https://www.orcarouter.ai/tools/tiktok/user-profile/x-tiktok-analytics-fetch-creator-info-and-milestones.md) | — | $0.0011 per call | OK |

Example input:

```json
{"username":"duolingo"}
```

### 2. Find their recent videos

Search TikTok for the competitor's name or campaign hashtag to see what it published and which posts are taking off.

Platform: [TikTok](https://www.orcarouter.ai/tools/tiktok.md) · Capability: Video search

No tool serves this step right now.

Example input:

```json
{"keyword":"duolingo"}
```

### 3. Read what viewers say

Pull the comments under the top videos from the previous step and have the agent sum up the themes: praise, complaints and questions.

Platform: [TikTok](https://www.orcarouter.ai/tools/tiktok.md) · Capability: Video comments

No tool serves this step right now.

Example input:

```json
{"video_id":"VIDEO_ID_FROM_STEP_2"}
```

## Code

### curl

```shell
# Needs curl and jq. Step 1 is shown; repeat it with each step's query.
TOOL_ID=$(curl -s https://api.orcarouter.ai/v1/tools/discover \
  -H "Authorization: Bearer $ORCAROUTER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"q": "tiktok user profile", "limit": 1}' | jq -r '.data.results[0].id')

# The inputs the tool takes
curl -s "https://api.orcarouter.ai/v1/tools/$TOOL_ID" \
  -H "Authorization: Bearer $ORCAROUTER_API_KEY" | jq '.data.input_schema'

# Run it with those inputs
curl -s "https://api.orcarouter.ai/v1/tools/$TOOL_ID/run" \
  -H "Authorization: Bearer $ORCAROUTER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"input": {"username": "duolingo"}}'
```

### Python

```python
# pip install requests openai
import json
import os

import requests
from openai import OpenAI

BASE = "https://api.orcarouter.ai"
KEY = os.environ["ORCAROUTER_API_KEY"]
HEADERS = {"Authorization": f"Bearer {KEY}"}

# One discover query per step of the recipe.
STEPS = ["tiktok user profile", "tiktok video search", "tiktok video comments"]
TASK = (
    "Compare the TikTok accounts @duolingo and @babbel: followers, "
    "what they posted recently and what commenters say about it."
)


def discover(query, limit=2):
    r = requests.post(f"{BASE}/v1/tools/discover", json={"q": query, "limit": limit}, headers=HEADERS, timeout=30)
    r.raise_for_status()
    return [tool["id"] for tool in r.json()["data"]["results"]]


def run(name, tool_input):
    r = requests.post(f"{BASE}/v1/tools/{name}/run", json={"input": tool_input}, headers=HEADERS, timeout=120)
    return r.text  # the tool's answer, or its error, goes back to the model


ids = [tool_id for query in STEPS for tool_id in discover(query)]
fns = requests.get(
    f"{BASE}/v1/tools/functions",
    params={"ids": ",".join(ids), "format": "openai"},
    headers=HEADERS,
    timeout=30,
)
fns.raise_for_status()
tools = fns.json()["data"]["tools"]

llm = OpenAI(base_url=f"{BASE}/v1", api_key=KEY)
messages = [{"role": "user", "content": TASK}]
for _ in range(10):
    msg = llm.chat.completions.create(model="openai/gpt-5-mini", messages=messages, tools=tools).choices[0].message
    messages.append(msg)
    if not msg.tool_calls:
        print(msg.content)
        break
    for call in msg.tool_calls:
        result = run(call.function.name, json.loads(call.function.arguments or "{}"))
        messages.append({"role": "tool", "tool_call_id": call.id, "content": result})
```

### TypeScript

```typescript
// npm install openai
import OpenAI from "openai";
import type { ChatCompletionMessageParam } from "openai/resources/chat/completions";

const BASE = "https://api.orcarouter.ai";
const KEY = process.env.ORCAROUTER_API_KEY!;
const headers = { Authorization: `Bearer ${KEY}`, "Content-Type": "application/json" };

// One discover query per step of the recipe.
const STEPS = ["tiktok user profile", "tiktok video search", "tiktok video comments"];
const TASK =
  "Compare the TikTok accounts @duolingo and @babbel: followers, " +
  "what they posted recently and what commenters say about it.";

async function discover(q: string, limit = 2): Promise<string[]> {
  const res = await fetch(`${BASE}/v1/tools/discover`, {
    method: "POST",
    headers,
    body: JSON.stringify({ q, limit }),
  });
  if (!res.ok) throw new Error(`discover: HTTP ${res.status}`);
  const { data } = await res.json();
  return data.results.map((tool: { id: string }) => tool.id);
}

async function run(name: string, input: unknown): Promise<string> {
  const res = await fetch(`${BASE}/v1/tools/${name}/run`, {
    method: "POST",
    headers,
    body: JSON.stringify({ input }),
  });
  return res.text(); // the tool's answer, or its error, goes back to the model
}

async function main() {
  const ids = (await Promise.all(STEPS.map((q) => discover(q)))).flat();
  const fns = await fetch(
    `${BASE}/v1/tools/functions?format=openai&ids=${encodeURIComponent(ids.join(","))}`,
    { headers },
  );
  if (!fns.ok) throw new Error(`functions: HTTP ${fns.status}`);
  const { data } = await fns.json();

  const llm = new OpenAI({ baseURL: `${BASE}/v1`, apiKey: KEY });
  const messages: ChatCompletionMessageParam[] = [{ role: "user", content: TASK }];
  for (let turn = 0; turn < 10; turn++) {
    const reply = await llm.chat.completions.create({ model: "openai/gpt-5-mini", messages, tools: data.tools });
    const msg = reply.choices[0].message;
    messages.push(msg);
    if (!msg.tool_calls?.length) {
      console.log(msg.content);
      return;
    }
    for (const call of msg.tool_calls) {
      const fn = call.type === "function" ? call.function : undefined;
      const result = fn ? await run(fn.name, JSON.parse(fn.arguments || "{}")) : "unsupported tool call";
      messages.push({ role: "tool", tool_call_id: call.id, content: result });
    }
  }
}

main();
```

## Related recipes

- [Monitor brand mentions on the web and TikTok](https://www.orcarouter.ai/tools/recipes/monitor-brand-mentions.md): Search the web and TikTok for your brand, then read the comments under the posts that mention it: a daily brand check an agent can run on its own.
- [Enrich a lead list](https://www.orcarouter.ai/tools/recipes/enrich-a-lead-list.md): Turn a list of company domains into firmographics, a contact's work email and a recent news hook, with one API key.
- [Find contact emails for a list of domains](https://www.orcarouter.ai/tools/recipes/find-contact-emails-for-domains.md): Go from a bare domain to the right person's work email: confirm the company, find who holds the role and look up the address.

---

Markdown version of https://www.orcarouter.ai/tools/recipes/track-competitor-tiktok-accounts
