Needle's MCP server lets an AI assistant work with your Needle account: see who to reply to, read a lead in full, draft a reply in your brand voice, mark leads as handled, and run new searches.
- URL:
https://api.useneedle.net/mcp(Streamable HTTP, stateless; MCP revisions 2025-11-25 and 2026-07-28) - Auth: an API key from Settings → API & MCP
Set up
Claude Code
claude mcp add --transport http needle https://api.useneedle.net/mcp --header "Authorization: Bearer ndl_live_…"
Cursor, Windsurf and other clients (mcp.json):
{
"mcpServers": {
"needle": {
"url": "https://api.useneedle.net/mcp",
"headers": { "Authorization": "Bearer ndl_live_…" }
}
}
}
claude.ai and ChatGPT custom connectors need an OAuth sign-in, which Needle does not offer yet. Until then, use a client that accepts a header, or the REST API.
A typical session: get_leads → get_lead → draft_reply → you post the reply → update_lead_status with replied.
Tools
list_brands
List the brands (products) this Needle account monitors, with their keywords, networks and monitoring schedule.
Use it first when you need a brand_id for get_leads or search, or to explain what Needle is watching.
Example: list_brands {}
Scope: leads:read. Counts as one API call. Read-only.
get_leads
Use get_leads to answer 'who should I reply to today?'. Returns saved leads (social posts from people who may need the user's product), newest first.
By default it returns only actionable leads (tier hot_lead or worth_engaging) still in the 'inbox'. Pass tier='all' to include 'monitor' posts, status='all' for every triage state.
Filters: brand_id (omit to merge all brands), intent_type (looking_for_tool, pain, switching, question), platform, since (ISO 8601 or '24h' / '7d'). Up to 50 per page; pass next_cursor back as cursor for more. Keep paging while next_cursor is set, even if a page has fewer leads than the limit.
Example: get_leads { "since": "24h", "limit": 10 }
Scope: leads:read. Counts as one API call. Read-only.
get_lead
Get one lead with its full post text (up to 4,000 characters), tier, intent and the evidence sentence.
Use it before drafting or judging a reply when the excerpt from get_leads is not enough.
Example: get_lead { "lead_id": "Ab12Cd34Ef56Gh78Ij90" }
Scope: leads:read. Counts as one API call. Read-only.
update_lead_status
Set the triage status of up to 100 leads: 'inbox', 'replied', 'done' or 'dismissed'.
Use it after the user replies to a lead ('replied'), finishes it ('done'), or decides to skip it ('dismissed'). It only changes Needle's triage state; nothing is posted anywhere.
Example: update_lead_status { "lead_ids": ["Ab12Cd34Ef56Gh78Ij90"], "status": "dismissed" }
Scope: leads:write. Counts as one API call.
search
Run a live search for 1-5 keywords across community networks (Reddit, Hacker News, forums and more) and return the best leads found, at most 10 per network and 40 in total.
Use it when the user wants fresh posts for new keywords, not ones already monitored (use get_leads for those). Each call uses one of the user's daily search runs, shared with the dashboard.
By default it searches the plan's networks except X, LinkedIn and YouTube (slow and metered); name them in networks to include them. timeframe: 1h, 24h, 7d (default) or 30d (plan permitting).
Searches can take a few minutes. If status is 'running', call get_search with the search_id after about 30 seconds. Only one search runs at a time.
Example: search { "keywords": ["crm for freelancers", "hubspot alternative"], "timeframe": "7d" }
Scope: search:run. Counts as one API call.
get_search
Get the status and leads of a search started with search. Free: does not count against the monthly call quota.
Use it to poll a search that returned status 'running' (wait about 30 seconds between calls).
Example: get_search { "search_id": "aBcD1234eFgH5678iJkL" }
Scope: search:run. Free. Read-only.
draft_reply
Write two reply drafts (best and alternate) for one lead, in the user's brand voice. Needle never posts: the drafts are text for the user to review and post themselves.
Each call uses one of the user's daily reply drafts. tone: casual (default), professional, witty. strategy: subtle (default), direct, or pure_value (no product mention). If fit_warning is set, tell the user why the lead may be a poor fit.
Example: draft_reply { "lead_id": "Ab12Cd34Ef56Gh78Ij90", "tone": "casual" }
Scope: drafts:write. Counts as one API call.
get_trending_problems
List problems people are currently discussing across communities, ranked by how much they are trending. Not specific to the user's brands.
Use it for content ideas or market research. Optional category filter; up to 50 results (capped by plan).
Example: get_trending_problems { "limit": 10 }
Scope: trending:read. Counts as one API call. Read-only.
get_usage
Show the user's plan, API calls left this month, and searches and reply drafts left today, with reset times (UTC). Free: does not count against the quota.
Use it before a batch of searches or drafts, or when a call fails with a limit error.
Example: get_usage {}
Scope: any key. Free. Read-only.
API keys
Create a key in Settings → API & MCP. Keys start with ndl_live_ and are shown once, so store them like a password. Revoke a key there at any time; it stops working within seconds. Keys never expire unless you pick an expiry (30, 90 or 365 days).
Each key has scopes. All are ticked by default; untick some for a narrower key, for example a read-only dashboard key.
| Scope | Allows |
|---|---|
leads:read |
Read your brands and leads |
leads:write |
Change lead status (replied, done, dismissed) |
search:run |
Run searches (uses your daily search runs) |
drafts:write |
Generate reply drafts (uses your daily drafts) |
trending:read |
Read trending problems |
Send the key as Authorization: Bearer ndl_live_… (or x-api-key: ndl_live_…). Never put it in a URL.
Plans and limits
The API and MCP server are included in the Growth and Scale plans.
| Growth | Scale | |
|---|---|---|
| API calls a month | 1,000 | 10,000 |
| Requests a minute | 60 per key, 300 per account | 60 per key, 300 per account |
| Searches and reply drafts | your daily plan limits, shared with the dashboard | same |
| API keys | up to 10 active | up to 10 active |
- Every tool call counts as one API call, only when it succeeds.
get_usageandget_searchare free. searchcounts when it starts. A search that fails or is interrupted later does not use one of your daily searches, but the call that started it stays counted.- Monthly calls reset on the 1st at 00:00 UTC. Daily searches and drafts reset at 00:00 UTC.
- One search runs at a time per account.
- Needle never posts for you. Reply drafts are text for you to review and post yourself.
Errors
Errors have a stable code you can rely on:
{ "error": { "code": "daily_limit_exceeded", "message": "No searches left today. Searches reset at 00:00 UTC." } }
| Code | Meaning |
|---|---|
invalid_request |
Bad input. details.fields lists each problem. |
invalid_token |
Missing, unknown, revoked or expired key. |
insufficient_scope |
The key lacks the scope in details.required_scope. |
account_deleted |
The account was deleted. |
api_plan_required |
The plan has no API access (Growth and Scale do). Keys are kept. |
api_access_disabled |
API access was disabled for this account. Contact support. |
not_found |
Not found, or not yours. |
rate_limited |
Too many requests. Wait Retry-After seconds. |
monthly_quota_exceeded |
Monthly API call limit reached. details.resets_at says when it resets (no Retry-After: retrying before then cannot succeed). |
daily_limit_exceeded |
No searches or reply drafts left today. Resets 00:00 UTC. |
plan_restricted |
The plan does not allow this network, timeframe or feature. |
branding_required |
Add a company name and product description in Settings before drafting. |
search_in_progress |
A search is already running (details.search_id). Poll it first. |
search_failed |
The search failed on every network. Try again. |
busy |
Capacity is busy. Retry shortly. |
service_disabled |
The API is temporarily off. Retry later. |
upstream_error |
An upstream service failed. Retry shortly. |
internal_error |
Our fault. Retry; the call is not counted. |