diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9b18856..3d4d014 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,13 @@ All notable changes to this project will be documented in this file.
 The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this
 project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 
+## Unreleased
+
+### Added
+
+- Add `encoding` (`Optional[str]`) parameter to `get_key`, `set_key` and `unset_key`.
+  (#379 by [@bbc2])
+
 ## [0.19.2] - 2021-11-11
 
 ### Fixed
diff --git a/src/dotenv/main.py b/src/dotenv/main.py
index d867f02..20ac61b 100644
--- a/src/dotenv/main.py
+++ b/src/dotenv/main.py
@@ -109,23 +109,30 @@ class DotEnv():
         return None
 
 
-def get_key(dotenv_path: Union[str, _PathLike], key_to_get: str) -> Optional[str]:
+def get_key(
+    dotenv_path: Union[str, _PathLike],
+    key_to_get: str,
+    encoding: Optional[str] = "utf-8",
+) -> Optional[str]:
     """
-    Gets the value of a given key from the given .env
+    Get the value of a given key from the given .env.
 
-    If the .env path given doesn't exist, fails
+    Returns `None` if the key isn't found or doesn't have a value.
     """
-    return DotEnv(dotenv_path, verbose=True).get(key_to_get)
+    return DotEnv(dotenv_path, verbose=True, encoding=encoding).get(key_to_get)
 
 
 @contextmanager
-def rewrite(path: Union[str, _PathLike]) -> Iterator[Tuple[IO[str], IO[str]]]:
+def rewrite(
+    path: Union[str, _PathLike],
+    encoding: Optional[str],
+) -> Iterator[Tuple[IO[str], IO[str]]]:
     try:
         if not os.path.isfile(path):
-            with io.open(path, "w+") as source:
+            with io.open(path, "w+", encoding=encoding) as source:
                 source.write("")
-        with tempfile.NamedTemporaryFile(mode="w+", delete=False) as dest:
-            with io.open(path) as source:
+        with tempfile.NamedTemporaryFile(mode="w+", delete=False, encoding=encoding) as dest:
+            with io.open(path, encoding=encoding) as source:
                 yield (source, dest)  # type: ignore
     except BaseException:
         if os.path.isfile(dest.name):
@@ -141,6 +148,7 @@ def set_key(
     value_to_set: str,
     quote_mode: str = "always",
     export: bool = False,
+    encoding: Optional[str] = "utf-8",
 ) -> Tuple[Optional[bool], str, str]:
     """
     Adds or Updates a key/value to the given .env
@@ -165,7 +173,7 @@ def set_key(
     else:
         line_out = "{}={}\n".format(key_to_set, value_out)
 
-    with rewrite(dotenv_path) as (source, dest):
+    with rewrite(dotenv_path, encoding=encoding) as (source, dest):
         replaced = False
         missing_newline = False
         for mapping in with_warn_for_invalid_lines(parse_stream(source)):
@@ -187,6 +195,7 @@ def unset_key(
     dotenv_path: Union[str, _PathLike],
     key_to_unset: str,
     quote_mode: str = "always",
+    encoding: Optional[str] = "utf-8",
 ) -> Tuple[Optional[bool], str]:
     """
     Removes a given key from the given .env
@@ -199,7 +208,7 @@ def unset_key(
         return None, key_to_unset
 
     removed = False
-    with rewrite(dotenv_path) as (source, dest):
+    with rewrite(dotenv_path, encoding=encoding) as (source, dest):
         for mapping in with_warn_for_invalid_lines(parse_stream(source)):
             if mapping.key == key_to_unset:
                 removed = True

diff --git a/tests/test_main.py b/tests/test_main.py
index 541ac5e..364fc24 100644
--- a/tests/test_main.py
+++ b/tests/test_main.py
@@ -53,6 +53,15 @@ def test_set_key(dotenv_file, before, key, value, expected, after):
     mock_warning.assert_not_called()
 
 
+def test_set_key_encoding(dotenv_file):
+    encoding = "latin-1"
+
+    result = dotenv.set_key(dotenv_file, "a", "é", encoding=encoding)
+
+    assert result == (True, "a", "é")
+    assert open(dotenv_file, "r", encoding=encoding).read() == "a='é'\n"
+
+
 def test_set_key_permission_error(dotenv_file):
     os.chmod(dotenv_file, 0o000)
 
@@ -107,6 +116,16 @@ def test_get_key_ok(dotenv_file):
     mock_warning.assert_not_called()
 
 
+def test_get_key_encoding(dotenv_file):
+    encoding = "latin-1"
+    with open(dotenv_file, "w", encoding=encoding) as f:
+        f.write("é=è")
+
+    result = dotenv.get_key(dotenv_file, "é", encoding=encoding)
+
+    assert result == "è"
+
+
 def test_get_key_none(dotenv_file):
     logger = logging.getLogger("dotenv.main")
     with open(dotenv_file, "w") as f:
@@ -147,6 +166,18 @@ def test_unset_no_value(dotenv_file):
     mock_warning.assert_not_called()
 
 
+def test_unset_encoding(dotenv_file):
+    encoding = "latin-1"
+    with open(dotenv_file, "w", encoding=encoding) as f:
+        f.write("é=x")
+
+    result = dotenv.unset_key(dotenv_file, "é", encoding=encoding)
+
+    assert result == (True, "é")
+    with open(dotenv_file, "r", encoding=encoding) as f:
+        assert f.read() == ""
+
+
 def test_unset_non_existent_file(tmp_path):
     nx_file = str(tmp_path / "nx")
     logger = logging.getLogger("dotenv.main")

encoding not an option using set_key
You can create and read a .env using the main.DotEnv class with an encoding, but the functions of setting key/unsetting do not act accordingly with the encoding set nor have an option to set it. - They both use the rewrite contextmanager
tests/test_main.py::test_set_key_encoding tests/test_main.py::test_get_key_encoding tests/test_main.py::test_unset_encoding
tests/test_main.py::test_set_key_no_file tests/test_main.py::test_set_key[-a--expected0-a=''\n] tests/test_main.py::test_set_key[-a-b-expected1-a='b'\n] tests/test_main.py::test_set_key[-a-'b'-expected2-a='\\'b\\''\n] tests/test_main.py::test_set_key[-a-"b"-expected3-a='"b"'\n] tests/test_main.py::test_set_key[-a-b'c-expected4-a='b\\'c'\n] tests/test_main.py::test_set_key[-a-b"c-expected5-a='b"c'\n] tests/test_main.py::test_set_key[a=b-a-c-expected6-a='c'\n] tests/test_main.py::test_set_key[a=b\n-a-c-expected7-a='c'\n] tests/test_main.py::test_set_key[a=b\n\n-a-c-expected8-a='c'\n\n] tests/test_main.py::test_set_key[a=b\nc=d-a-e-expected9-a='e'\nc=d] tests/test_main.py::test_set_key[a=b\nc=d\ne=f-c-g-expected10-a=b\nc='g'\ne=f] tests/test_main.py::test_set_key[a=b\n-c-d-expected11-a=b\nc='d'\n] tests/test_main.py::test_set_key[a=b-c-d-expected12-a=b\nc='d'\n] tests/test_main.py::test_get_key_no_file tests/test_main.py::test_get_key_not_found tests/test_main.py::test_get_key_ok tests/test_main.py::test_get_key_none tests/test_main.py::test_unset_with_value tests/test_main.py::test_unset_no_value tests/test_main.py::test_unset_non_existent_file tests/test_main.py::test_find_dotenv_no_file_raise tests/test_main.py::test_find_dotenv_no_file_no_raise tests/test_main.py::test_find_dotenv_found tests/test_main.py::test_load_dotenv_existing_file tests/test_main.py::test_load_dotenv_no_file_verbose tests/test_main.py::test_load_dotenv_existing_variable_no_override tests/test_main.py::test_load_dotenv_existing_variable_override tests/test_main.py::test_load_dotenv_redefine_var_used_in_file_no_override tests/test_main.py::test_load_dotenv_redefine_var_used_in_file_with_override tests/test_main.py::test_load_dotenv_string_io_utf_8 tests/test_main.py::test_load_dotenv_file_stream tests/test_main.py::test_load_dotenv_in_current_dir tests/test_main.py::test_dotenv_values_file tests/test_main.py::test_dotenv_values_string_io[env0-a=$b-False-expected0] tests/test_main.py::test_dotenv_values_string_io[env1-a=$b-True-expected1] tests/test_main.py::test_dotenv_values_string_io[env2-a=${b}-False-expected2] tests/test_main.py::test_dotenv_values_string_io[env3-a=${b}-True-expected3] tests/test_main.py::test_dotenv_values_string_io[env4-a=${b:-d}-False-expected4] tests/test_main.py::test_dotenv_values_string_io[env5-a=${b:-d}-True-expected5] tests/test_main.py::test_dotenv_values_string_io[env6-b=c\na=${b}-True-expected6] tests/test_main.py::test_dotenv_values_string_io[env7-a=${b}-True-expected7] tests/test_main.py::test_dotenv_values_string_io[env8-a=${b:-d}-True-expected8] tests/test_main.py::test_dotenv_values_string_io[env9-a="${b}"-True-expected9] tests/test_main.py::test_dotenv_values_string_io[env10-a='${b}'-True-expected10] tests/test_main.py::test_dotenv_values_string_io[env11-a=x${b}y-True-expected11] tests/test_main.py::test_dotenv_values_string_io[env12-a=${a}-True-expected12] tests/test_main.py::test_dotenv_values_string_io[env13-a=${a}-True-expected13] tests/test_main.py::test_dotenv_values_string_io[env14-a=${a:-c}-True-expected14] tests/test_main.py::test_dotenv_values_string_io[env15-a=${a:-c}-True-expected15] tests/test_main.py::test_dotenv_values_string_io[env16-a=${b}${b}-True-expected16] tests/test_main.py::test_dotenv_values_string_io[env17-b=d\na=${b}-True-expected17] tests/test_main.py::test_dotenv_values_string_io[env18-a=b\na=c\nd=${a}-True-expected18] tests/test_main.py::test_dotenv_values_string_io[env19-a=b\nc=${a}\nd=e\nc=${d}-True-expected19] tests/test_main.py::test_dotenv_values_file_stream