Telegram search is fine for finding a message in a chat you are already in. It is close to useless for finding a chat — and if the content is Chinese, it gets worse in ways that are not obvious until you try to build something.
I work on an index of public Telegram channels and groups for Chinese-speaking communities abroad, so I have spent a lot of time on this. Here is what actually works, what does not, and the traps I hit.
Why Telegram search fails for discovery
- Scope. Search only covers chats you are a member of (plus some public username matches). There is no global index of public channels.
-
Chinese has no spaces. Matching on "西游记4k蓝光" against a message containing "西游记 4K 蓝光" needs tokenisation, not substring matching. Naive
LIKE '%word%'misses most of it. - Simplified vs Traditional. 简体 and 繁體 are the same language written two ways. Users type one, channels post the other.
- Discovery spam. Once you do build an index, the top results fill up with channels that game whatever signal you expose.
What works
1. t.me/s/<channel> is the public web mirror
For public channels, https://t.me/s/<channel> renders the last ~20 posts as plain HTML — no login, no API key. Pagination is ?before=<message_id>, and the HTML is stable enough to parse. This is how you read a channel without joining it.
Caveat: it works for public channels only, and it gives you recent posts, not history.
2. Store the users' language, not just the text
For Chinese, the trick is to normalise before you index: convert Traditional to Simplified, split CJK into bigrams (西游记 → 西游, 游记), and keep latin/digits as whole tokens (4k, 蓝光). Then you can run a real inverted index (SQLite FTS5 or Meilisearch) instead of LIKE. Recall goes up dramatically for mixed queries like 西游记4k蓝光.
3. Rank on things that are hard to fake
Four signals, in this order, worked for us:
- text relevance (BM25 over the inverted index),
- recency (a channel that posted today beats one dormant for a year),
- channel quality (subscriber count, message count, media ratio — computed from what we crawl),
-
views (per-post view counts, which are visible on the
t.me/s/mirror).
That is it. No manual boosts, no paid placement. This is not a moral stance so much as a practical one: the moment a ranking position is for sale, the index fills with whoever pays, users notice within a week, and the search stops being useful.
4. Expect to fight junk
Crypto "招商" channels, pirated-media dumps and affiliate spam are a large share of what you will crawl. Some practical filters: minimum subscriber threshold, media-to-text ratio, duplicate-title detection across channels, and a hard cap on how many results any single channel can occupy.
What we ended up with
We index about 95,000 public channels and groups and roughly 675,000 messages, and expose it two ways:
- a Telegram bot (
@yosou) for query-in-chat, and - a web entry point at yosou.pro that works without Telegram — useful when someone shares a link outside Telegram.
Because "who runs this channel" is usually more useful than any single post, we also publish a directory of the people behind the indexed channels — each page shows the channel owner, the channels they hold, and subscriber counts. It is a plain server-rendered page, so it opens from any browser without an account.
Search is deliberately boring: BM25 + freshness + channel quality + views, four signals, all computed from public data.
If you are building something similar
- Start with
t.me/s/mirrors for ingestion; they are cheap and legal-ish for public channels (respect rate limits androbots.txt). - Do the Chinese-aware tokenisation before you index, not at query time — otherwise every search pays the cost.
- Keep a "why ranked here" explanation per result. Users trust a search that can explain itself, and you will need it yourself when debugging recall.
- Do not sell ranking. Everything else is negotiable.
Happy to answer questions about the ingestion or tokenisation side — the interesting bugs are all in the CJK edge cases.
Top comments (0)