curl --request POST \
--url https://api.superx.so/v1/signals/leads/search \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"keywords": "cancelled today, mrr dropped, renewal call, churn rate",
"icp_description": "Founders of small B2B SaaS companies who are worried about retention and would try a churn-reduction tool.",
"offer": "ChurnRadar, retention analytics that flags the accounts about to cancel.",
"precision": "discovery",
"max_leads": 10,
"max_post_age_days": 7
}
'import requests
url = "https://api.superx.so/v1/signals/leads/search"
payload = {
"keywords": "cancelled today, mrr dropped, renewal call, churn rate",
"icp_description": "Founders of small B2B SaaS companies who are worried about retention and would try a churn-reduction tool.",
"offer": "ChurnRadar, retention analytics that flags the accounts about to cancel.",
"precision": "discovery",
"max_leads": 10,
"max_post_age_days": 7
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
keywords: 'cancelled today, mrr dropped, renewal call, churn rate',
icp_description: 'Founders of small B2B SaaS companies who are worried about retention and would try a churn-reduction tool.',
offer: 'ChurnRadar, retention analytics that flags the accounts about to cancel.',
precision: 'discovery',
max_leads: 10,
max_post_age_days: 7
})
};
fetch('https://api.superx.so/v1/signals/leads/search', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.superx.so/v1/signals/leads/search",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'keywords' => 'cancelled today, mrr dropped, renewal call, churn rate',
'icp_description' => 'Founders of small B2B SaaS companies who are worried about retention and would try a churn-reduction tool.',
'offer' => 'ChurnRadar, retention analytics that flags the accounts about to cancel.',
'precision' => 'discovery',
'max_leads' => 10,
'max_post_age_days' => 7
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.superx.so/v1/signals/leads/search"
payload := strings.NewReader("{\n \"keywords\": \"cancelled today, mrr dropped, renewal call, churn rate\",\n \"icp_description\": \"Founders of small B2B SaaS companies who are worried about retention and would try a churn-reduction tool.\",\n \"offer\": \"ChurnRadar, retention analytics that flags the accounts about to cancel.\",\n \"precision\": \"discovery\",\n \"max_leads\": 10,\n \"max_post_age_days\": 7\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.superx.so/v1/signals/leads/search")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"keywords\": \"cancelled today, mrr dropped, renewal call, churn rate\",\n \"icp_description\": \"Founders of small B2B SaaS companies who are worried about retention and would try a churn-reduction tool.\",\n \"offer\": \"ChurnRadar, retention analytics that flags the accounts about to cancel.\",\n \"precision\": \"discovery\",\n \"max_leads\": 10,\n \"max_post_age_days\": 7\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.superx.so/v1/signals/leads/search")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"keywords\": \"cancelled today, mrr dropped, renewal call, churn rate\",\n \"icp_description\": \"Founders of small B2B SaaS companies who are worried about retention and would try a churn-reduction tool.\",\n \"offer\": \"ChurnRadar, retention analytics that flags the accounts about to cancel.\",\n \"precision\": \"discovery\",\n \"max_leads\": 10,\n \"max_post_age_days\": 7\n}"
response = http.request(request)
puts response.read_body{
"data": {
"query_used": "(\"cancelled today\" OR \"they churned\" OR \"lost a customer\") (saas OR mrr OR retention) lang:en -filter:replies -filter:retweets",
"query_source": "planned",
"queries_used": 10,
"query_plan": [
{
"query": "(\"cancelled today\" OR \"they churned\" OR \"lost a customer\") (saas OR mrr OR retention) lang:en -filter:replies -filter:retweets",
"angle": "workflow",
"posts_in_window": 24,
"candidates": 11
},
{
"query": "(churnzero OR chartmogul OR baremetrics) (churn OR retention OR mrr) lang:en -filter:replies -filter:retweets",
"angle": "competitor",
"posts_in_window": 9,
"candidates": 4
}
],
"partial": false,
"precision": "discovery",
"requests_used": 12,
"posts_scanned": 194,
"candidates_scored": 60,
"credits_charged": 2,
"freshness": {
"max_post_age_days": 7,
"posts_in_window": 31,
"stale_skipped": 4,
"oldest_skipped_days": 212
},
"leads": [
{
"user": {
"x_user_id": "1178367350552305665",
"username": "robj3d3",
"name": "Rob",
"avatar_url": "https://pbs.twimg.com/profile_images/abc_normal.jpg",
"followers_count": 8213,
"posts_count": 4102
},
"icp_score": 4,
"icp_rationale": "Runs a small B2B SaaS and is actively complaining about cancellations.",
"provenance": {
"action": "authored",
"tweet_id": "1969318805251100000",
"tweet_text": "third cancellation this week and I still cannot tell why they left",
"target_handle": null,
"posted_at": "2026-09-12T08:41:07.000Z",
"post_age_days": 2
}
}
]
},
"note": "Nothing was saved: this search creates no signal agent and stores no leads."
}Search for leads on X now
One live keyword search over X, scored against an ideal-customer profile, returned in the response.
It creates nothing. No signal agent, no saved leads, no contact
list membership: the leads exist only in this response. For an
audience that keeps filling up on its own, create a signal agent
(POST /v1/signals/agents) instead, and read what it finds with
GET /v1/signals/leads.
It plans, then searches. From offer, icp_description and your
seed angles, a model writes up to 10 short queries across the angles
that find buyers (the buyer’s working day, their pains, the tools they
already pay for, adjacent roles), runs them all, and scores everyone it
finds. queries_used says how many reached X, query_plan shows each
query with its angle and what it contributed, and partial is true
when a time budget cut the run short.
So keywords are seed ANGLES, not the query: 2-5 short phrases of how
the BUYER talks on a normal working day, not the product’s own name and
not a full sentence. offer is optional but it is the strongest input
you can give. precision: high keeps only confident matches;
discovery (the default) also returns adjacent ones.
The search takes up to a minute. Each lead comes from ONE matched
post, so treat posts_count as lifetime volume rather than proof of
current activity.
Only recent posts count. max_post_age_days sets the window
(1-90, 30 by default): a matching post older than that is skipped and
counted in freshness.stale_skipped, and every lead carries its
post’s posted_at and post_age_days. Leads come back freshest
first within each score. An empty leads with a stale_skipped
above 0 means people do post about this, just not inside the window:
broaden the keywords, or raise max_post_age_days when you want
people who are active over a longer stretch rather than a fresh pain
point.
Cost. Reading X live costs AI credits, measured from what the
search actually spends: at least 1 credit for any search that reaches
X, typically 1-2. It also uses one of the plan’s daily lead searches
(429 ai_action_limited with scope: "account") and a slice of a
platform-wide fair-use ceiling on live-data actions (scope: "platform", shared with every account, so wait and retry rather than
assuming your own quota is gone). A search that never reaches X is
not charged.
Nothing is written by this endpoint, but it still needs a key with
the write scope, because it spends AI credits and live requests:
every non-GET /v1 request needs that scope anyway. The MCP
search_leads tool needs the write scope for the same reason. On
both surfaces an account shared with you works, unlike the endpoints
that really write: nothing is written to the account, and the credits
come from the key owner’s pool.
curl --request POST \
--url https://api.superx.so/v1/signals/leads/search \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"keywords": "cancelled today, mrr dropped, renewal call, churn rate",
"icp_description": "Founders of small B2B SaaS companies who are worried about retention and would try a churn-reduction tool.",
"offer": "ChurnRadar, retention analytics that flags the accounts about to cancel.",
"precision": "discovery",
"max_leads": 10,
"max_post_age_days": 7
}
'import requests
url = "https://api.superx.so/v1/signals/leads/search"
payload = {
"keywords": "cancelled today, mrr dropped, renewal call, churn rate",
"icp_description": "Founders of small B2B SaaS companies who are worried about retention and would try a churn-reduction tool.",
"offer": "ChurnRadar, retention analytics that flags the accounts about to cancel.",
"precision": "discovery",
"max_leads": 10,
"max_post_age_days": 7
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
keywords: 'cancelled today, mrr dropped, renewal call, churn rate',
icp_description: 'Founders of small B2B SaaS companies who are worried about retention and would try a churn-reduction tool.',
offer: 'ChurnRadar, retention analytics that flags the accounts about to cancel.',
precision: 'discovery',
max_leads: 10,
max_post_age_days: 7
})
};
fetch('https://api.superx.so/v1/signals/leads/search', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.superx.so/v1/signals/leads/search",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'keywords' => 'cancelled today, mrr dropped, renewal call, churn rate',
'icp_description' => 'Founders of small B2B SaaS companies who are worried about retention and would try a churn-reduction tool.',
'offer' => 'ChurnRadar, retention analytics that flags the accounts about to cancel.',
'precision' => 'discovery',
'max_leads' => 10,
'max_post_age_days' => 7
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.superx.so/v1/signals/leads/search"
payload := strings.NewReader("{\n \"keywords\": \"cancelled today, mrr dropped, renewal call, churn rate\",\n \"icp_description\": \"Founders of small B2B SaaS companies who are worried about retention and would try a churn-reduction tool.\",\n \"offer\": \"ChurnRadar, retention analytics that flags the accounts about to cancel.\",\n \"precision\": \"discovery\",\n \"max_leads\": 10,\n \"max_post_age_days\": 7\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.superx.so/v1/signals/leads/search")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"keywords\": \"cancelled today, mrr dropped, renewal call, churn rate\",\n \"icp_description\": \"Founders of small B2B SaaS companies who are worried about retention and would try a churn-reduction tool.\",\n \"offer\": \"ChurnRadar, retention analytics that flags the accounts about to cancel.\",\n \"precision\": \"discovery\",\n \"max_leads\": 10,\n \"max_post_age_days\": 7\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.superx.so/v1/signals/leads/search")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"keywords\": \"cancelled today, mrr dropped, renewal call, churn rate\",\n \"icp_description\": \"Founders of small B2B SaaS companies who are worried about retention and would try a churn-reduction tool.\",\n \"offer\": \"ChurnRadar, retention analytics that flags the accounts about to cancel.\",\n \"precision\": \"discovery\",\n \"max_leads\": 10,\n \"max_post_age_days\": 7\n}"
response = http.request(request)
puts response.read_body{
"data": {
"query_used": "(\"cancelled today\" OR \"they churned\" OR \"lost a customer\") (saas OR mrr OR retention) lang:en -filter:replies -filter:retweets",
"query_source": "planned",
"queries_used": 10,
"query_plan": [
{
"query": "(\"cancelled today\" OR \"they churned\" OR \"lost a customer\") (saas OR mrr OR retention) lang:en -filter:replies -filter:retweets",
"angle": "workflow",
"posts_in_window": 24,
"candidates": 11
},
{
"query": "(churnzero OR chartmogul OR baremetrics) (churn OR retention OR mrr) lang:en -filter:replies -filter:retweets",
"angle": "competitor",
"posts_in_window": 9,
"candidates": 4
}
],
"partial": false,
"precision": "discovery",
"requests_used": 12,
"posts_scanned": 194,
"candidates_scored": 60,
"credits_charged": 2,
"freshness": {
"max_post_age_days": 7,
"posts_in_window": 31,
"stale_skipped": 4,
"oldest_skipped_days": 212
},
"leads": [
{
"user": {
"x_user_id": "1178367350552305665",
"username": "robj3d3",
"name": "Rob",
"avatar_url": "https://pbs.twimg.com/profile_images/abc_normal.jpg",
"followers_count": 8213,
"posts_count": 4102
},
"icp_score": 4,
"icp_rationale": "Runs a small B2B SaaS and is actively complaining about cancellations.",
"provenance": {
"action": "authored",
"tweet_id": "1969318805251100000",
"tweet_text": "third cancellation this week and I still cannot tell why they left",
"target_handle": null,
"posted_at": "2026-09-12T08:41:07.000Z",
"post_age_days": 2
}
}
]
},
"note": "Nothing was saved: this search creates no signal agent and stores no leads."
}Authorizations
A SuperX API key ("sxk_..."), created in the SuperX app under Account > API / MCP / CLI. Keys are server-side secrets.
Body
Seed ANGLES for the search plan, not the query itself: 2-5 short phrases (2-3 words each) of how the BUYER talks on a normal working day, their workflows, the tools they already pay for, their jargon, a specific symptom. One comma-separated string or a list of strings (a list is joined with commas), 3-300 characters in total. Never the product's own name, never a full sentence, no search operators.
Who counts as a good lead, in one or two sentences: role, domain, and the buying intent or pain that qualifies them.
3 - 500What you are selling, in one sentence: the product or service and
what it does. Optional, and the single strongest input: the search
plan is written from it, so a search with an offer finds people
talking about the problem rather than only people naming the
product.
3 - 300high keeps only confident matches; discovery also returns adjacent ones.
high, discovery 1 <= x <= 30Only posts written within the last N days count. Older matching
posts are skipped and counted in freshness.stale_skipped. Use 7
for a pain point or buying intent worth catching while it is
fresh, 1-3 for today, 30 for anyone who is simply active.
1 <= x <= 90Which of your accounts to search as. Omit for the main account.