Free Tool
4.9/5

Free Telegram Bot Link Generator

Build t.me deep links that hand your bot a referral id, coupon, or campaign name. We validate the username, count your payload against Telegram's 64-character limit, and base64url encode anything the start parameter will not accept.

This tool builds bot deep links. Want a share sheet instead? Use the Telegram Share Link Generator. Just shortening a Telegram URL? Try the Telegram Link Shortener.

Paste @name, a t.me link, or the plain handle. A leading @ is stripped for you, and bot usernames must be 5 to 32 characters and end in "bot".

0 / 64

This is the value your bot receives after /start. Keep one payload per source and you can tell campaigns apart later.

Try:
Encoding

Telegram counts starts. It never counts clicks.

Wrap each bot link in a Flyn short link and you see both numbers. Total clicks are free on every link; country, device, and referrer breakdowns come with Pro.

Shorten for free

How to Build a Telegram Bot Link in 3 Steps

Step 1: Enter your bot username and the payload, Telegram Bot Link Generator screenshot
1

Enter your bot username and the payload

Paste the handle with or without the @, or drop in a whole t.me link and we pull the username out of it. We check it against the Telegram username rules: 5 to 32 characters, letters, digits and underscores only, starting with a letter and ending in "bot". Then type the value your bot should receive.

Step 2: Choose raw or base64url encoding, Telegram Bot Link Generator screenshot
2

Choose raw or base64url encoding

The counter tracks your payload against the 64-character cap and turns red past it, and the validator names every character Telegram will not accept. Switch to base64url and punctuation, spaces and colons all survive. The decoded value is shown next to the link so you can confirm exactly what your start handler will read.

Step 3: Publish one link per source and count the clicks, Telegram Bot Link Generator screenshot
3

Publish one link per source and count the clicks

Copy the start link for private chats and the startgroup link for adding the bot to a group. Give each placement its own payload, shorten each link with Flyn, and the click count next to the start count tells you how many people opened Telegram but never pressed START.

How a Telegram Bot Deep Link Actually Works

Every bot has a plain link that opens a chat with it: t.me/your_bot. Add a start parameter and that link carries a value with it. Someone taps t.me/your_bot?start=ref_9f2c, Telegram opens the chat and puts a START button where the message box normally sits, and when the conversation begins your bot receives an ordinary message whose text is the literal string "/start ref_9f2c". Nothing exotic happens on your side: it is the same update your webhook already handles, with one argument after the command.

The startgroup parameter does the same trick for group chats. Instead of opening a private conversation, Telegram asks the person to choose a group and add your bot to it, and the payload travels with the invitation, which is how you can tell which of your onboarding links brought the bot into a given team. Telegram also sends a my_chat_member update whenever your bot's own membership changes, and that update arrives by default, so it makes a reliable backstop for tracking additions even when nobody presses anything.

The part that trips people up is the payload itself. Telegram allows only A-Z, a-z, 0-9, the underscore and the hyphen, with a hard limit of 64 characters. A URL will happily carry a colon, a space, or an ampersand, so a bad link looks completely normal and still opens your bot, which is why the failure is so quiet: the click works, the chat opens, and only the attribution goes missing. Encode anything outside that character set before it ever reaches the link, and the problem disappears.

Which t.me Bot Parameter Does What

Six ways to point a link at a bot, and what each one actually delivers to your code. This tool builds the first two, which are the pair almost every bot needs.

ParameterExampleWhat the click doesWhat your bot sees
?start=t.me/your_bot?start=ref_9f2cOpens a private chat with your bot and shows a START button in place of the message box.A normal message update whose text is /start ref_9f2c.
?startgroup=t.me/your_bot?startgroup=team_aAsks the person to pick a group, then adds your bot to it./start team_a in that group, plus a my_chat_member update for the new membership.
?startchannelt.me/your_bot?startchannel&admin=post_messagesAsks the person to add your bot to a channel with the admin rights you list.A my_chat_member update naming the rights granted. Channels have no /start message.
?startapp=t.me/your_bot?startapp=lvl3Opens your bot Mini App instead of the chat.The value as start_param inside the Mini App init data, not as a /start message.
?startattach=t.me/your_bot?startattach=noteOpens the attachment menu with your bot selected, for bots allowed in that menu.The value when the attachment-menu Mini App opens.
tg://resolvetg://resolve?domain=your_bot&start=ref_9f2cSame as ?start=, but only from an app that handles the tg: scheme.Identical to ?start=. Use the t.me form on the web.

Wherever a payload applies, the same rules hold: 64 characters maximum, and only A-Z, a-z, 0-9, underscore and hyphen.

The 64-Character Rule, and Why 48 Bytes Is the Real Budget

base64url is base64 with two substitutions and one deletion: the plus sign becomes a hyphen, the slash becomes an underscore, and the trailing equals padding is dropped. That leaves an alphabet of exactly A-Z, a-z, 0-9, hyphen and underscore, which is precisely the set Telegram allows in a start parameter. It is not a coincidence, it is why Telegram's own documentation recommends base64url for payloads that carry anything more structured than an id.

Encoding is not free. base64 turns every 3 bytes of input into 4 characters of output, so your raw value has to stay at or under 48 bytes to land inside the 64-character cap. Note bytes, not characters: an accented letter costs 2 bytes in UTF-8 and an emoji usually costs 4, so a short-looking string can blow the budget. The counter in the tool above shows the encoded length, which is the number that actually matters.

Percent-encoding is the wrong tool here, and it is the mistake we see most. A space written as %20 is still not a legal start parameter, and if it does survive the trip your handler receives the literal three characters %20 rather than a space. Worse, the /start command splits on whitespace, so a real space in the payload cuts your value in half and you keep only the first word. Encode once, with base64url, and decode once inside the handler.

What you want to passLegal as a raw payload?What goes in the linkChars
ref_9f2cYesref_9f2c8
src-newsletterYessrc-newsletter14
coupon:SAVE20No, the colonY291cG9uOlNBVkUyMA18
campaign=spring saleNo, the equals sign and the spaceY2FtcGFpZ249c3ByaW5nIHNhbGU27
ref:[email protected]No, the colon and the at signcmVmOmthcmFuQGV4YW1wbGUuY29t28
user_id=90210&src=newsletterNo, the equals sign and the ampersanddXNlcl9pZD05MDIxMCZzcmM9bmV3c2xldHRlcg38

Rows marked with a warning are encoded with base64url in the third column. Paste any of them into the generator above to see the same result and its decoded value.

Reading the Payload in Your Bot

There is no special update type for deep links. The payload arrives inside the message you already receive, as the text after the command:

{ "message": { "text": "/start Y291cG9uOlNBVkUyMA", "chat": { "id": 4242 } } }

Split that text once on the first space and keep the remainder, or let your framework do it. In grammY the argument is ctx.match, in python-telegram-bot it is context.args, and in most other libraries it is whatever the command handler calls the rest of the line:

// grammY (Node)
bot.command('start', (ctx) => {
  const payload = ctx.match;                                  // "Y291cG9uOlNBVkUyMA"
  const value = Buffer.from(payload, 'base64url').toString(); // "coupon:SAVE20"
});

# python-telegram-bot
async def start(update, context):
    payload = context.args[0] if context.args else None
    pad = "=" * (-len(payload) % 4)          # base64url padding was stripped
    value = base64.urlsafe_b64decode(payload + pad).decode()

Two details worth writing down. Telegram strips the equals padding from base64url, so languages that insist on correct padding need you to add it back, which is what the pad line above does. And inside a group a command can arrive addressed as /start@your_bot, so strip anything after the @ before you compare the command name, or your group onboarding will silently do nothing.

Finally, record the payload only the first time you see a given user id. A returning user who clicks a friend's referral link sends /start again, and if you overwrite on every start you will keep rewriting attribution to whoever shared last. Store it once, then ignore later payloads for that user, or keep them in a separate history table if you want the full trail.

Where Bot Deep Links Earn Their Keep

Any time the bot should already know something before the first message, the payload is how you tell it.

Referral and affiliate programs

Give every referrer a payload of their own, credit the right account inside the /start handler, and never ask a new user to paste a code they will get wrong.

Coupons and gated content

Put the coupon key in the payload so the bot can hand out the reward on the first message, instead of running a conversation just to collect a code.

Support handoffs from your website

Send the ticket or order id along with the click. The bot opens with context already loaded, so nobody has to repeat their order number to a chat window.

Onboarding a bot into a team group

Use the startgroup link with a workspace key, so when someone adds your bot to their group it already knows which account the group belongs to.

Bot Links + Flyn: See the Clicks Telegram Hides

The generator is free forever. Add Flyn when you want to know how many people reached the START button and stopped there.

Clicks, not just starts

Your bot only sees the people who pressed START. A short link counts everyone who clicked, so the drop-off becomes visible.

Editable destinations

Renamed your bot or changed the payload scheme? Re-point the short link once and every posted copy follows.

QR codes included

Turn a bot link into a QR code for packaging, posters, or an event badge, free on every link.

A free plan that lasts

25 links a month with total click counts and QR codes. No trial countdown. Custom domains come with Pro.

Frequently Asked Questions

What is a Telegram bot deep link?
A Telegram bot deep link is a normal t.me link with a start parameter attached, for example https://t.me/your_bot?start=ref_9f2c. Following it opens a one-to-one chat with your bot and shows a START button in place of the message box. Once the chat begins, your bot receives an ordinary message whose text is /start ref_9f2c, so the value you put in the link arrives inside the update your webhook already handles. That is the whole mechanism: one query parameter in, one command argument out.
What characters are allowed in a Telegram start parameter?
Only A-Z, a-z, 0-9, the underscore and the hyphen. Telegram documents this set for both start and startgroup, and everything else, including spaces, colons, equals signs, ampersands, plus signs, dots and any non-Latin letter, is out of spec. This is the single biggest reason bot links fail silently: nothing warns you, the click still opens the bot, and your handler just receives a value it does not recognise. The generator on this page flags the exact illegal characters in your payload before you publish the link.
How long can the start payload be?
The parameter is capped at 64 characters. That is 64 characters of what actually appears in the URL, not 64 characters of your original data, which matters the moment you encode. If you need to pass more than that, put a short lookup key in the payload and keep the full record in your own database, keyed by that value. A 12-character key with a database row behind it beats trying to squeeze structured data into 64 characters.
When do I need base64url encoding for a start payload?
Any time your value contains something outside A-Z, a-z, 0-9, underscore and hyphen. Telegram itself recommends base64url for exactly this, and the reason is neat: the base64url alphabet is that legal set plus nothing else, so encoded output is always accepted. base64url is standard base64 with plus replaced by hyphen, slash replaced by underscore, and the trailing equals padding removed. Encoding costs you length, so a raw value has to stay under 48 bytes to still fit inside 64 characters afterwards.
What is the difference between start and startgroup?
The start parameter opens a private chat between the person clicking and your bot. The startgroup parameter instead prompts them to pick a group and add your bot to it, and the same payload rides along, so you can tell which invitation link brought the bot into that group. Use start for onboarding, referrals and coupons where you want a private conversation, and startgroup when your bot is meant to work inside a team or community chat. This tool builds both from one payload so you never have to hand-edit the second one.
How does my bot read the start payload in code?
The payload arrives as part of a normal message: the text is the literal string /start followed by a space and your value. Split the text on the first space and everything after it is the payload, or use your framework helper, which is context.args in python-telegram-bot and ctx.match in grammY. If you encoded the value, decode it with base64url and re-add padding first in languages that require it. In groups the command can arrive addressed as /start@your_bot, so strip anything after the @ before you compare.
Why is my start parameter not reaching the bot?
Four causes account for almost all of it. The payload contains an illegal character or runs past 64 characters, so what your handler receives is no longer what you generated. The person already had a chat open with your bot and never pressed START again, so no fresh /start message was ever sent. Or the fault is in the code: a handler that reads the command name and quietly drops the argument, or one that base64url-decodes without re-adding the equals padding Telegram strips, then throws and falls back to treating the start as empty.
How do I use bot deep links for referral tracking?
Give every referrer their own payload, store the mapping in your database, and read it in the /start handler to credit the right account. Keep the payload itself opaque, something like ref_9f2c rather than a username or an email, so it stays inside the 64-character budget and reveals nothing to the person clicking. Record the payload the first time you see a user id, not on every /start, otherwise a returning user can overwrite their original attribution. Pair each referral link with its own short link and you also learn how many people clicked but never pressed START.
Is the start payload secret?
No, treat it as public. It sits in the URL, anyone can read it before clicking, and it is written into the chat as the visible text /start your_payload, where it stays in the conversation history. Base64url is encoding, not encryption, so it hides nothing from anyone who cares to decode it. Use the payload as a lookup key for something you hold server-side, and never put a session token, a password reset code, or personal data in it.
What is startapp, and how is it different from start?
The startapp parameter opens your bot Mini App instead of the chat, and the value arrives as start_param inside the Mini App init data rather than as a /start message. It follows the same 64-character and charset rules, so the payload you build here is valid for it too. Use start when you want a conversation with the bot, and startapp when the click should land straight in your web interface. A Mini App link can also be aimed at a specific app with t.me/your_bot/app_name?startapp=value.
Can I count how many people click my Telegram bot link?
Telegram gives you no click data, only the people who actually reach your /start handler. Wrap the t.me link in a Flyn short link and every click is counted before the handoff to Telegram: total clicks are free on every link, and country, device and referrer breakdowns come with Pro. Use one short link per placement, the bio, the newsletter, the ad, and the difference between clicks and starts becomes visible. That gap is usually the most useful number you can get about a bot funnel.
Is this tool free, and do you store my bot username or payload?
It is completely free, with no sign-up and no usage cap. Everything runs in your browser: the username is validated, the payload is encoded, and the links are assembled locally, and nothing is sent to a server when you press Generate. Your recent links are kept in your own browser localStorage so you can reuse them, capped at the last 20, and you can clear that list at any time.

Ready to ship a bot link that carries its own context?

Generate start and startgroup links for free, then shorten them with Flyn so you can see which bio, newsletter, or ad actually sends people to your bot. Analytics, QR codes, and editable links included.