Telegram Join Request Bot: The 5-Minute Window Most Admins Miss

Telegram Join Request Bot: The 5-Minute Window Most Admins Miss

Telegram has a verification mechanism that does what a captcha cannot, and most admins do not know what it gives them.

Create an invite link with creates_join_request=True and users request to join rather than joining. Your bot receives a ChatJoinRequest, and you approve or decline with approveChatJoinRequest / declineChatJoinRequest — both requiring the can_invite_users admin right.

The interesting part is what the request object contains, and one line in the documentation that almost nobody has read.

What a join request gives you

The user's bio. ChatJoinRequest carries an Optional bio field — present only when the user has set a profile bio. When it is present you can read it before deciding to admit them. Farm accounts that monetise their bio advertise in it, and reading bio costs you nothing: it already arrived with the update, no extra API call and no friction for the user. It will not catch an account that left its bio empty.

Their full user object, including language_code, username, and whether they have is_premium. A Telegram Premium account is not proof of legitimacy, but it costs money to maintain, which changes the economics for a disposable farm account.

Timestamp. Join bursts are visible: several requests within a short window is a raid signature.

And this, from the documentation for user_chat_id:

"The bot can use this identifier for 5 minutes to send messages until the join request is processed, assuming no other administrator contacted the user."

You can message someone who has requested to join, before they are a member, for five minutes.

That is a verification channel that does not exist anywhere else in the platform, and it is the single most underused capability in Telegram group management.

What the five-minute window is for

You can hold a private exchange with a prospective member outside your group. The challenge happens in a one-to-one chat, not in front of your community.

That solves several problems at once:

No captcha clutter in the group. Verification messages never touch your channel. No bot noise, no deleted challenge messages, no confusion for existing members.

You can ask a real question. "What brings you here?" or a topic-specific question. Free-text answers from a bot farm are obviously wrong in a way a tapped button never is.

You can state rules and get agreement before admission rather than hoping people read a pinned post.

A non-response is itself a signal. An account that requests to join and ignores a direct message for five minutes is not eager to participate.

The constraint is real, though: five minutes, and only "assuming no other administrator contacted the user." Design for a fast exchange — one message, one expected reply — not a conversation. And do not build a flow that breaks if the window closes; fall back to holding the request for human review.

Why this beats captcha

CaptchaJoin request
Friction for legitimate usersVisible, alwaysVery low if auto-approved
Signal available before entryNoneBio, username, premium, timing
Happens in the groupUsuallyNo — private
Stops delayed-drip farmsNoOnly if the account advertises in its bio — we have no measurement of how often that holds
Leaked link still grants accessYesNo — approval is required

That last row matters for paid channels specifically. A captcha protects a link that still admits anyone who solves it. A join request means a leaked link grants nothing — every entry passes through your approval, where you check entitlement. See invite link leaks. For what the captcha side of this table costs in practice, see captcha types compared.

The one thing you must get right

Approve fast.

The entire advantage collapses if requests sit in a queue. A user who taps "Request to join" and hears nothing for four hours has been given worse friction than any captcha would have imposed — an unbounded, unexplained wait.

Auto-approve the clear cases. Hold only the flagged ones for a human. Target seconds, not minutes.

A workable policy:

Auto-approve when the bio is present and clean, the account has a username or getUserProfilePhotos returns at least one photo, and there is no join burst from that link. An absent bio is no signal, not a clean one.

Challenge in the five-minute window when signals are mixed — one soft flag, or a brand-new-looking account.

Hold for review on two or more spam signals.

Auto-decline only on unambiguous combinations — links in bio plus known scam phrasing plus a burst. Score, don't execute on one signal; plenty of legitimate creators have links in their bio.

The trade-off, honestly

creates_join_request and member_limit are mutually exclusive. The documentation is explicit: "If True, member_limit can't be specified."

So you give up the automatic per-link cap and gain an approval decision. For most groups that is a good trade — the cap was never a strong control anyway, since member_limit counts simultaneous members and frees up when someone leaves.

The other cost is operational: you now have a queue, and a queue that nobody watches is worse than no queue. Do not switch to join requests unless approval is automated or somebody is genuinely watching.

Setting it up

  1. Create the link with creates_join_request=True
  2. Handle the chat_join_request update
  3. Score the request — bio, username, premium, burst detection, plus a profile photo lookup via getUserProfilePhotos if you want that signal
  4. Auto-approve, challenge via user_chat_id, or hold
  5. Approve within seconds where you can
  6. Log everything, including invite_link, for measuring drop-off

Then still apply new-member restrictions after admission. Join requests filter at the door; restrictions cover the account that gets through and waits.


Field definitions and constraints quoted from Telegram's Bot API documentation, verified September 2026.