diff --git a/src/bot/api.rs b/src/bot/api.rs
index 37087c7b..4301e89d 100644
--- a/src/bot/api.rs
+++ b/src/bot/api.rs
@@ -9,7 +9,7 @@ use crate::{
         SendLocation, SendMediaGroup, SendMessage, SendPhoto, SendPoll,
         SendVenue, SendVideo, SendVideoNote, SendVoice, SetChatDescription,
         SetChatStickerSet, StopMessageLiveLocation, UnbanChatMember,
-        UnpinChatMessage, SetChatTitle, DeleteChatPhoto
+        UnpinChatMessage, SetChatTitle, DeleteChatPhoto, SetChatPhoto
     },
     types::{ChatAction, ChatId, ChatPermissions, InputFile, InputMedia},
 };
@@ -378,4 +378,16 @@ impl Bot {
     {
         DeleteChatPhoto::new(self, chat_id)
     }
+
+    pub fn set_chat_photo<C, P>(
+        &self,
+        chat_id: C,
+        photo: P,
+    ) -> SetChatPhoto
+    where
+        C: Into<ChatId>,
+        P: Into<InputFile>,
+    {
+        SetChatPhoto::new(self, chat_id, photo)
+    }
 }
diff --git a/src/requests/mod.rs b/src/requests/mod.rs
index afce883d..b626aa9a 100644
--- a/src/requests/mod.rs
+++ b/src/requests/mod.rs
@@ -36,6 +36,7 @@ pub use send_video::*;
 pub use send_video_note::*;
 pub use send_voice::*;
 pub use set_chat_description::*;
+pub use set_chat_photo::*;
 pub use set_chat_sticker_set::*;
 pub use set_chat_title::*;
 pub use stop_message_live_location::*;
@@ -78,6 +79,7 @@ mod send_video;
 mod send_video_note;
 mod send_voice;
 mod set_chat_description;
+mod set_chat_photo;
 mod set_chat_sticker_set;
 mod set_chat_title;
 mod stop_message_live_location;
diff --git a/src/requests/set_chat_photo.rs b/src/requests/set_chat_photo.rs
new file mode 100644
index 00000000..34236fed
--- /dev/null
+++ b/src/requests/set_chat_photo.rs
@@ -0,0 +1,91 @@
+use async_trait::async_trait;
+
+use crate::bot::Bot;
+use crate::types::{ChatId, True, InputFile};
+use crate::requests::{Request, ResponseResult};
+use crate::network;
+use crate::requests::form_builder::FormBuilder;
+
+#[derive(Debug, Clone, Serialize)]
+pub struct SetChatPhoto<'a> {
+    #[serde(skip_serializing)]
+    bot: &'a Bot,
+
+    chat_id: ChatId,
+    photo: InputFile,
+}
+
+#[async_trait]
+impl Request for SetChatPhoto<'_> {
+    type Output = True;
+
+    async fn send_boxed(self) -> ResponseResult<Self::Output> {
+        self.send().await
+    }
+}
+
+impl SetChatPhoto<'_> {
+    async fn send(self) -> ResponseResult<True> {
+        let params = FormBuilder::new()
+            .add("chat_id", self.chat_id)
+            .add("photo", self.photo);
+
+        network::request_multipart(
+            self.bot.client(),
+            self.bot.token(),
+            "setChatPhoto",
+            params.build(),
+        ).await
+    }
+}
+
+impl<'a> SetChatPhoto<'a> {
+    pub(crate) fn new<C, P>(
+        bot: &'a Bot,
+        chat_id: C,
+        photo: P
+    ) -> Self
+    where
+        C: Into<ChatId>,
+        P: Into<InputFile>,
+    {
+        Self {
+            bot,
+            chat_id: chat_id.into(),
+            photo: photo.into(),
+        }
+    }
+
+    pub fn chat_id<C>(mut self, chat_id: C) -> Self
+    where
+        C: Into<ChatId>,
+    {
+        self.chat_id = chat_id.into();
+        self
+    }
+
+    pub fn photo<P>(mut self, photo: P) -> Self
+    where
+        P: Into<InputFile>,
+    {
+        self.photo = photo.into();
+        self
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn serialize() {
+        let bot = Bot::new("token");
+        let chat_id = 123;
+        let photo_url = "https://some_url".to_string();
+        let method = SetChatPhoto::new(&bot, chat_id, InputFile::Url(photo_url));
+
+        let expected = r#"{"chat_id":123,"photo":"https://some_url"}"#;
+        let actual = serde_json::to_string::<SetChatPhoto>(&method).unwrap();
+        assert_eq!(actual, expected);
+    }
+}