Stormy Cookbook — TikTok, YouTube, Instagram, LinkedIn, X and Reddit API recipes for AI agents
Open-source, copy-pasteable recipes for the Stormy Social Data API and the Stormy MCP server (Model Context Protocol, Streamable HTTP).
One HTTP contract — search, profile, emails, jobs — across six social networks. No proxy pool, no six vendor SDKs, no scraper to babysit. Point your agent at https://stormy.ai/mcp, or curl the REST base at https://stormy.ai/api/v1.
Jump to: Recipes · MCP quickstart · REST quickstart · Endpoints · Pricing · Errors · FAQ
Why this exists
Every "get social data" project starts the same way: six different APIs, six auth schemes, six rate limits, six response shapes, and a scraper that breaks on a Tuesday. Then you bolt an LLM on top and discover none of it is shaped for an agent — no cost signal, no idempotency, no durable jobs, no machine-readable capability list.
Stormy is that layer, already built. This cookbook is how you use it.
Quickstart A: connect the MCP server
The Stormy MCP server speaks Streamable HTTP at https://stormy.ai/mcp and authenticates with an HTTP bearer token. Get a key at stormy.ai/account and export it:
export STORMY_API_KEY="stm_live_..." # never commit this
Claude Code
claude mcp add --transport http stormy https://stormy.ai/mcp \
--header "Authorization: Bearer $STORMY_API_KEY"
Codex CLI (~/.codex/config.toml)
[mcp_servers.stormy]
url = "https://stormy.ai/mcp"
bearer_token_env_var = "STORMY_API_KEY"
Cursor / Windsurf / any mcp.json client
{
"mcpServers": {
"stormy": {
"url": "https://stormy.ai/mcp",
"headers": {
"Authorization": "Bearer ${STORMY_API_KEY}"
}
}
}
}
ChatGPT custom connector
Name: Stormy Social Data
MCP URL: https://stormy.ai/mcp
Authentication: OAuth
The ten MCP tools you get
Then just ask:
Find 25 TikTok creators posting about home espresso, pull their follower counts, and tell me what it cost.
Quickstart B: call the REST API directly
Base URL: https://stormy.ai/api/v1 (also reachable at https://api.stormy.ai/api/v1).
Auth: Authorization: Bearer <key> or X-API-Key: <key>. Never put a key in a URL, a JSON body, a prompt, or an MCP tool argument.
curl
curl -X POST 'https://stormy.ai/api/v1/search' \
-H "Authorization: Bearer $STORMY_API_KEY" \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: espresso-tiktok-2026-07' \
-d '{
"platform": "tiktok",
"query": "home espresso and coffee gear creators",
"limit": 25,
"fresh": true
}'
Python
import os
import requests
response = requests.post(
"https://stormy.ai/api/v1/search",
headers={
"Authorization": f"Bearer {os.environ['STORMY_API_KEY']}",
"Idempotency-Key": "espresso-tiktok-2026-07",
},
json={
"platform": "tiktok",
"query": "home espresso and coffee gear creators",
"limit": 25,
"fresh": True,
},
timeout=90,
)
response.raise_for_status()
payload = response.json()
for creator in payload["results"]:
print(creator["handle"], creator["follower_count"])
print("cost:", payload["usage"]["cost_usd"], "USD")
TypeScript
const response = await fetch("https://stormy.ai/api/v1/search", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.STORMY_API_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": "espresso-tiktok-2026-07",
},
body: JSON.stringify({
platform: "tiktok",
query: "home espresso and coffee gear creators",
limit: 25,
fresh: true,
}),
});
if (!response.ok) throw new Error(await response.text());
const { results, usage } = await response.json();
console.log(results.length, "creators for", usage.cost_usd, "USD");
What comes back
{
"ok": true,
"platform": "tiktok",
"query": "home espresso and coffee gear creators",
"fresh": true,
"results": [
{
"id": "6812...",
"handle": "@homebarista",
"nickname": "Home Barista",
"url": "https://tiktok.com/@homebarista",
"signature": "Espresso at home, no snobbery.",
"verified": false,
"follower_count": 184000,
"following_count": 312,
"likes_count": 4210000,
"video_count": 612
}
],
"usage": {
"operation": "fresh_discovery",
"metered_results": 25,
"billed_results": 25,
"cost_usd": "2.00",
"credits": 200,
"plan": "paid",
"remaining_usage_usd": "23.00"
}
}
Every successful response carries a usage receipt. You are billed for outcomes, not requests.
What you can do on each network
Field lists are authoritative in GET /api/v1/capabilities → fields_by_platform. Treat every platform-specific field as nullable — you get it when it is public and the source supplied it.
TikTok — profile: id, sec_uid, handle, nickname, url, signature, avatar_url, verified, follower_count, following_count, likes_count, video_count · posts: video_id, url, description, create_time, duration, views, likes, comments, shares, saves, image_url, is_pinned, music, hashtags, caption_url
YouTube — profile: channel_id, handle, name, url, subscribers, description, videos_count, total_views, profile_image_url, banner_image_url, country, keywords, links · posts: video_id, url, title, description, published_at, duration, views, likes, comments, thumbnail_url, transcript, caption_url
Instagram — profile: username, full_name, biography, profile_pic_url, follower_count, following_count, posts_count, avg_engagement_rate, biolinks, country, is_verified, is_business_account, business_category_name · posts: media_id, post_url, caption, taken_at, like_count, comment_count, image_url, location, location_data, comments
X (Twitter) — profile: id, handle, name, url, description, profile_image_url, verified, location, website, followers, following, posts_count, joined_at · posts: id, url, text, created_at, language, likes, replies, reposts, quotes, views, bookmarks, conversation_id, author
LinkedIn — profile: id, name, linkedin_url, headline, country, country_iso_2, followers, total_posts, posts_last_6_months, posting_frequency, avg_likes, avg_comments, avg_reposts, avg_total_interactions, top_post_text, top_post_interactions, last_post_date, ai_summary, is_suitable_for_promotion, relevant_posts · posts: post_url, text, headline, posted_datetime, total_interactions, num_likes, num_comments, num_reposts, num_reactions_breakdown, poster_name, poster_linkedin_url
Reddit — profile: author, author_url, karma, account_created_at · posts: id, url, permalink, subreddit, author, title, text, created_at, score, upvote_ratio, comments_count, is_self, over_18
Recipes
Every recipe is a single runnable markdown file with real code and a real cost estimate. Full index with difficulty and pricing: recipes/README.md.
The whole API on one screen
Paid calls
Free calls
Legacy aliases /social/search, /social/profile and /social/emails still work; new clients should use the short paths.
Send an Idempotency-Key header on any paid call. Retrying with the same key never duplicates work or charges.
Machine-readable contract
Pricing
One credit is one US cent. You are charged for successful outcomes only — a verified-email lookup that finds nothing costs $0.00.
- Free preview: 50 cached results per rolling 30 days. No fresh data, no emails.
- Paid: $50 / month, including $25 (2,500 credits) of usage. Overage is metered at the rates above.
Worked examples (straight from GET /pricing):
Ask before you spend — estimate_price / GET /pricing is free:
curl 'https://stormy.ai/api/v1/pricing?quantity=250&include_email=true'
Errors and retries
Durable jobs never fail on a rate limit: they move to throttled without consuming an attempt and resume after the cooldown. Job statuses are queued, scheduled, running, throttled, retrying, succeeded, failed, cancelled. Poll only after poll_after_seconds, and stop when terminal is true.
Full worked handling in recipe 10.
Privacy and scope
- Stormy returns public social data only.
searchandprofileresponses haveemail,business_email,contact_email,phoneandphone_numberrecursively stripped at the API boundary. This is enforced server-side, not by convention.POST /emailsis the only path to contact data, it is opt-in, it is limited to Instagram / TikTok / YouTube, and it is billed only when a verified email is found.- Agents should call it only when a user explicitly asks for contact details.
FAQ
Yes — POST /api/v1/search with "platform": "tiktok", or the search_people MCP tool. You get creator search by niche, @handle → profile resolution, videos with views/likes/shares/saves/music/hashtags, and verified email enrichment. See recipe 01.
Yes. platform: "linkedin" on /search and /profile returns headline, follower count, posting frequency, average likes/comments/reposts, top post and an AI summary; include_posts adds posts with a full reaction breakdown. LinkedIn does not support /emails.
platform: "reddit" searches threads across subreddits and returns score, upvote_ratio, comments_count, permalink and author karma. See recipe 07.
Instagram, TikTok and YouTube. 1–25 targets per call. $0.15 per email actually found; misses are free. X, LinkedIn and Reddit are search/profile only.
No. One STORMY_API_KEY covers Instagram, YouTube, TikTok, X, LinkedIn and Reddit over both REST and MCP, with one rate card, one usage balance and one receipt format.
None, functionally — they are two faces of the same service, entitlements and rate card. MCP is for agents that discover tools at runtime; REST is for your own code. Mix them freely; a job started over REST is visible over MCP and vice versa.
Send a stable Idempotency-Key header (REST) or idempotency_key argument (MCP jobs) derived from the user's intent. Replaying the same key returns the original result and the original charge.
Contributing
Recipes, fixes and new language ports are welcome. Read CONTRIBUTING.md — the short version: one recipe per file, every snippet must run, no invented parameters, and never commit a key.
License
MIT. The recipes are yours to lift into production.