
Open-source cookbook for the Stormy Social Data API and MCP server (Model Context Protocol) — one REST API for the TikTok API, YouTube API, Instagram API, LinkedIn API, X (Twitter) API and Reddit...
Overview
Open-source, copy-pasteable recipes for the and the (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. 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. The Stormy 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: Find 25 TikTok creators posting about home espresso, pull their follower counts, and tell me what it cost.
README
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
| Tool | What it does |
|---|---|
search_people(platform, query, limit=10, fresh=false) |
Search one network from a natural-language query |
lookup_profile(target, platform=null, fresh=false, include_posts=false) |
Resolve a URL / @handle / channel ID to a normalized profile |
find_emails(platform, targets) |
Verified contact emails for 1–25 Instagram, TikTok or YouTube profiles |
estimate_price(quantity=100, include_email=false) |
Rate card + a maximum estimate, spends nothing |
account_status() |
Plan, remaining prepaid usage, top-up URL |
describe_social_data() |
The machine-readable platform / field / pricing / workflow contract |
start_social_job(operation, arguments, idempotency_key, ...) |
Queue fresh, bulk or email work durably |
get_social_job(job_id) |
Status, progress, poll_after_seconds, result, full timeline |
list_social_jobs(status=null, limit=20) |
Recover prior work instead of resubmitting |
cancel_social_job(job_id) |
Cancel queued / scheduled / throttled / retrying work |
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 or X-API-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
| Network | POST /search |
POST /profile |
include_posts |
POST /emails |
Public fields |
|---|---|---|---|---|---|
| TikTok API | Creators by niche | @handle or URL |
Videos, views, shares, saves, music, hashtags | ✅ verified email | 27 |
| YouTube API | Channels by topic | Handle or channel ID | Videos, transcripts, captions | ✅ verified email | 25 |
| Instagram API | Creators by niche | Username or URL | Posts, captions, engagement, location | ✅ verified email | 23 |
| X (Twitter) API | Accounts and posts | @handle or URL |
Posts, views, bookmarks, conversation ID | — | 26 |
| LinkedIn API | People and companies | Profile URL | Posts, reaction breakdowns, cadence | — | 31 |
| Reddit API | Threads by topic | Author karma and age | Score, upvote ratio, comment count | — | 17 |
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.
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.
| # | Recipe | Networks | What you get |
|---|---|---|---|
| 01 | Find influencers by niche | TikTok, YouTube, Instagram | A ranked shortlist of creators with follower counts and engagement |
| 02 | Build an outreach list with verified emails | TikTok, YouTube, Instagram | Search → filter → /emails → CSV, paying only for hits |
| 03 | Enrich a CRM from handles | All six | Handles in, normalized profile rows out |
| 04 | Competitor content analysis | TikTok, YouTube, Instagram | Which of a rival’s posts actually worked, and why |
| 05 | Monitor a creator over time | Any | A daily snapshot job and a growth delta |
| 06 | Cross-platform audience research | All six | One query fanned out across six networks, merged |
| 07 | Reddit topic listening | Reddit, X | Which threads are moving on a topic you care about |
| 08 | Durable jobs for large collections | All six | 1,000+ results without holding an HTTP connection |
| 09 | Use Stormy from an MCP agent | All six | Prompts + tool policy for Claude Code, Cursor, Codex |
| 10 | Handle errors, rate limits and billing | — | 402 / 429 / 503 handling, headless top-up, idempotency |
The whole API on one screen
Paid calls
| Method | Path | Body | Notes |
|---|---|---|---|
POST |
/search |
platform, query, limit (1–100, default 10), fresh (default false) |
Cache-first. fresh=true calls a live provider |
POST |
/profile |
target, platform?, fresh, include_posts |
Returns data, not results. platform is optional when target is a URL |
POST |
/emails |
platform (instagram | tiktok | youtube), targets (1–25) |
The only endpoint that returns contact data |
POST |
/jobs |
operation, arguments, delay_seconds (0–604800), priority (−10…10), max_attempts (1–10) |
202 Accepted. operation ∈ search_people, lookup_profile, find_emails |
GET |
/jobs/{job_id} |
?include_events=true |
Status, progress and the event timeline |
GET |
/jobs |
?status=running&limit=20 |
List recent jobs |
DELETE |
/jobs/{job_id} |
— | Cancel non-terminal work |
Free calls
| Method | Path | Notes |
|---|---|---|
GET |
/pricing?quantity=100&include_email=true |
Authoritative rate card + a maximum estimate |
GET |
/capabilities |
Platforms, per-platform fields, endpoints, job statuses, agent policy |
GET |
/account |
Plan, usage_balance, scopes, top-up URL |
POST |
/account/top-up |
amount_usd, note? → an instant Stripe checkout_url |
GET POST DELETE |
/keys, /keys/{id} |
List, mint and revoke API keys |
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
| Resource | URL |
|---|---|
| Capabilities JSON | |
| OpenAPI | |
llms.txt |
|
| Human docs |
Pricing
One credit is one US cent. You are charged for successful outcomes only — a verified-email lookup that finds nothing costs $0.00.
| Operation | Price | When it applies |
|---|---|---|
| Cached result | $0.01 | fresh=false on /search or /profile |
| Fresh profile | $0.05 | /profile with fresh=true |
| Fresh discovery | $0.08 | Per matching person returned by /search with fresh=true |
| Verified email | $0.15 | Per email actually found by /emails |
- 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):
| Workload | Cost |
|---|---|
| 100 cached profiles | $1.00 |
| 100 fresh profiles | $5.00 |
| 100 fresh matching people | $8.00 |
| 100 verified emails | $15.00 |
| 100 fresh people + their verified emails | $23.00 |
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
| Status | Code | What to do |
|---|---|---|
400 |
invalid_request |
Fix the body. Unsupported platform, empty query, limit out of 1–100, more than 25 email targets |
401 |
invalid_token |
Key missing, expired, revoked or wrong |
402 |
upgrade_required |
Return the upgrade_url to the user |
402 |
insufficient_balance |
POST /account/top-up with error.recommended_topup_usd, hand back checkout_url, poll GET /account, retry |
404 |
job_not_found |
Job missing or owned by another account |
429 |
rate_limit |
Wait Retry-After seconds. Do not retry immediately |
429 |
provider cooldown | The shared upstream pool is cooling down — switch to a durable job |
503 |
provider_not_configured |
Our deployment problem, not your request. Retry later |
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
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.
Built by Stormy · docs · capabilities · [email protected]
Install
This server does not publish a one-line install command.
Open the repository installation guide New to MCP? Start with the mcpskill.org guide →Configuration
{
"mcpServers": {
"stormy": {
"url": "https://stormy.ai/mcp",
"headers": {
"Authorization": "Bearer ${STORMY_API_KEY}"
}
}
}
}