Back to use cases

    Use case / Analysts / Python 3 / Published

    Track Quote Posts of an X Launch with Python

    Give a few launch posts on X. The script loads each post, collects quote posts, and writes who quoted it, what they said, and any disclosure wording it found.

    Card for Track Quote Posts of an X Launch with Python

    Launch Quote Ledger takes a few product launch posts on X. For each one it fetches the post by id, finds quote posts of that launch, and writes a ledger of who quoted it, what they said, and any disclosure wording in the public text, such as ad, sponsored or partner.

    The problem

    Quote posts are where people react to a launch, but they are spread across accounts. Opening each one, noting the text, and scanning for disclosure wording is slow when a launch has dozens of quotes.

    What the app does

    • Reads launch post ids from config.json.
    • Loads each post with GET /twitter/post.
    • Collects quote posts with quoted_tweet_id:<id> and checks that each result quotes that id.
    • Scans the public text for disclosure wording and flags phrases that sit inside a negation, such as "not sponsored".
    • Writes quotes.csv, a Markdown table and a comparison of a few other query shapes.

    How it works

    1. Load the post. GET /twitter/post?id=<post id> returns the text, quote count and date.
    2. Find quotes. GET /twitter with quoted_tweet_id:<id>, is_quote=true, count and sort=Latest.
    3. Check the id. Each quote is kept when quoted_status_id matches the launch.
    4. Scan wording. The disclosure column is text that was found, not a judgement.
    5. Write the ledger. Quoters in the Markdown table are shown as Account A, Account B and so on.

    On 2026-10-15, every result of the quoted_tweet_id: query quoted the launch post: 60 of 60 across 3 launches. A search for the author handle with is_quote=true also returns quotes of that author's other posts, so the id filter is the one the ledger uses.

    The code

    def run_live(cfg):
        import requests
        key = api_key()
        s = requests.Session(); s.headers["Authorization"] = key
        raw = {"run_at": now(), "count": cfg["count"], "launches": [], "probes": []}
        for L in cfg["launches"]:
            url = f"https://x.com/{L['user']}/status/{L['id']}"
            e = {"label": L["label"], "id": L["id"], "user": L["user"], "calls": {}}
            e["calls"]["post"] = call(s, "/twitter/post", {"id": L["id"]}, f"{L['label']} post")
            variants = {
                "url_quote": {"query": f"url:{url}", "is_quote": "true"},
                "handle_quote": {"query": f"@{L['user']}", "is_quote": "true"},
                "handle_quote_blue": {"query": f"@{L['user']}", "is_quote": "true", "blue_verified": "true"},
                "quoted_id_quote": {"query": f"quoted_tweet_id:{L['id']}", "is_quote": "true"},
            }
            for k, p in variants.items():
                p = dict(p, count=cfg["count"], sort="Latest")
                e["calls"][k] = call(s, "/twitter", p, f"{L['label']} {k}")
            raw["launches"].append(e)
        raw["probes"].append(call(s, "/twitter/post", {"id": cfg["bad_id"]}, "probe bad post id"))
        raw["probes"].append(call(s, "/twitter/post", {"id": "not-a-number"}, "probe malformed post id"))
        raw["probes"].append(call(s, "/twitter", {"query": cfg["empty_query"], "is_quote": "true", "count": 20}, "probe empty quote search"))
        save_raw(raw, key)
        return raw, key
    

    quoted_tweet_id: worked in that run. It is an operator the example used, not a documented search feature. The key is read from the environment and is not written to the output.

    Endpoints

    • GET /twitter/post
    • GET /twitter

    Example output

    The 2026-10-15 sample is in sample-output.md. Each quoted_tweet_id: call that returned 20 quotes cost $0.003 by the response header. The CSV has one row per quote, with columns launch, variant, quote_id, quoter, quoter_blue, created_at, likes, views, disclosure_wording, negated and text.

    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.

    What it costs

    $0.003 per 20-quote quoted_tweet_id: call in the 2026-10-15 sample. The post lookup is a separate call. See pricing.

    Ideas to extend it

    • Keep only quotes that include disclosure wording.
    • Diff a second run to see new quotes since the last ledger.
    • Add the quote count from the launch post next to the number of quotes you actually fetched.

    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 to load the launch post ids I give you, then collect public quote posts of each one. List who quoted the launch, the text, and any disclosure wording such as ad or sponsored. Quote only posts you retrieve. Do not judge whether a post is an ad beyond the wording you found.

    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