"""Serper.dev client — Google SERP data for keyword ranking & competitor analysis.""" import os import requests from typing import List, Dict SERPER_KEY = os.getenv("SERPER_KEY", "") BASE_URL = "https://google.serper.dev" def _key() -> str: """Return first available serper key, rotating across SERPER_KEY, SERPER_KEY_2, etc.""" for suffix in ["", "_2", "_3", "_4", "_5"]: k = os.environ.get(f"SERPER_KEY{suffix}", "").strip() if k: return k return SERPER_KEY def search(query: str, gl: str = "sa", hl: str = "ar", num: int = 10) -> Dict: """Run a Google search via serper.dev. Returns organic results + answerBox.""" key = _key() if not key: return {"error": "no_serper_key"} try: r = requests.post( f"{BASE_URL}/search", headers={"X-API-KEY": key, "Content-Type": "application/json"}, json={"q": query, "gl": gl, "hl": hl, "num": num}, timeout=12, ) r.raise_for_status() return r.json() except Exception as e: return {"error": str(e)} def rank_check(site_url: str, keywords: List[str], gl: str = "sa", hl: str = "ar") -> List[Dict]: """ For each keyword, search Google and find where site_url appears. Returns list of {kw, position, url, title, snippet, found}. """ domain = site_url.replace("https://", "").replace("http://", "").rstrip("/").split("/")[0] results = [] for kw in keywords[:10]: # cap at 10 to save credits data = search(kw, gl=gl, hl=hl, num=10) organic = data.get("organic", []) found = None for item in organic: link = item.get("link", "") if domain in link: found = item break results.append({ "kw": kw, "position": found["position"] if found else None, "url": found["link"] if found else None, "title": found["title"] if found else None, "snippet": found["snippet"] if found else None, "found": bool(found), "top_competitor": organic[0] if organic and not found else None, }) return results def competitor_serp(site_url: str, keyword: str, gl: str = "sa", hl: str = "ar") -> Dict: """ Search for a keyword and return top 10 competitors with their positions. """ domain = site_url.replace("https://", "").replace("http://", "").rstrip("/").split("/")[0] data = search(keyword, gl=gl, hl=hl, num=10) organic = data.get("organic", []) competitors = [] site_position = None for item in organic: link = item.get("link", "") is_self = domain in link if is_self: site_position = item["position"] competitors.append({ "position": item.get("position"), "title": item.get("title"), "link": link, "snippet": item.get("snippet", ""), "is_self": is_self, }) return { "keyword": keyword, "site_position": site_position, "competitors": competitors, "answer_box": data.get("answerBox"), "knowledge_graph": data.get("knowledgeGraph"), "related_searches": data.get("relatedSearches", []), }