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
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.
Example input { "username": "duolingo" }Tools for “Read each account's profile” Tool Price Success (7 days) Median latency Runs (30 days) Tier TikTok ProfileFetch a TikTok creator's public profile (followers, likes, bio, verification) by handle. $0.000495 / call 100.0% 1.4 s 1 bridged 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 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.
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
# 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"}}'# 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})// 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();More recipes
- Monitor brand mentions on the web and TikTokSearch 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 listTurn 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 domainsGo from a bare domain to the right person's work email: confirm the company, find who holds the role and look up the address.
