Answers

How do I monitor Weibo for new posts mentioning my brand?

Short answer

Run Weibo post search with sort=latest for each brand keyword every hour, and stop paging as soon as you reach a post you already stored. A page holds about 10 posts and costs $0.002, so 3 keywords checked hourly at 2 pages each (144 calls) cost $0.288 a day.

Say you run social for a skincare brand. You want every new Weibo post that names the brand or its two best-selling products in one sheet each morning, with reposts and likes, so the team can thank fans, answer complaints and spot a problem early. That is a keyword search sorted by newest first, run on a schedule, that remembers what it has already seen.

The endpoint

Weibo post search takes query and sort. With sort=latest you get the newest matching posts first, about 10 per page, each with text, time, author (ID, name, followers, verified), likes, comments, reposts, images and hashtags. Pass page.next_cursor back as cursor for the next page; each page is billed on its own.

What a day costs

Brand volume Pages per keyword per hour Calls a day (3 keywords) Cost a day
Quiet 1 72 $0.144
Typical 2 144 $0.288
Busy launch week 5 360 $0.72

Most hours you stop after the first page, because the second page is already old news. The early stop is where the savings come from.

The monitor

import json, os, requests

URL = "https://api.okdata.online/v1/weibo/posts/search"
H = {"Authorization": f"Bearer {os.environ['OKDATA_API_KEY']}"}
KEYWORDS = ["山野护肤", "山野精华", "山野防晒"]
MAX_PAGES = 5

seen = set(open("seen.txt").read().split()) if os.path.exists("seen.txt") else set()
new = []
for kw in KEYWORDS:
    cursor = None
    for _ in range(MAX_PAGES):
        params = {"query": kw, "sort": "latest", **({"cursor": cursor} if cursor else {})}
        r = requests.get(URL, headers=H, params=params, timeout=30).json()
        if r["code"] != "ok":
            print(kw, r["code"], r["message"]); break
        fresh = [p for p in r["data"]["items"] if p["id"] not in seen]
        new += fresh
        seen.update(p["id"] for p in fresh)
        if len(fresh) < len(r["data"]["items"]) or not r["page"]["has_more"]:
            break  # reached posts we already have
        cursor = r["page"]["next_cursor"]

with open("mentions.jsonl", "a", encoding="utf-8") as f:
    for p in new:
        f.write(json.dumps(p, ensure_ascii=False) + "\n")
open("seen.txt", "w").write("\n".join(seen))

Limits worth knowing

  • Search returns what Weibo's search chooses to show. Posts from restricted accounts, or posts Weibo hides from search, never appear, so treat this as a strong sample, not a complete record.
  • Short brand names match inside unrelated words in Chinese. Add a product word to the query, or filter the results before anyone reads them.
  • Long posts can come back cut short; with view=full, ext.is_long_text marks them. The catalog has no single-post lookup, so open the post's url to read the rest.
  • Comments and a user's own timeline are not in the catalog. If you need the replies under a complaint, read them on Weibo.

Other ways

Social listening platforms bundle this with sentiment dashboards and suit teams that want reports, not scripts. For a one-off deep crawl that includes comments, an open-source crawler such as MediaCrawler run with your own Weibo login does more, at the cost of maintaining the cookie. Under ten mentions a day, the Real-time tab of Weibo search read by hand is enough. More on the whole workflow: word of mouth.

Updated