Telegram Inline Mode — Results Are Cached and Shared by Default
Inline mode is the feature that lets someone type @yourbot something in any chat — including chats your bot isn't a member of — and pick from results your bot returns. It's the most useful thing a small Telegram bot can offer, and it ships with two defaults that will surprise you. Results are cached for 300 seconds, and by default that cache is shared: the documentation says results "may be returned to any user who sends the same query" (python-telegram-bot). If what your bot returns depends on who asked — their notes, their tasks, their bookmarks — the default hands one user's results to another. Setting is_personal fixes it, and it's the first thing to get right.
How inline mode works
You enable it for your bot through BotFather. After that, typing @yourbot followed by text in any chat sends your bot an inline query containing the text and the user who typed it. You reply with a list of results, and whatever they pick gets posted into that chat.
Two things make it different from every other bot interaction:
- It fires while the user is typing. Not once, on a command — repeatedly, as the query changes.
- It doesn't need your bot in the chat. The query goes to your bot directly, so you get the user but not the conversation.
That shapes everything below.
The caching defaults, precisely
cache_time defaults to 300 seconds — described as "the maximum amount of time in seconds that the result of the inline query may be cached on the server." Five minutes is a long time for anything that changes.
is_personal defaults to off, and that's the one that matters. When you set it, "results may be cached on the server side only for the user that sent the query." When you don't, the same query from a different person can be served your first user's answer.
The rule is simple and worth stating flatly: if the results depend on who is asking, set is_personal. A bot that searches a public dataset can happily share its cache. A bot that returns "your saved items" must not.
This is the same trap as an app that assumes one user at a time — it works perfectly while you're the only person using it, and the failure is quiet, because nothing errors. Someone just sees results that aren't theirs.
For anything time-sensitive, lower cache_time as well. For anything genuinely live, set it low and expect to actually answer every keystroke's worth of queries.
Latency is the entire experience
The user is mid-sentence with a dropdown open. If your results take two seconds, the feature feels broken even when it's working.
Two consequences for how you run the bot:
This is the strongest argument for webhooks over polling. Long polling adds a round of latency you can't remove, and inline queries are the one interaction where that's directly visible to the user. If you're weighing the two, inline mode should push you toward a webhook.
Cache your own lookups in-process. Whatever you query to build results — a database, an external API — put a small cache in front of it with a short TTL. There's one container, so a dictionary is a complete cache with nothing to coordinate, the same reason you rarely need Redis here.
And keep the work small. An inline handler is not the place for a model call or anything else measured in seconds.
Fifty results, and a 64-byte pagination budget
Two limits worth designing around.
No more than 50 results per query. That's plenty — nobody scrolls an inline dropdown. Return the best ten and stop.
Pagination goes through next_offset, described as the offset a client sends in the next query for more results, with the constraint that "offset length can't exceed 64 bytes."
Sixty-four bytes is not much, and the right response is the same one Discord's 100-character custom_id calls for: put a pointer in it, not state. A page number, or a cursor key that means something to your storage. Not a serialised query.
Pass an empty string when there are no more results, or you'll invite a request you can't satisfy.
What to show for an empty query
The moment someone types @yourbot and nothing else, you get a query with empty text. Treat it as an opportunity rather than an edge case — this is your bot's menu.
Show recent items, popular items, or a short hint about what the bot does. An empty result list here reads as a broken bot, and it's the first thing most people see.
You get a user, not a conversation
Inline queries carry the user who typed and the text they typed. They do not carry the chat, and your bot may not even be in it.
So anything you'd normally look up per-chat isn't available. If your bot's behaviour depends on group settings, inline mode can't see them — which is a design constraint, not a bug. Per-user state is fine; keep it on a volume like anything else that must survive a redeploy.
It also means an inline query is a fine place to require that the user has linked an account with you, and a poor place to assume they have.
Be careful what you log
Inline queries are people typing in other people's chats, often mid-conversation. The text is more sensitive than a command sent directly to your bot, and it arrives constantly — one query per keystroke burst rather than one per action.
- Don't log query text. Counts and timings are enough to understand usage.
- Volume alone is a reason. Logs are capped and rotate, and logging every inline query will evict everything else you needed.
The same instinct applies as with Telegram's privacy mode in groups: the default posture should be seeing and keeping as little as possible.
FAQ
Why do users see each other's inline results?
Results are cached and shared by default. Set is_personal so the cache is scoped to the user who sent the query.
How long are inline results cached?
cache_time defaults to 300 seconds. Lower it for anything that changes often.
How many results can I return?
Up to 50 per query, though far fewer is usually better since nobody scrolls the dropdown.
How do I paginate inline results?
Use next_offset, keeping it under 64 bytes. Store a pointer such as a page number or cursor, not serialised state.
Should I use polling or webhooks for inline mode?
Webhooks. The user is typing while they wait, so the extra latency of long polling is directly visible.
Does my bot need to be in the chat?
No. Inline queries reach your bot regardless, which is why you receive the user but not the conversation.
Wrapping up
Inline mode is a small amount of code for a large amount of reach — your bot becomes usable in every chat its users are in, without being added to any of them. The two things to get right before you ship are both defaults rather than features: scope the cache to the user if the results are personal, and treat the latency budget as the product, because someone is watching an empty dropdown while you work. Get those right and the rest is a list of ten results and a sensible empty state.