Podcast Guest Shortlist is for a host who needs fresh guests on a niche show. It searches public X posts on the topic from the last 30 days, checks that each person posts about it more than once, and looks for past podcast pages. Each person is shown as a user. The host reviews the table and reaches out personally. The script does not draft a message or look for contact details.
How this differs
The Instagram influencer finder ranks public Instagram creators in a niche by followers and engagement. This script does not read Instagram. It starts from public X posts about a topic, confirms the person keeps posting about that topic, and notes whether a web search found past podcast pages.
The problem
Scrolling X for people who post well about a topic, then searching each name to see if they have been on a show, takes a full morning. A one-off post is a weak guest lead. A shop or a recipe page is not a guest.
What the app does
- Reads topic terms, the topic name, a 30-day window,
min_likes, how many people to check, and brand words fromconfig.json. - Searches X, drops posts outside the window, and drops posts that do not contain a topic term.
- Sends accounts whose display name or bio matches brand words to a skipped list.
- For each remaining person, reads their own posts on the same topic and marks "one post only" when there is just one.
- Searches the web for "
namepodcast", and once more with the topic word if the first pages do not match. - Writes a pitch angle from that person's newest on-topic post. The written table says "a user" and does not include account names or personal post links.
How it works
- Topic search.
GET /twitterwith the terms inside parentheses,sort=Latest,min_likes,count, andstart_date/end_dateasYYYY-MM-DD. - Their posts.
GET /twitter/post/userwithuser, the same topic query, andcount. The script sorts those posts by date. - Past shows.
GET /web?query=<display name> podcast. A second call adds the topic name when the first pages do not match. Otherwise the cell says "none found". - Write
output/shortlist.mdandoutput/guests.csv.
The code
The topic query and the two X requests:
def topic_query(terms):
parts = ['"%s"' % term if " " in term else term for term in terms]
return "(" + " OR ".join(parts) + ")"
def search_params(cfg, start, end):
return {
"query": topic_query(cfg["topic_terms"]),
"sort": "Latest",
"start_date": start.isoformat(),
"end_date": end.isoformat(),
"min_likes": int(cfg.get("min_likes", 20)),
"count": int(cfg.get("count", 50)),
}
def user_params(cfg, account):
return {"user": account, "query": topic_query(cfg["topic_terms"]), "count": int(cfg.get("per_user", 10))}
How a podcast page is counted:
def podcast_status(calls, tokens):
if not calls:
return "none found"
pages = as_list(calls[0].get("body"))
if any(page_matches(item, tokens) for item in pages):
return "found"
if len(calls) > 1 and any(page_matches(item, tokens) for item in as_list(calls[1].get("body"))):
return "found after adding the topic"
return "none found"
The live calls, after the topic search:
for person in people:
if person["brand"]:
continue
uparams = user_params(cfg, person["account"])
ures = session.get(BASE + "/twitter/post/user", params=uparams, timeout=90)
user_calls.append({"account": person["account"], "params": uparams, "status": ures.status_code, "body": _json(ures)})
first_query = "%s podcast" % (person["name"] or "a user")
first = session.get(BASE + "/web", params={"query": first_query}, timeout=90)
first_body = _json(first)
web_calls.append({"account": person["account"], "query": first_query, "status": first.status_code, "body": first_body})
if not any(page_matches(item, person["tokens"]) for item in as_list(first_body)):
second_query = "%s %s podcast" % (person["name"] or "a user", cfg["topic_name"])
second = session.get(BASE + "/web", params={"query": second_query}, timeout=90)
web_calls.append({
"account": person["account"], "query": second_query, "status": second.status_code, "body": _json(second),
})
The key is sent only in the Authorization header. The public table does not print account names.
Endpoints
GET /twitterGET /twitter/post/userGET /web
Example output
The 2026-10-14 sample used home baking terms, at least 20 likes, over 30 days. 50 posts came back. 15 people were checked. 13 were kept as individuals and 2 were skipped as brand-style accounts. Past podcast pages were found for 5 of the 15. One of those showed up only after the topic was added to the web search. The public table calls every person "a user".
Run it yourself
The public examples folder linked above includes the script, config.json, a saved prompt and a README. Install its requirements, set DESEARCH_API_KEY from console.desearch.ai, and use the run command in that README. You can also paste the saved prompt into an MCP client pointed at https://mcp.desearch.ai/mcp.
What it costs
A run is one topic search, then one posts call and one or two web calls for each person who is not skipped as a brand. The sample write-up does not add X-Desearch-Cost-Usd into a single total. See pricing.
Ideas to extend it
- Keep last month's accounts so the next shortlist is new people.
- Add a column for how many of the checked posts fall inside the 30-day window.
- Stop at fewer people while you try a new topic.
