Quickstart
Send a listener body as a dry run, read the estimate, the plan and the screen, and show the user the price before anything is created
Send the body you would use to create a listener to POST /v1/listening/streams?dry_run=1. The answer says what the listener would cost each month in credits and in money, how it reads your keywords, what it would search on each platform and whether the use screen accepts it. A dry run costs 0 credits and creates nothing. Listeners themselves ship in LRel2a.
curl -X POST "https://www.socialcrawl.dev/v1/listening/streams?dry_run=1" \
-H "x-api-key: YOUR_API_KEY" -H "Content-Type: application/json" \
-d '{"keywords": ["Acme", "Acme Cloud"], "platforms": ["youtube", "reddit", "x", "news"], "context": "Acme Inc., the cloud software company, not the cartoon brand", "schedule": {"every": "1h", "max_credits_per_month": 20000, "name": "Acme brand"}}'A dry run is a read, so it carries no Idempotency-Key. dry_run=true works too. Sent without dry_run, the same call answers a 400 naming LRel2a, which ships the create, and charges nothing.
What comes back
Shortened. The figures are this body's estimate for October 2026 on an account that has not bought a pack, so the money is at the Starter list price:
{
"success": true,
"data": {
"dry_run": true,
"stream": {
"groups": [
{
"id": null,
"keywords": ["Acme", "Acme Cloud"],
"ast": { "or": [{ "term": "Acme" }, { "phrase": "Acme Cloud" }] },
"read_back": "Posts mentioning “Acme” or “Acme Cloud”"
}
],
"platforms": ["youtube", "reddit", "twitter", "news"],
"lanes": [
{ "platform": "youtube", "lane": "youtube/search/advanced", "search_in": "posts", "ordering": "top_results", "label": "Top results only" },
{ "platform": "reddit", "lane": "reddit/search", "search_in": "posts", "ordering": "newest_first", "label": "Newest first" },
{ "platform": "twitter", "lane": "twitter/search/tweets", "search_in": "posts", "ordering": "newest_first", "label": "Newest first" },
{ "platform": "news", "lane": "google_news/search", "search_in": "posts", "ordering": "window_sample", "label": "Time window" }
],
"schedule": { "every": "1h", "name": "Acme brand", "start_at": null },
"caps": { "max_credits_per_month": 20000, "max_credits_per_run": 20 },
"sponsored": "exclude",
"relevance": "filter",
"relevance_definition": "mentions"
},
"estimate": {
"month": "2026-10",
"slots_in_month": 744,
"searches_per_slot": 8,
"credits_per_month": 5952,
"credits_per_hour": 8,
"depth_range_credits_per_month": [5952, 14880],
"max_credits_per_slot": 20,
"recommended_max_credits_per_month": 16368,
"money_per_credit": { "amount": 0.006, "currency": "GBP", "basis": "starter_list" },
"money_per_month": { "amount": 89.28, "currency": "GBP" },
"by_platform": [
{ "platform": "reddit", "lane": "reddit/search", "search_in": "posts", "searches_per_slot": 2, "checks_per_month": 744, "credits_per_month": 1488, "max_pages": 3, "coverage": "newest_first", "empty_pages_refunded": true }
],
"by_group": [{ "group": null, "credits_per_month": 5952 }],
"duplicates": []
},
"plan": [
{ "group": null, "platform": "reddit", "lane": "reddit/search", "fetch_terms": ["Acme", "\"Acme Cloud\""], "local": [] }
],
"screen": { "result": "allowed", "target": null },
"warnings": [
"youtube/search/advanced returns its top results, not every new post; we deliver the new ones we find.",
"On twitter/search/tweets, a share of scheduled checks is skipped, with nothing billed, when the only source that answers costs more than the check; that share is measured once listeners run."
],
"next_step": "Show the user up to £89.28 a month (14,880 credits if every search reads its lane's page cap), usually less: about 5,952 credits at one page a search, and pages that find nothing new on reddit/search and twitter/search/tweets are refunded (no_new_items). Recommended cap 16,368 credits; ask before creating the listener. For a budget in money, set max_credits_per_month to the budget in GBP divided by money_per_credit, rounded down, and never below max_credits_per_slot (20 here). Convert a budget in another currency to GBP first, and say which rate you used."
},
"credits_used": 0,
"request_id": "req-..."
}data.streamis the listener as it would be stored, with noid.xcomes back astwitter, and each group carries its rule as a tree (ast) and in words (read_back). Readread_backto the user when they gave you a boolean rule.data.stream.lanessays how each platform orders what it returns: Newest first, Top results only or Time window. Lanes explains what each means.data.estimateis the monthly cost.money_per_monthis the most a month can bill, every search reading its lane's page cap (depth_range_credits_per_month[1]), in GBP at the account'smoney_per_credit.credits_per_monthis the typical month, one page a search. Pages that find nothing new on newest-first lanes are refunded, so the bill is usually lower still. Pricing shows the arithmetic.data.planhas one row per group and platform: the search terms sent (fetch_terms) and the conditions checked on each post (local).data.screenis the use screen's verdict:allowed,refusedorunavailable.warningsandnext_stepare written for the user. Relay them.
What to show the user
Before a listener is created, show the user money_per_month (the most a month can bill; the bill is usually less), the price per credit (money_per_credit) and the cap you will send as schedule.max_credits_per_month, and get a yes. recommended_max_credits_per_month is the longest month at one slot's maximum, plus 10%. For a budget in money, divide the budget in GBP by money_per_credit, round down, and never go below max_credits_per_slot. next_step says the same in one paragraph, so an agent can relay it as written.
For agents
The read tool socialcrawl_listening carries the dry_run action with the fields above. Fetch it as JSON, with no key: /agent-tools/socialcrawl_listening.json. It uses Anthropic's tool format (name, description, input_schema). For OpenAI function calling, rename input_schema to parameters. The model never sends an Idempotency-Key: a dry run is a read.
The OpenAPI entry for the route is in /openapi/listening.json.
