Back to use cases

    Use case / Marketers / Python 3 / Published

    AI Visibility Tracker: Check If Your Brand Shows Up in AI Answers

    Card for AI Visibility Tracker: Check If Your Brand Shows Up in AI Answers

    AI Answer Check is a small Python script that works as an AI visibility tracker. Give it your brand, a few other brands in your category and 15 to 20 buyer questions. It asks each question to an AI answer built from live web and X sources and tells you whether your brand is named, where, and which sites the answer cites.

    The problem

    Buyers now ask AI tools "what is the best tool for X" and act on the answer. Marketers want to know if their brand appears, how often and next to whom. Asking by hand gives one answer at a time, and answers change from run to run, so a single check says little.

    What the app does

    • Reads one JSON file with your brand, other brands and your buyer questions.
    • Asks each question several times (default 3) with a fixed, neutral system message.
    • Records whether your brand is named and its position among the brands you listed.
    • Saves the sentence that names your brand and the domains the answer links to.
    • Writes a share-of-answer table, a per-question table and the top cited domains.
    • Can re-score saved answers with an edited config, with no new calls.

    How it works

    1. Ask. One POST /desearch/ai/search call per question and run, with tools: ["web", "twitter"] and result_type: "LINKS_WITH_FINAL_SUMMARY".
    2. Read the answer. The response holds the answer text with numbered citations, plus the web results and X posts behind it.
    3. Match brands as whole words, case-insensitive, with optional aliases.
    4. Score. Count runs where your brand appears, its position, and how stable the brand set is across runs.
    5. Write answer-check.md, runs.csv and a full JSON snapshot with every call and its cost.

    The code

        def ai_search(self, body, tag="call"):
            """One non-streamed AI Search call; one retry on 429/5xx/network error. Returns (ok, body, call)."""
            attempt = 0
            while True:
                attempt += 1
                ts = dt.datetime.now().astimezone().isoformat(timespec="seconds")
                t0 = time.perf_counter()
                status, headers, data, err = None, {}, None, None
                try:
                    r = self.s.post(self.base + ENDPOINT, json=body, timeout=self.timeout,
                                    headers={"Authorization": self._key, "Content-Type": "application/json"})
                    status, headers = r.status_code, {k.lower(): v for k, v in r.headers.items()}
                    try:
                        data = r.json()
                    except ValueError:
                        data = r.text
                except Exception as e:  # network error
                    err = f"{type(e).__name__}: {e}"
                lat = round(time.perf_counter() - t0, 3)
                cost = headers.get("x-desearch-cost-usd")
                call = {"ts": ts, "tag": tag, "attempt": attempt, "status": status, "latency_s": lat,
                        "ok": status == 200 and isinstance(data, dict) and err is None,
                        "cost_usd": float(cost) if cost not in (None, "") else None,
                        "usage_count": int(headers["x-desearch-usage-count"]) if headers.get("x-desearch-usage-count") else None,
                        "service": headers.get("x-desearch-service"), "error": err,
                        "content_type": headers.get("content-type")}
                if not call["ok"] and err is None:
                    call["error"] = (json.dumps(data)[:300] if not isinstance(data, str) else data[:300])
                self.calls.append(call)
                self._save(f"{len(self.calls):03d}-{tag}.json", {
                    "request": {"method": "POST", "url": self.base + ENDPOINT,
                                "headers": {"Authorization": "<redacted>"}, "body": body},
                    "call": call, "response_headers": headers, "body": data})
                retry = err is not None or status == 429 or (status or 0) >= 500
                print(f"  {tag} {status} {lat}s cost={cost}", file=sys.stderr)
                if call["ok"] or not retry or attempt >= 2:
                    return call["ok"], data, call
                time.sleep(3)
    

    The folder has answercheck.py, an example config in examples/, offline tests in test_answercheck.py and a full report in sample-output.md.

    Endpoints

    • POST /desearch/ai/search

    Example output

    From a run on 2026-10-10: 18 buyer questions in one software category, 3 runs each, plus 3 questions asked again without the system message (63 calls).

    OutputWhat it shows
    Share of answerhow many of the 54 answers named each brand
    Per questionnamed in how many of 3 runs, positions, same brand set or not
    Top cited domainswhich sites the answers link to most

    The cited domains list doubles as an outreach list: these are the pages AI answers lean on for your category.

    Run it yourself

    The public examples folder linked above includes the script and a README. Install its requirements, set DESEARCH_API_KEY from console.desearch.ai, and use the run command in that README.

    Useful flags: --runs (default 3), --questions N (only the first N), --ab N (also ask without the system message), --max-spend (default $0.50), --reanalyse RUN_DIR (re-score saved answers, no cost).

    What it costs

    Every call cost $0.004 (10 billed units at the AI Search rate) on 2026-10-10. A plain 18 questions x 3 runs check is 54 calls and $0.216. See pricing.

    Ideas to extend it

    • Run it monthly and chart your share of answer over time.
    • Pitch the top cited domains so your brand is in the sources AI answers read.
    • Add more questions per buyer stage (research, comparison, alternatives).

    Use with AI agents

    Point an MCP client at the hosted Desearch server. Replace the placeholder with a key from the console. Do not commit the key.

    {
      "mcpServers": {
        "desearch": {
          "url": "https://mcp.desearch.ai/mcp",
          "headers": {
            "Authorization": "Bearer <DESEARCH_API_KEY>"
          }
        }
      }
    }

    Prompt you can paste for this example:

    Use Desearch AI search to answer the buyer questions I list, with web and X tools. For each answer, say whether my brand is named, where it appears, and which sources are cited. Do not invent citations.

    Build your own example.

    Give your agents access to web and social search. Get an API key to start building, or talk to us about your integration.

    Read docs