inline mode

let users type @your_bot query in any chat and pick a result generated by your bot.

answer inline queries

inline query handlers must answer with a list of results. keep the response fast, cache where possible, and use is_personal when results depend on the current user.

inline-query.ts
bot.on("inline_query", async (ctx) => {
  const query = ctx.update.inline_query!;
  const results = await search(query.query);

  await ctx.api.call("answerInlineQuery", {
    inline_query_id: query.id,
    cache_time: 10,
    is_personal: true,
    results: results.map((item) => ({
      type: "article",
      id: item.id,
      title: item.title,
      input_message_content: { message_text: item.text },
    })),
  });
});

track chosen results

if enabled in botfather, telegram sends chosen_inline_result after a user picks one of your results. use it for analytics, not for critical business logic.

chosen-result.ts
bot.on("chosen_inline_result", async (ctx) => {
  const chosen = ctx.update.chosen_inline_result!;
  await analytics.track("inline_chosen", {
    resultId: chosen.result_id,
    userId: chosen.from.id,
    query: chosen.query,
  });
});

switch inline buttons

inline keyboard buttons can open the inline picker with a prefilled query. this is useful for sharing products, documents, search results, or mini app content.

keyboard.ts
new InlineKeyboard()
  .switchInline("share", "product:42")
  .row()
  .switchInlineCurrentChat("search here", "cats");

rules of thumb

  • return stable result ids so analytics and caches stay meaningful.
  • keep result payloads small; the user is waiting inside telegram's inline picker.
  • use cache_time aggressively for public search results.
  • set is_personal for private/user-specific results.
  • add inline_query and chosen_inline_result to allowedUpdates when polling.