diff --git a/src/bot/api.rs b/src/bot/api.rs
index ea3be1bc..a0dda9cf 100644
--- a/src/bot/api.rs
+++ b/src/bot/api.rs
@@ -1,4 +1,5 @@
 use crate::bot::Bot;
+use crate::requests::AnswerCallbackQuery;
 use crate::{
     requests::{
         AnswerPreCheckoutQuery, AnswerShippingQuery, EditMessageLiveLocation,
@@ -274,4 +275,14 @@ impl Bot {
     {
         UnpinChatMessage::new(self, chat_id)
     }
+
+    pub fn answer_callback_query<S>(
+        &self,
+        callback_query_id: S,
+    ) -> AnswerCallbackQuery
+    where
+        S: Into<String>,
+    {
+        AnswerCallbackQuery::new(self, callback_query_id)
+    }
 }
diff --git a/src/requests/answer_callback_query.rs b/src/requests/answer_callback_query.rs
new file mode 100644
index 00000000..600cb755
--- /dev/null
+++ b/src/requests/answer_callback_query.rs
@@ -0,0 +1,122 @@
+use crate::bot::Bot;
+use crate::network;
+use crate::requests::{Request, ResponseResult};
+use crate::types::True;
+use async_trait::async_trait;
+
+/// Use this method to send answers to callback queries sent from inline
+/// keyboards. The answer will be displayed to the user as a notification at the
+/// top of the chat screen or as an alert. On success, True is returned.
+///
+/// Alternatively, the user can be redirected to the specified Game URL. For
+/// this option to work, you must first create a game for your bot via
+/// @Botfather and accept the terms. Otherwise, you may use links like
+/// t.me/your_bot?start=XXXX that open your bot with a parameter.
+#[derive(Debug, Clone, Serialize)]
+pub struct AnswerCallbackQuery<'a> {
+    #[serde(skip_serializing)]
+    bot: &'a Bot,
+
+    /// Unique identifier for the query to be answered.
+    callback_query_id: String,
+
+    /// Text of the notification. If not specified, nothing will be shown to
+    /// the user, 0-200 characters
+    #[serde(skip_serializing_if = "Option::is_none")]
+    text: Option<String>,
+
+    /// If true, an alert will be shown by the client instead of a notification
+    /// at the top of the chat screen. Defaults to false.
+    #[serde(skip_serializing_if = "Option::is_none")]
+    show_alert: Option<bool>,
+
+    /// URL that will be opened by the user's client. If you have created a
+    /// Game and accepted the conditions via @Botfather, specify the URL that
+    /// opens your game – note that this will only work if the query comes from
+    /// a callback_game button.
+    #[serde(skip_serializing_if = "Option::is_none")]
+    url: Option<String>,
+
+    /// The maximum amount of time in seconds that the result of the callback
+    /// query may be cached client-side. Telegram apps will support caching
+    /// starting in version 3.14. Defaults to 0.
+    #[serde(skip_serializing_if = "Option::is_none")]
+    cache_time: Option<i32>,
+}
+
+#[async_trait]
+impl Request for AnswerCallbackQuery<'_> {
+    type Output = True;
+
+    async fn send_boxed(self) -> ResponseResult<Self::Output> {
+        self.send().await
+    }
+}
+
+impl AnswerCallbackQuery<'_> {
+    pub async fn send(self) -> ResponseResult<True> {
+        network::request_json(
+            self.bot.client(),
+            self.bot.token(),
+            "answerCallbackQuery",
+            &self,
+        )
+        .await
+    }
+}
+
+impl<'a> AnswerCallbackQuery<'a> {
+    pub(crate) fn new<S>(bot: &'a Bot, callback_query_id: S) -> Self
+    where
+        S: Into<String>,
+    {
+        Self {
+            bot,
+            callback_query_id: callback_query_id.into(),
+            text: None,
+            show_alert: None,
+            url: None,
+            cache_time: None,
+        }
+    }
+
+    pub fn callback_query_id<S>(mut self, value: S) -> Self
+    where
+        S: Into<String>,
+    {
+        self.callback_query_id = value.into();
+        self
+    }
+
+    pub fn text<S>(mut self, value: S) -> Self
+    where
+        S: Into<String>,
+    {
+        self.text = Some(value.into());
+        self
+    }
+
+    pub fn show_alert<B>(mut self, value: B) -> Self
+    where
+        B: Into<bool>,
+    {
+        self.show_alert = Some(value.into());
+        self
+    }
+
+    pub fn url<S>(mut self, value: S) -> Self
+    where
+        S: Into<String>,
+    {
+        self.url = Some(value.into());
+        self
+    }
+
+    pub fn cache_time<I>(mut self, value: I) -> Self
+    where
+        I: Into<i32>,
+    {
+        self.cache_time = Some(value.into());
+        self
+    }
+}
diff --git a/src/requests/mod.rs b/src/requests/mod.rs
index a1ecef59..ec4b8232 100644
--- a/src/requests/mod.rs
+++ b/src/requests/mod.rs
@@ -3,6 +3,7 @@
 use async_trait::async_trait;
 use serde::de::DeserializeOwned;
 
+pub use answer_callback_query::*;
 pub use answer_pre_checkout_query::*;
 pub use answer_shipping_query::*;
 pub use edit_message_live_location::*;
@@ -37,6 +38,7 @@ pub use unpin_chat_message::*;
 mod form_builder;
 mod utils;
 
+mod answer_callback_query;
 mod answer_pre_checkout_query;
 mod answer_shipping_query;
 mod edit_message_live_location;