Install
$ agentstack add mcp-oneinterface-stormy-cookbook ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ● Network access Used
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ● Environment & secrets Used
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
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.
[](LICENSE) [](https://github.com/OneInterface/stormy-cookbook/actions/workflows/link-check.yml) [](https://modelcontextprotocol.io) [](#what-you-can-do-on-each-network) [](https://stormy.ai/docs)
Jump to: [Recipes](#recipes) · [MCP quickstart](#quickstart-a-connect-the-mcp-server) · [REST quickstart](#quickstart-b-call-the-rest-api-directly) · [Endpoints](#the-whole-api-on-one-screen) · [Pricing](#pricing) · [Errors](#errors-and-retries) · [FAQ](#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.
Per-network field lists (click to expand)
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](recipes/README.md).
| # | Recipe | Networks | What you get | | --- | --- | --- | --- | | 01 | [Find influencers by niche](recipes/01-find-influencers-by-niche.md) | TikTok, YouTube, Instagram | A ranked shortlist of creators with follower counts and engagement | | 02 | [Build an outreach list with verified emails](recipes/02-build-an-outreach-list-with-verified-emails.md) | TikTok, YouTube, Instagram | Search → filter → /emails → CSV, paying only for hits | | 03 | [Enrich a CRM from handles](recipes/03-enrich-a-crm-from-handles.md) | All six | Handles in, normalized profile rows out | | 04 | [Competitor content analysis](recipes/04-competitor-content-analysis.md) | TikTok, YouTube, Instagram | Which of a rival's posts actually worked, and why | | 05 | [Monitor a creator over time](recipes/05-monitor-a-creator-over-time.md) | Any | A daily snapshot job and a growth delta | | 06 | [Cross-platform audience research](recipes/06-cross-platform-audience-research.md) | All six | One query fanned out across six networks, merged | | 07 | [Reddit topic listening](recipes/07-reddit-topic-listening.md) | Reddit, X | Which threads are moving on a topic you care about | | 08 | [Durable jobs for large collections](recipes/08-durable-jobs-for-large-collections.md) | All six | 1,000+ results without holding an HTTP connection | | 09 | [Use Stormy from an MCP agent](recipes/09-agent-workflows-with-mcp.md) | All six | Prompts + tool policy for Claude Code, Cursor, Codex | | 10 | [Handle errors, rate limits and billing](recipes/10-errors-rate-limits-and-billing.md) | — | 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](recipes/10-errors-rate-limits-and-billing.md).
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 verifi
…
Source & license
This open-source MCP server is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: OneInterface
- Source: OneInterface/stormy-cookbook
- License: MIT
- Homepage: https://stormy.ai
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.