Skip to content

Commit aa09eb2

Browse files
kriptoburakclaude
andcommitted
feat: add Xquik as X/Twitter search engine for online research
Adds search_with_xquik as a sixth search engine alongside Serper, Exa, Firecrawl, Google, and SearXNG. Activates when XQUIK_API_KEY env var is set. Returns results in the same {organic} format. Adds real-time social media perspectives (tweets, expert opinions, community sentiment) to research results — a dimension currently missing from web-only search engines. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 9258f57 commit aa09eb2

1 file changed

Lines changed: 69 additions & 0 deletions

File tree

src/khoj/processor/tools/online_search.py

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@
4646
SEARXNG_URL = os.getenv("KHOJ_SEARXNG_URL")
4747
# Exa API credentials
4848
EXA_API_KEY = os.getenv("EXA_API_KEY")
49+
# Xquik API credentials (X/Twitter search)
50+
XQUIK_API_KEY = os.getenv("XQUIK_API_KEY")
4951

5052
# Whether to automatically read web pages from search results
5153
AUTO_READ_WEBPAGE = is_env_var_true("KHOJ_AUTO_READ_WEBPAGE")
@@ -114,6 +116,9 @@ async def search_online(
114116
if SEARXNG_URL:
115117
search_engine = "Searxng"
116118
search_engines.append((search_engine, search_with_searxng))
119+
if XQUIK_API_KEY:
120+
search_engine = "Xquik"
121+
search_engines.append((search_engine, search_with_xquik))
117122

118123
if send_status_func:
119124
subqueries_str = "\n- " + "\n- ".join(subqueries)
@@ -358,6 +363,70 @@ async def search_with_searxng(query: str, location: LocationData) -> Tuple[str,
358363
return query, {}
359364

360365

366+
async def search_with_xquik(query: str, location: LocationData) -> Tuple[str, Dict[str, List[Dict]]]:
367+
"""
368+
Search X/Twitter using Xquik API.
369+
Returns real-time social media perspectives, expert opinions, and community sentiment.
370+
371+
Args:
372+
query: The search query string
373+
location: Location data (unused — X/Twitter search is global)
374+
375+
Returns:
376+
Tuple containing the original query and a dictionary of search results
377+
"""
378+
xquik_api_url = "https://xquik.com/api/v1/x/tweets/search"
379+
headers = {"X-API-Key": XQUIK_API_KEY, "Accept": "application/json"}
380+
params = {"q": query, "limit": "10", "queryType": "Top"}
381+
382+
async with aiohttp.ClientSession() as session:
383+
try:
384+
async with session.get(
385+
xquik_api_url, headers=headers, params=params, timeout=WEBPAGE_REQUEST_TIMEOUT
386+
) as response:
387+
if response.status != 200:
388+
error_text = await response.text()
389+
logger.error(f"Xquik search failed: {error_text[:200]}")
390+
return query, {}
391+
392+
response_json = await response.json()
393+
tweets = response_json.get("tweets", [])
394+
395+
if is_none_or_empty(tweets):
396+
return query, {}
397+
398+
organic_results = []
399+
for tweet in tweets:
400+
author = tweet.get("author", {})
401+
username = author.get("username", "unknown")
402+
text = tweet.get("text", "")
403+
tweet_id = tweet.get("id", "")
404+
405+
likes = tweet.get("likeCount", 0)
406+
retweets = tweet.get("retweetCount", 0)
407+
views = tweet.get("viewCount", 0)
408+
409+
engagement = f"{likes} likes, {retweets} RTs"
410+
if views:
411+
engagement += f", {views} views"
412+
413+
organic_results.append(
414+
{
415+
"title": f"@{username}: {text[:120]}",
416+
"link": f"https://x.com/{username}/status/{tweet_id}",
417+
"snippet": text,
418+
"content": f"Tweet by @{username}:\n\n{text}\n\nEngagement: {engagement}\n\n"
419+
f"Source: X/Twitter (social media post, represents real-time opinion)",
420+
}
421+
)
422+
423+
return query, {"organic": organic_results}
424+
425+
except Exception as e:
426+
logger.error(f"Error searching with Xquik: {str(e)}")
427+
return query, {}
428+
429+
361430
async def search_with_google(query: str, location: LocationData) -> Tuple[str, Dict[str, List[Dict]]]:
362431
country_code = location.country_code.lower() if location and location.country_code else "us"
363432
base_url = "https://www.googleapis.com/customsearch/v1"

0 commit comments

Comments
 (0)