Agent recipe

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

    User profile · TikTok · 8 tools · from $0.000495

    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.

    Example input
    {
      "username": "duolingo"
    }
    Tools for “Read each account's profile”
    ToolPriceSuccess (7 days)Median latencyRuns (30 days)Tier
    TikTok ProfileFetch a TikTok creator's public profile (followers, likes, bio, verification) by handle.$0.000495 / call100.0%1.4 s1bridged
    TikTok user profile by username or secUidPublic TikTok profile by username (uniqueId) or secUid$0.0011 / call———bridged
    TikTok creator info and milestones by user idGet creator info and milestones$0.0011 / call———bridged

    See all 8 tools for this step

  2. Find their recent videos

    Video search · TikTok

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

    Example input
    {
      "keyword": "duolingo"
    }

    No tool in the catalog serves this step right now. The rest of the recipe still runs; check back for this one.

  3. Read what viewers say

    Video comments · TikTok

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

    Example input
    {
      "video_id": "VIDEO_ID_FROM_STEP_2"
    }

    No tool in the catalog serves this step right now. The rest of the recipe still runs; check back for this one.

Run the whole recipe

curl
# 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
# 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
// 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();
© 2026 OrcaRouter

For Providers

Run an inference platform? Get your models on OrcaRouter.

providers@orcarouter.ai

Join our community

Discordsupport@orcarouter.aiXLinkedInGitHubYouTube