diff --git a/src/integration-test/momento/cache_client_test.exs b/src/integration-test/momento/cache_client_test.exs
index f561e7c..fea8bc0 100644
--- a/src/integration-test/momento/cache_client_test.exs
+++ b/src/integration-test/momento/cache_client_test.exs
@@ -79,10 +79,10 @@ defmodule CacheClientTest do
     value = "test_value"
 
     {:error, error} = CacheClient.set(cache_client, cache_name, key, value, "sixty")
-    assert String.contains?(error.message, "The TTL must be a positive float")
+    assert String.contains?(error.message, "The TTL must be a float")
 
     {:error, error} = CacheClient.set(cache_client, cache_name, key, value, -20.0)
-    assert String.contains?(error.message, "The TTL must be a positive float")
+    assert String.contains?(error.message, "The TTL must be positive")
   end
 
   test "get/3 returns miss when no value is found for a key", %{
@@ -136,4 +136,178 @@ defmodule CacheClientTest do
     {:error, error} = CacheClient.delete(cache_client, cache_name, 12345)
     assert String.contains?(error.message, "The key must be a binary")
   end
+
+  describe "sorted_set_put_elements/5" do
+    test "should be able to put elements in a sorted set, overwriting existing elements", %{
+      cache_client: cache_client,
+      cache_name: cache_name
+    } do
+      sorted_set_name = random_string(16)
+      first_elements = %{"key1" => 1.0, "key2" => 2.0}
+      second_elements = %{"key2" => 5.0, "key3" => 3.0}
+
+      :miss = CacheClient.sorted_set_fetch_by_rank(cache_client, cache_name, sorted_set_name)
+
+      {:ok, _} =
+        CacheClient.sorted_set_put_elements(
+          cache_client,
+          cache_name,
+          sorted_set_name,
+          first_elements
+        )
+
+      {:ok, hit} = CacheClient.sorted_set_fetch_by_rank(cache_client, cache_name, sorted_set_name)
+      assert hit.value == [{"key1", 1.0}, {"key2", 2.0}]
+
+      {:ok, _} =
+        CacheClient.sorted_set_put_elements(
+          cache_client,
+          cache_name,
+          sorted_set_name,
+          second_elements
+        )
+
+      {:ok, hit} = CacheClient.sorted_set_fetch_by_rank(cache_client, cache_name, sorted_set_name)
+      assert hit.value == [{"key1", 1.0}, {"key3", 3.0}, {"key2", 5.0}]
+    end
+  end
+
+  describe "sorted_set_put_element/6" do
+    test "should be able to put individual elements in a sorted set, overwriting existing elements",
+         %{
+           cache_client: cache_client,
+           cache_name: cache_name
+         } do
+      sorted_set_name = random_string(16)
+
+      :miss = CacheClient.sorted_set_fetch_by_rank(cache_client, cache_name, sorted_set_name)
+
+      {:ok, _} =
+        CacheClient.sorted_set_put_element(cache_client, cache_name, sorted_set_name, "key1", 1.0)
+
+      {:ok, hit} = CacheClient.sorted_set_fetch_by_rank(cache_client, cache_name, sorted_set_name)
+      assert hit.value == [{"key1", 1.0}]
+
+      {:ok, _} =
+        CacheClient.sorted_set_put_element(cache_client, cache_name, sorted_set_name, "key1", 5.0)
+
+      {:ok, hit} = CacheClient.sorted_set_fetch_by_rank(cache_client, cache_name, sorted_set_name)
+      assert hit.value == [{"key1", 5.0}]
+
+      {:ok, _} =
+        CacheClient.sorted_set_put_element(cache_client, cache_name, sorted_set_name, "key2", 2.0)
+
+      {:ok, hit} = CacheClient.sorted_set_fetch_by_rank(cache_client, cache_name, sorted_set_name)
+      assert hit.value == [{"key2", 2.0}, {"key1", 5.0}]
+    end
+  end
+
+  describe "sorted_set_fetch_by_rank/6" do
+    test "should be able to fetch in ascending and descending order", %{
+      cache_client: cache_client,
+      cache_name: cache_name
+    } do
+      sorted_set_name = random_string(16)
+      elements = %{"key1" => 1.0, "key2" => 2.0, "key3" => 3.0}
+
+      :miss = CacheClient.sorted_set_fetch_by_rank(cache_client, cache_name, sorted_set_name)
+
+      {:ok, _} =
+        CacheClient.sorted_set_put_elements(cache_client, cache_name, sorted_set_name, elements)
+
+      {:ok, hit} =
+        CacheClient.sorted_set_fetch_by_rank(
+          cache_client,
+          cache_name,
+          sorted_set_name,
+          sort_order: :asc
+        )
+
+      assert hit.value == [{"key1", 1.0}, {"key2", 2.0}, {"key3", 3.0}]
+
+      {:ok, hit} =
+        CacheClient.sorted_set_fetch_by_rank(
+          cache_client,
+          cache_name,
+          sorted_set_name,
+          sort_order: :desc
+        )
+
+      assert hit.value == [{"key3", 3.0}, {"key2", 2.0}, {"key1", 1.0}]
+    end
+
+    test "should be able to fetch a subset of a sorted set", %{
+      cache_client: cache_client,
+      cache_name: cache_name
+    } do
+      sorted_set_name = random_string(16)
+      elements = [{"key1", 1.0}, {"key2", 2.0}, {"key3", 3.0}, {"key4", 4.0}, {"key5", 5.0}]
+
+      :miss = CacheClient.sorted_set_fetch_by_rank(cache_client, cache_name, sorted_set_name)
+
+      {:ok, _} =
+        CacheClient.sorted_set_put_elements(cache_client, cache_name, sorted_set_name, elements)
+
+      {:ok, hit} =
+        CacheClient.sorted_set_fetch_by_rank(cache_client, cache_name, sorted_set_name,
+          start_rank: 0
+        )
+
+      assert hit.value == [
+               {"key1", 1.0},
+               {"key2", 2.0},
+               {"key3", 3.0},
+               {"key4", 4.0},
+               {"key5", 5.0}
+             ]
+
+      {:ok, hit} =
+        CacheClient.sorted_set_fetch_by_rank(cache_client, cache_name, sorted_set_name,
+          start_rank: 0,
+          end_rank: 5
+        )
+
+      assert hit.value == [
+               {"key1", 1.0},
+               {"key2", 2.0},
+               {"key3", 3.0},
+               {"key4", 4.0},
+               {"key5", 5.0}
+             ]
+
+      {:ok, hit} =
+        CacheClient.sorted_set_fetch_by_rank(cache_client, cache_name, sorted_set_name,
+          start_rank: 0,
+          end_rank: 100
+        )
+
+      assert hit.value == [
+               {"key1", 1.0},
+               {"key2", 2.0},
+               {"key3", 3.0},
+               {"key4", 4.0},
+               {"key5", 5.0}
+             ]
+
+      {:ok, hit} =
+        CacheClient.sorted_set_fetch_by_rank(cache_client, cache_name, sorted_set_name,
+          start_rank: 1,
+          end_rank: 3
+        )
+
+      assert hit.value == [{"key2", 2.0}, {"key3", 3.0}]
+
+      {:ok, hit} =
+        CacheClient.sorted_set_fetch_by_rank(
+          cache_client,
+          cache_name,
+          sorted_set_name,
+          start_rank: 1,
+          end_rank: 3,
+          sort_order: :desc
+        )
+
+      assert hit.value == [{"key4", 4.0}, {"key3", 3.0}]
+    end
+  end
 end
diff --git a/src/integration-test/momento/integration_test_utils.exs b/src/integration-test/momento/integration_test_utils.exs
index 82efaf7..abad0a7 100644
--- a/src/integration-test/momento/integration_test_utils.exs
+++ b/src/integration-test/momento/integration_test_utils.exs
@@ -24,7 +24,7 @@ defmodule Momento.IntegrationTestUtils do
       }
     }
 
-    cache_client = CacheClient.create!(config, credential_provider)
+    cache_client = CacheClient.create!(config, credential_provider, 120.0)
     [cache_name: cache_name, cache_client: cache_client]
   end
 
diff --git a/src/lib/momento/cache_client.ex b/src/lib/momento/cache_client.ex
index 29e8555..72a0c2d 100644
--- a/src/lib/momento/cache_client.ex
+++ b/src/lib/momento/cache_client.ex
@@ -1,5 +1,8 @@
 defmodule Momento.CacheClient do
   alias Momento.Auth.CredentialProvider
+  alias Momento.Responses.{CreateCache, DeleteCache, ListCaches, Set, Get, Delete}
+  alias Momento.Responses.SortedSet
+  alias Momento.Requests.CollectionTtl
   alias Momento.Internal.ScsControlClient
   alias Momento.Internal.ScsDataClient
   alias Momento.Config.Configuration, as: Configuration
@@ -13,12 +16,14 @@ defmodule Momento.CacheClient do
   @enforce_keys [
     :config,
     :credential_provider,
+    :default_ttl_seconds,
     :control_client,
     :data_client
   ]
   defstruct [
     :config,
     :credential_provider,
+    :default_ttl_seconds,
     :control_client,
     :data_client
   ]
@@ -26,6 +31,7 @@ defmodule Momento.CacheClient do
   @opaque t() :: %__MODULE__{
             config: Configuration.t(),
             credential_provider: CredentialProvider.t(),
+            default_ttl_seconds: float(),
             control_client: ScsControlClient.t(),
             data_client: ScsDataClient.t()
           }
@@ -44,14 +50,16 @@ defmodule Momento.CacheClient do
   """
   @spec create!(
           config :: Configuration.t(),
-          credential_provider :: CredentialProvider.t()
+          credential_provider :: CredentialProvider.t(),
+          default_ttl_seconds :: float()
         ) :: t()
-  def create!(config, credential_provider) do
+  def create!(config, credential_provider, default_ttl_seconds) do
     with control_client <- ScsControlClient.create!(credential_provider),
          data_client <- ScsDataClient.create!(credential_provider) do
       %__MODULE__{
         config: config,
         credential_provider: credential_provider,
+        default_ttl_seconds: default_ttl_seconds,
         control_client: control_client,
         data_client: data_client
       }
@@ -70,7 +78,7 @@ defmodule Momento.CacheClient do
   - `{:ok, %Momento.Responses.ListCaches.Ok{caches: caches}}` on a successful listing.
   - `{:error, error}` tuple if an error occurs.
   """
-  @spec list_caches(client :: t()) :: Momento.Responses.ListCaches.t()
+  @spec list_caches(client :: t()) :: ListCaches.t()
   def list_caches(client) do
     ScsControlClient.list_caches(client.control_client)
   end
@@ -92,7 +100,7 @@ defmodule Momento.CacheClient do
   @spec create_cache(
           client :: t(),
           cache_name :: String.t()
-        ) :: Momento.Responses.DeleteCache.t()
+        ) :: CreateCache.t()
   def create_cache(client, cache_name) do
     ScsControlClient.create_cache(client.control_client, cache_name)
   end
@@ -113,7 +121,7 @@ defmodule Momento.CacheClient do
   @spec delete_cache(
           client :: t(),
           cache_name :: String.t()
-        ) :: Momento.Responses.DeleteCache.t()
+        ) :: DeleteCache.t()
   def delete_cache(client, cache_name) do
     ScsControlClient.delete_cache(client.control_client, cache_name)
   end
@@ -139,11 +147,11 @@ defmodule Momento.CacheClient do
           cache_name :: String.t(),
           key :: binary(),
           value :: binary(),
-          ttl_seconds :: float()
-        ) ::
-          Momento.Responses.Set.t()
+          ttl_seconds :: float() | nil
+        ) :: Set.t()
   def set(client, cache_name, key, value, ttl_seconds) do
-    ScsDataClient.set(client.data_client, cache_name, key, value, ttl_seconds)
+    ttl = ttl_seconds || client.default_ttl_seconds
+    ScsDataClient.set(client.data_client, cache_name, key, value, ttl)
   end
 
   @doc """
@@ -161,8 +169,7 @@ defmodule Momento.CacheClient do
   - `:miss` if the key does not exist.
   - `{:error, error}` tuple if an error occurs.
   """
-  @spec get(client :: t(), cache_name :: String.t(), key :: binary) ::
-          Momento.Responses.Get.t()
+  @spec get(client :: t(), cache_name :: String.t(), key :: binary) :: Get.t()
   def get(client, cache_name, key) do
     ScsDataClient.get(client.data_client, cache_name, key)
   end
@@ -181,9 +188,91 @@ defmodule Momento.CacheClient do
   - `{:ok, %Momento.Responses.Delete.Ok{}}` on a successful deletion.
   - `{:error, error}` tuple if an error occurs.
   """
-  @spec delete(client :: t(), cache_name :: String.t(), key :: binary) ::
-          Momento.Responses.Delete.t()
+  @spec delete(client :: t(), cache_name :: String.t(), key :: binary) :: Delete.t()
   def delete(client, cache_name, key) do
     ScsDataClient.delete(client.data_client, cache_name, key)
   end
+
+  @spec sorted_set_put_element(
+          client :: t(),
+          cache_name :: String.t(),
+          sorted_set_name :: String.t(),
+          value :: binary(),
+          score :: float(),
+          opts :: [collection_ttl :: CollectionTtl.t()]
+        ) :: SortedSet.PutElement.t()
+  def sorted_set_put_element(
+        client,
+        cache_name,
+        sorted_set_name,
+        value,
+        score,
+        opts \\ []
+      ) do
+    collection_ttl =
+      Keyword.get(opts, :collection_ttl, CollectionTtl.of(client.default_ttl_seconds))
+
+    case ScsDataClient.sorted_set_put_elements(
+           client.data_client,
+           cache_name,
+           sorted_set_name,
+           [{value, score}],
+           collection_ttl
+         ) do
+      {:ok, _} -> {:ok, %Momento.Responses.SortedSet.PutElement.Ok{}}
+      {:error, error} -> {:error, error}
+    end
+  end
+
+  @spec sorted_set_put_elements(
+          client :: t(),
+          cache_name :: String.t(),
+          sorted_set_name :: String.t(),
+          elements :: %{binary() => float()} | [{binary(), float()}],
+          opts :: [collection_ttl :: CollectionTtl.t()]
+        ) :: SortedSet.PutElements.t()
+  def sorted_set_put_elements(
+        client,
+        cache_name,
+        sorted_set_name,
+        elements,
+        opts \\ []
+      ) do
+    collection_ttl =
+      Keyword.get(opts, :collection_ttl, CollectionTtl.of(client.default_ttl_seconds))
+
+    ScsDataClient.sorted_set_put_elements(
+      client.data_client,
+      cache_name,
+      sorted_set_name,
+      elements,
+      collection_ttl
+    )
+  end
+
+  @spec sorted_set_fetch_by_rank(
+          client :: t(),
+          cache_name :: String.t(),
+          sorted_set_name :: String.t(),
+          opts :: [start_rank: integer(), end_rank: integer(), sort_order: :asc | :desc]
+        ) :: SortedSet.Fetch.t()
+  def sorted_set_fetch_by_rank(
+        client,
+        cache_name,
+        sorted_set_name,
+        opts \\ []
+      ) do
+    start_rank = Keyword.get(opts, :start_rank)
+    end_rank = Keyword.get(opts, :end_rank)
+    sort_order = Keyword.get(opts, :sort_order, :asc)
+
+    ScsDataClient.sorted_set_fetch_by_rank(
+      client.data_client,
+      cache_name,
+      sorted_set_name,
+      start_rank,
+      end_rank,
+      sort_order
+    )
+  end
 end
diff --git a/src/lib/momento/error.ex b/src/lib/momento/error.ex
index ff2db2f..0073493 100644
--- a/src/lib/momento/error.ex
+++ b/src/lib/momento/error.ex
@@ -4,18 +4,32 @@ defmodule Momento.Error do
 
   @type t() :: %__MODULE__{
           error_code: Momento.Error.Code.t(),
-          cause: String.t() | nil,
+          cause: Exception.t() | nil,
           message: String.t()
         }
 
-  @spec convert(GRPC.RPCError.t()) :: Momento.Error.t()
-  def convert(%GRPC.RPCError{status: status, message: message}) do
-    case status do
+  @spec convert(error :: Exception.t()) :: Momento.Error.t()
+  def convert(%Momento.Error{} = error), do: error
+  def convert(%GRPC.RPCError{} = error), do: convert_grpc_error(error)
+
+  def convert(%Protobuf.EncodeError{} = error),
+    do: invalid_argument("protobuf encode error", error)
+
+  def convert(error),
+    do: %Momento.Error{
+      error_code: Momento.Error.Code.unknown(),
+      cause: error,
+      message: "Momento SDK Failed to process the request."
+    }
+
+  @spec convert_grpc_error(error :: GRPC.RPCError.t()) :: Momento.Error.t()
+  defp convert_grpc_error(error) do
+    case error.status do
       # Cancelled
       1 ->
         %Momento.Error{
           error_code: Momento.Error.Code.cancelled_error(),
-          cause: message,
+          cause: error,
           message: "The request was cancelled by the server; please contact Momento."
         }
 
@@ -23,7 +37,7 @@ defmodule Momento.Error do
       2 ->
         %Momento.Error{
           error_code: Momento.Error.Code.unknown_service_error(),
-          cause: message,
+          cause: error,
           message: "The service returned an unknown response; please contact Momento."
         }
 
@@ -31,7 +45,7 @@ defmodule Momento.Error do
       3 ->
         %Momento.Error{
           error_code: Momento.Error.Code.bad_request_error(),
-          cause: message,
+          cause: error,
           message: "The request was invalid; please contact Momento."
         }
 
@@ -39,7 +53,7 @@ defmodule Momento.Error do
       4 ->
         %Momento.Error{
           error_code: Momento.Error.Code.timeout_error(),
-          cause: message,
+          cause: error,
           message:
             "The client's configured timeout was exceeded; you may need to use a Configuration with more lenient timeouts."
         }
@@ -48,7 +62,7 @@ defmodule Momento.Error do
       5 ->
         %Momento.Error{
           error_code: Momento.Error.Code.not_found_error(),
-          cause: message,
+          cause: error,
           message:
             "A cache with the specified name does not exist. To resolve this error, make sure you have created the cache before attempting to use it."
         }
@@ -57,7 +71,7 @@ defmodule Momento.Error do
       6 ->
         %Momento.Error{
           error_code: Momento.Error.Code.already_exists_error(),
-          cause: message,
+          cause: error,
           message:
             "A cache with the specified name already exists. To resolve this error, either delete the existing cache and make a new one, or use a different name."
         }
@@ -66,7 +80,7 @@ defmodule Momento.Error do
       7 ->
         %Momento.Error{
           error_code: Momento.Error.Code.permission_error(),
-          cause: message,
+          cause: error,
           message: "Insufficient permissions to perform an operation on a cache."
         }
 
@@ -74,7 +88,7 @@ defmodule Momento.Error do
       8 ->
         %Momento.Error{
           error_code: Momento.Error.Code.limit_exceeded_error(),
-          cause: message,
+          cause: error,
           message:
             "Request rate, bandwidth, or object size exceeded the limits for this account. To resolve this error, reduce your usage as appropriate or contact Momento to request a limit increase."
         }
@@ -83,7 +97,7 @@ defmodule Momento.Error do
       9 ->
         %Momento.Error{
           error_code: Momento.Error.Code.bad_request_error(),
-          cause: message,
+          cause: error,
           message: "The request was invalid; please contact Momento."
         }
 
@@ -91,7 +105,7 @@ defmodule Momento.Error do
       10 ->
         %Momento.Error{
           error_code: Momento.Error.Code.internal_server_error(),
-          cause: message,
+          cause: error,
           message:
             "An unexpected error occurred while trying to fulfill the request; please contact Momento."
         }
@@ -100,7 +114,7 @@ defmodule Momento.Error do
       11 ->
         %Momento.Error{
           error_code: Momento.Error.Code.bad_request_error(),
-          cause: message,
+          cause: error,
           message: "The request was invalid; please contact Momento."
         }
 
@@ -108,7 +122,7 @@ defmodule Momento.Error do
       12 ->
         %Momento.Error{
           error_code: Momento.Error.Code.bad_request_error(),
-          cause: message,
+          cause: error,
           message: "The request was invalid; please contact Momento."
         }
 
@@ -116,7 +130,7 @@ defmodule Momento.Error do
       13 ->
         %Momento.Error{
           error_code: Momento.Error.Code.internal_server_error(),
-          cause: message,
+          cause: error,
           message:
             "An unexpected error occurred while trying to fulfill the request; please contact Momento."
         }
@@ -125,7 +139,7 @@ defmodule Momento.Error do
       14 ->
         %Momento.Error{
           error_code: Momento.Error.Code.server_unavailable(),
-          cause: message,
+          cause: error,
           message:
             "The server was unable to handle the request; consider retrying. If the error persists, please contact Momento."
         }
@@ -134,7 +148,7 @@ defmodule Momento.Error do
       15 ->
         %Momento.Error{
           error_code: Momento.Error.Code.internal_server_error(),
-          cause: message,
+          cause: error,
           message:
             "An unexpected error occurred while trying to fulfill the request; please contact Momento."
         }
@@ -143,18 +157,18 @@ defmodule Momento.Error do
       16 ->
         %Momento.Error{
           error_code: Momento.Error.Code.authentication_error(),
-          cause: message,
+          cause: error,
           message: "Invalid authentication credentials to connect to the cache service."
         }
     end
   end
 
-  @spec invalid_argument(String.t()) :: Momento.Error.t()
-  def invalid_argument(message) do
+  @spec invalid_argument(message :: String.t(), cause :: Exception.t() | nil) :: Momento.Error.t()
+  def invalid_argument(message, cause \\ nil) do
     %Momento.Error{
       error_code: Momento.Error.Code.invalid_argument_error(),
-      cause: nil,
-      message: "Invalid argument passed to Momento client: " <> message
+      cause: cause,
+      message: "Invalid argument passed to Momento client: #{message}"
     }
   end
 end
diff --git a/src/lib/momento/internal/scs_data_client.ex b/src/lib/momento/internal/scs_data_client.ex
index c1f00d2..fa85d5b 100644
--- a/src/lib/momento/internal/scs_data_client.ex
+++ b/src/lib/momento/internal/scs_data_client.ex
@@ -1,6 +1,7 @@
 defmodule Momento.Internal.ScsDataClient do
   alias Momento.Auth.CredentialProvider
   alias Momento.Responses.{Set, Get, Delete}
+  alias Momento.Requests.CollectionTtl
   import Momento.Validation
 
   @enforce_keys [:auth_token, :channel]
@@ -104,4 +105,191 @@ defmodule Momento.Internal.ScsDataClient do
       error -> error
     end
   end
+
+  @spec sorted_set_put_elements(
+          data_client :: t(),
+          cache_name :: String.t(),
+          sorted_set_name :: String.t(),
+          elements :: %{binary() => float()} | [{binary(), float()}],
+          collection_ttl :: CollectionTtl.t()
+        ) :: Momento.Responses.SortedSet.PutElements.t()
+  def sorted_set_put_elements(
+        data_client,
+        cache_name,
+        sorted_set_name,
+        elements,
+        collection_ttl
+      ) do
+    with :ok <- validate_cache_name(cache_name),
+         :ok <- validate_sorted_set_name(sorted_set_name),
+         :ok <- validate_sorted_set_elements(elements),
+         :ok <- validate_collection_ttl(collection_ttl) do
+      try do
+        send_sorted_set_put_elements(
+          data_client,
+          cache_name,
+          sorted_set_name,
+          elements,
+          collection_ttl
+        )
+      rescue
+        e -> {:error, Momento.Error.convert(e)}
+      end
+    else
+      error -> error
+    end
+  end
+
+  @spec send_sorted_set_put_elements(
+          data_client :: t(),
+          cache_name :: String.t(),
+          sorted_set_name :: String.t(),
+          elements :: %{binary() => float()} | [{binary(), float()}],
+          collection_ttl :: CollectionTtl.t()
+        ) :: Momento.Responses.SortedSet.PutElements.t()
+  defp send_sorted_set_put_elements(
+         data_client,
+         cache_name,
+         sorted_set_name,
+         elements,
+         collection_ttl
+       ) do
+    ttl_milliseconds = collection_ttl.ttl_seconds |> Kernel.*(1000) |> round()
+    metadata = %{cache: cache_name, Authorization: data_client.auth_token}
+
+    transformed_elements =
+      Enum.map(elements, fn {value, score} ->
+        %Momento.Protos.CacheClient.SortedSetElement{
+          value: value,
+          score: score
+        }
+      end)
+
+    sorted_set_put_request = %Momento.Protos.CacheClient.SortedSetPutRequest{
+      set_name: sorted_set_name,
+      elements: transformed_elements,
+      ttl_milliseconds: ttl_milliseconds,
+      refresh_ttl: collection_ttl.refresh_ttl
+    }
+
+    case Momento.Protos.CacheClient.Scs.Stub.sorted_set_put(
+           data_client.channel,
+           sorted_set_put_request,
+           metadata: metadata
+         ) do
+      {:ok, _} -> {:ok, %Momento.Responses.SortedSet.PutElements.Ok{}}
+      {:error, error_response} -> {:error, Momento.Error.convert(error_response)}
+    end
+  end
+
+  @spec sorted_set_fetch_by_rank(
+          data_client :: t(),
+          cache_name :: String.t(),
+          sorted_set_name :: String.t(),
+          start_rank :: integer() | nil,
+          end_rank :: integer() | nil,
+          sort_order :: :asc | :desc
+        ) :: Momento.Responses.SortedSet.Fetch.t()
+  def sorted_set_fetch_by_rank(
+        data_client,
+        cache_name,
+        sorted_set_name,
+        start_rank \\ nil,
+        end_rank \\ nil,
+        sort_order \\ :asc
+      ) do
+    with :ok <- validate_cache_name(cache_name),
+         :ok <- validate_sorted_set_name(sorted_set_name),
+         :ok <- validate_index_range(start_rank, end_rank),
+         :ok <- validate_sort_order(sort_order) do
+      try do
+        send_sorted_set_fetch_by_rank(
+          data_client,
+          cache_name,
+          sorted_set_name,
+          start_rank,
+          end_rank,
+          sort_order
+        )
+      rescue
+        e -> {:error, Momento.Error.convert(e)}
+      end
+    else
+      error -> error
+    end
+  end
+
+  @spec send_sorted_set_fetch_by_rank(
+          data_client :: t(),
+          cache_name :: String.t(),
+          sorted_set_name :: String.t(),
+          start_rank :: integer() | nil,
+          end_rank :: integer() | nil,
+          sort_order :: :asc | :desc
+        ) :: Momento.Responses.SortedSet.Fetch.t()
+  defp send_sorted_set_fetch_by_rank(
+         data_client,
+         cache_name,
+         sorted_set_name,
+         start_rank,
+         end_rank,
+         sort_order
+       ) do
+    metadata = %{cache: cache_name, Authorization: data_client.auth_token}
+
+    start_index =
+      case start_rank do
+        nil -> {:unbounded_start, %Momento.Protos.CacheClient.Unbounded{}}
+        _ -> {:inclusive_start_index, start_rank}
+      end
+
+    end_index =
+      case end_rank do
+        nil -> {:unbounded_end, %Momento.Protos.CacheClient.Unbounded{}}
+        _ -> {:exclusive_end_index, end_rank}
+      end
+
+    order =
+      case sort_order do
+        :asc -> 0
+        _ -> 1
+      end
+
+    fetch_request = %Momento.Protos.CacheClient.SortedSetFetchRequest{
+      set_name: sorted_set_name,
+      order: order,
+      with_scores: true,
+      range:
+        {:by_index,
+         %Momento.Protos.CacheClient.SortedSetFetchRequest.ByIndex{
+           start: start_index,
+           end: end_index
+         }}
+    }
+
+    case Momento.Protos.CacheClient.Scs.Stub.sorted_set_fetch(
+           data_client.channel,
+           fetch_request,
+           metadata: metadata
+         ) do
+      {:ok, response} ->
+        case response.sorted_set do
+          {:found, found} ->
+            {:values_with_scores, values_with_scores} = found.elements
+
+            scored_values =
+              Enum.map(values_with_scores.elements, fn element ->
+                {element.value, element.score}
+              end)
+
+            {:ok, %Momento.Responses.SortedSet.Fetch.Hit{value: scored_values}}
+
+          {:missing, _} ->
+            :miss
+        end
+
+      {:error, error_response} ->
+        {:error, Momento.Error.convert(error_response)}
+    end
+  end
 end
diff --git a/src/lib/momento/requests/collection_ttl.ex b/src/lib/momento/requests/collection_ttl.ex
new file mode 100644
index 0000000..7e02a86
--- /dev/null
+++ b/src/lib/momento/requests/collection_ttl.ex
@@ -0,0 +1,17 @@
+defmodule Momento.Requests.CollectionTtl do
+  @enforce_keys [:ttl_seconds, :refresh_ttl]
+  defstruct [:ttl_seconds, :refresh_ttl]
+
+  @type t() :: %__MODULE__{
+          ttl_seconds: float() | nil,
+          refresh_ttl: boolean()
+        }
+
+  @spec of(ttl_seconds :: float()) :: t()
+  def of(ttl_seconds) do
+    %Momento.Requests.CollectionTtl{
+      ttl_seconds: ttl_seconds,
+      refresh_ttl: true
+    }
+  end
+end
diff --git a/src/lib/momento/responses/sorted_set/fetch.ex b/src/lib/momento/responses/sorted_set/fetch.ex
new file mode 100644
index 0000000..e4c9405
--- /dev/null
+++ b/src/lib/momento/responses/sorted_set/fetch.ex
@@ -0,0 +1,13 @@
+defmodule Momento.Responses.SortedSet.Fetch do
+  defmodule Hit do
+    @enforce_keys [:value]
+    defstruct [:value]
+
+    @type t() :: %__MODULE__{
+            value: [{binary(), float()}]
+          }
+  end
+
+  @type t() ::
+          {:ok, Momento.Responses.SortedSet.Fetch.Hit.t()} | :miss | {:error, Momento.Error.t()}
+end
diff --git a/src/lib/momento/responses/sorted_set/put_element.ex b/src/lib/momento/responses/sorted_set/put_element.ex
new file mode 100644
index 0000000..cec6b83
--- /dev/null
+++ b/src/lib/momento/responses/sorted_set/put_element.ex
@@ -0,0 +1,9 @@
+defmodule Momento.Responses.SortedSet.PutElement do
+  defmodule Ok do
+    @enforce_keys []
+    defstruct []
+    @type t() :: %__MODULE__{}
+  end
+
+  @type t() :: {:ok, Momento.Responses.SortedSet.PutElement.Ok.t()} | {:error, Momento.Error.t()}
+end
diff --git a/src/lib/momento/responses/sorted_set/put_elements.ex b/src/lib/momento/responses/sorted_set/put_elements.ex
new file mode 100644
index 0000000..3462f72
--- /dev/null
+++ b/src/lib/momento/responses/sorted_set/put_elements.ex
@@ -0,0 +1,9 @@
+defmodule Momento.Responses.SortedSet.PutElements do
+  defmodule Ok do
+    @enforce_keys []
+    defstruct []
+    @type t() :: %__MODULE__{}
+  end
+
+  @type t() :: {:ok, Momento.Responses.SortedSet.PutElements.Ok.t()} | {:error, Momento.Error.t()}
+end
diff --git a/src/lib/momento/validation.ex b/src/lib/momento/validation.ex
index 5c90a88..64fda18 100644
--- a/src/lib/momento/validation.ex
+++ b/src/lib/momento/validation.ex
@@ -1,27 +1,156 @@
 defmodule Momento.Validation do
   import Momento.Error
 
-  @spec validate_cache_name(String.t()) :: :ok | {:error, Momento.Error.t()}
-  def validate_cache_name(nil), do: {:error, invalid_argument("The cache name cannot be nil")}
-
+  @spec validate_cache_name(cache_name :: String.t()) :: :ok | {:error, Momento.Error.t()}
   def validate_cache_name(cache_name) do
-    if String.valid?(cache_name),
+    validate_string(cache_name, "cache name")
+  end
+
+  @spec validate_sorted_set_name(sorted_set_name :: String.t()) ::
+          :ok | {:error, Momento.Error.t()}
+  def validate_sorted_set_name(sorted_set_name) do
+    validate_string(sorted_set_name, "sorted set name")
+  end
+
+  @spec validate_sorted_set_elements(elements :: %{binary() => float()} | [{binary(), float()}]) ::
+          :ok | {:error, Momento.Error.t()}
+  def validate_sorted_set_elements(nil),
+    do: {:error, invalid_argument("Sorted set elements cannot be nil")}
+
+  def validate_sorted_set_elements(elements) do
+    try do
+      case Enum.all?(elements, fn {value, score} ->
+             is_binary(value) and is_float(score)
+           end) do
+        true ->
+          :ok
+
+        false ->
+          {:error,
+           invalid_argument(
+             "Sorted set elements must contain only binary values and float scores"
+           )}
+      end
+    rescue
+      e ->
+        {:error,
+         invalid_argument(
+           "Sorted set elements must be a map or list of tuples of values and scores",
+           e
+         )}
+    end
+  end
+
+  @spec validate_sort_order(sort_order :: atom()) :: :ok | {:error, Momento.Error.t()}
+  def validate_sort_order(sort_order) when sort_order in [:asc, :desc], do: :ok
+
+  def validate_sort_order(_),
+    do: {:error, invalid_argument("The sort order must be either :asc or :desc")}
+
+  @spec validate_key(key :: binary()) :: :ok | {:error, Momento.Error.t()}
+  def validate_key(key), do: validate_binary(key, "key")
+
+  @spec validate_value(value :: binary()) :: :ok | {:error, Momento.Error.t()}
+  def validate_value(value), do: validate_binary(value, "value")
+
+  @spec validate_score(score :: float()) :: :ok | {:error, Momento.Error.t()}
+  def validate_score(score), do: validate_float(score, "score")
+
+  @spec validate_ttl(ttl :: float()) :: :ok | {:error, Momento.Error.t()}
+  def validate_ttl(ttl), do: validate_positive_float(ttl, "TTL")
+
+  @spec validate_collection_ttl(collection_ttl :: Momento.Requests.CollectionTtl.t()) ::
+          :ok | {:error, Momento.Error.t()}
+  def validate_collection_ttl(collection_ttl) do
+    with :ok <-
+           validate_struct(
+             collection_ttl,
+             "collection_ttl",
+             Elixir.Momento.Requests.CollectionTtl
+           ),
+         :ok <- validate_positive_float(collection_ttl.ttl_seconds, "TTL") do
+      :ok
+    else
+      error -> error
+    end
+  end
+
+  @spec validate_index_range(start_index :: integer() | nil, end_index :: integer() | nil) ::
+          :ok | {:error, Momento.Error.t()}
+  def validate_index_range(nil, _), do: :ok
+  def validate_index_range(_, nil), do: :ok
+
+  def validate_index_range(start_index, _) when not is_integer(start_index),
+    do: {:error, invalid_argument("#{start_index} is not an integer")}
+
+  def validate_index_range(_, end_index) when not is_integer(end_index),
+    do: {:error, invalid_argument("#{end_index} is not an integer")}
+
+  def validate_index_range(start_index, end_index) when start_index < end_index, do: :ok
+
+  def validate_index_range(_, _),
+    do:
+      {:error,
+       invalid_argument("start_index (inclusive) must be less than end_index (exclusive)")}
+
+  @spec validate_not_nil(any(), String.t()) :: :ok | {:error, Momento.Error.t()}
+  def validate_not_nil(nil, name), do: {:error, invalid_argument("#{name} cannot be nil")}
+  def validate_not_nil(_, _), do: :ok
+
+  @spec validate_string(string :: String.t(), name_type :: String.t()) ::
+          :ok | {:error, Momento.Error.t()}
+  defp validate_string(nil, string_name),
+    do: {:error, invalid_argument("The #{string_name} cannot be nil")}
+
+  defp validate_string(string, string_name) do
+    if String.valid?(string),
       do: :ok,
-      else: {:error, invalid_argument("The cache name must be a string")}
+      else: {:error, invalid_argument("The #{string_name} must be a string")}
+  end
+
+  @spec validate_binary(binary :: binary(), binary_name :: String.t()) ::
+          :ok | {:error, Momento.Error.t()}
+  defp validate_binary(nil, binary_name),
+    do: {:error, invalid_argument("The #{binary_name} cannot be nil")}
+
+  defp validate_binary(binary, _) when is_binary(binary), do: :ok
+
+  defp validate_binary(_, binary_name),
+    do: {:error, invalid_argument("The #{binary_name} must be a binary")}
+
+  @spec validate_positive_float(float :: float(), float_name :: String.t()) ::
+          :ok | {:error, Momento.Error.t()}
+  defp validate_positive_float(float, float_name) do
+    case validate_float(float, float_name) do
+      :ok ->
+        if float > 0.0 do
+          :ok
+        else
+          {:error, invalid_argument("The #{float_name} must be positive")}
+        end
+
+      error ->
+        error
+    end
   end
 
-  @spec validate_key(binary()) :: :ok | {:error, Momento.Error.t()}
-  def validate_key(nil), do: {:error, invalid_argument("The key cannot be nil")}
-  def validate_key(key) when is_binary(key), do: :ok
-  def validate_key(_), do: {:error, invalid_argument("The key must be a binary")}
+  @spec validate_float(float :: float(), float_name :: String.t()) ::
+          :ok | {:error, Momento.Error.t()}
+  defp validate_float(nil, float_name),
+    do: {:error, invalid_argument("The #{float_name} cannot be nil")}
+
+  defp validate_float(float, _) when is_float(float), do: :ok
+
+  defp validate_float(_, float_name),
+    do: {:error, invalid_argument("The #{float_name} must be a float")}
+
+  @spec validate_struct(struct :: struct(), struct_name :: String.t(), struct_type :: atom()) ::
+          :ok | {:error, Momento.Error.t()}
+  defp validate_struct(nil, struct_name, _),
+    do: {:error, invalid_argument("The #{struct_name} cannot be nil")}
 
-  @spec validate_value(binary()) :: :ok | {:error, Momento.Error.t()}
-  def validate_value(nil), do: {:error, invalid_argument("The value cannot be nil")}
-  def validate_value(value) when is_binary(value), do: :ok
-  def validate_value(_), do: {:error, invalid_argument("The value must be a binary")}
+  defp validate_struct(struct, _, struct_type) when struct.__struct__ == struct_type, do: :ok
 
-  @spec validate_ttl(float()) :: :ok | {:error, Momento.Error.t()}
-  def validate_ttl(nil), do: {:error, invalid_argument("The TTL cannot be nil")}
-  def validate_ttl(ttl) when is_float(ttl) and ttl > 0.0, do: :ok
-  def validate_ttl(_), do: {:error, invalid_argument("The TTL must be a positive float")}
+  defp validate_struct(_, struct_name, struct_type),
+    do: {:error, invalid_argument("#{struct_name} must be an #{Atom.to_string(struct_type)}")}
 end
diff --git a/src/test/momento/cache_client_test.exs b/src/test/momento/cache_client_test.exs
new file mode 100644
index 0000000..a44d0c5
--- /dev/null
+++ b/src/test/momento/cache_client_test.exs
@@ -0,0 +1,465 @@
+defmodule Momento.CacheClientTest do
+  use ExUnit.Case
+
+  alias Momento.CacheClient
+
+  @fake_cache_client %Momento.CacheClient{
+    config: %Momento.Config.Configuration{
+      transport_strategy: %Momento.Config.Transport.TransportStrategy{
+        grpc_config: %Momento.Config.Transport.GrpcConfiguration{
+          deadline_millis: 5000
+        }
+      }
+    },
+    credential_provider: %Momento.Auth.CredentialProvider{
+      control_endpoint: "fake",
+      cache_endpoint: "fake",
+      auth_token: "fake"
+    },
+    default_ttl_seconds: 100.0,
+    control_client: "fake",
+    data_client: "fake"
+  }
+
+  describe "sorted_set_put_element/6" do
+    test "returns an error with a bad cache name" do
+      sorted_set_name = "sorted set name"
+      value = "value"
+      score = 1.0
+      collection_ttl = Momento.Requests.CollectionTtl.of(60.0)
+
+      {:error, error} =
+        CacheClient.sorted_set_put_element(
+          @fake_cache_client,
+          nil,
+          sorted_set_name,
+          value,
+          score,
+          collection_ttl: collection_ttl
+        )
+
+      assert String.contains?(error.message, "The cache name cannot be nil")
+
+      {:error, error} =
+        CacheClient.sorted_set_put_element(
+          @fake_cache_client,
+          12345,
+          sorted_set_name,
+          value,
+          score,
+          collection_ttl: collection_ttl
+        )
+
+      assert String.contains?(error.message, "The cache name must be a string")
+    end
+
+    test "returns an error with a bad sorted set name" do
+      cache_name = "cache name"
+      value = "value"
+      score = 1.0
+      collection_ttl = Momento.Requests.CollectionTtl.of(60.0)
+
+      {:error, error} =
+        CacheClient.sorted_set_put_element(
+          @fake_cache_client,
+          cache_name,
+          nil,
+          value,
+          score,
+          collection_ttl: collection_ttl
+        )
+
+      assert String.contains?(error.message, "The sorted set name cannot be nil")
+
+      {:error, error} =
+        CacheClient.sorted_set_put_element(
+          @fake_cache_client,
+          cache_name,
+          12345,
+          value,
+          score,
+          collection_ttl: collection_ttl
+        )
+
+      assert String.contains?(error.message, "The sorted set name must be a string")
+    end
+
+    test "returns an error with a bad value" do
+      cache_name = "cache name"
+      sorted_set_name = "sorted set name"
+      score = 1.0
+      collection_ttl = Momento.Requests.CollectionTtl.of(60.0)
+
+      {:error, error} =
+        CacheClient.sorted_set_put_element(
+          @fake_cache_client,
+          cache_name,
+          sorted_set_name,
+          nil,
+          score,
+          collection_ttl: collection_ttl
+        )
+
+      assert String.contains?(
+               error.message,
+               "Sorted set elements must contain only binary values and float scores"
+             )
+
+      {:error, error} =
+        CacheClient.sorted_set_put_element(
+          @fake_cache_client,
+          cache_name,
+          sorted_set_name,
+          12345,
+          score,
+          collection_ttl: collection_ttl
+        )
+
+      assert String.contains?(
+               error.message,
+               "Sorted set elements must contain only binary values and float scores"
+             )
+    end
+
+    test "returns an error with a bad score" do
+      cache_name = "cache name"
+      sorted_set_name = "sorted set name"
+      value = "value"
+      collection_ttl = Momento.Requests.CollectionTtl.of(60.0)
+
+      {:error, error} =
+        CacheClient.sorted_set_put_element(
+          @fake_cache_client,
+          cache_name,
+          sorted_set_name,
+          value,
+          nil,
+          collection_ttl: collection_ttl
+        )
+
+      assert String.contains?(
+               error.message,
+               "Sorted set elements must contain only binary values and float scores"
+             )
+
+      {:error, error} =
+        CacheClient.sorted_set_put_element(
+          @fake_cache_client,
+          cache_name,
+          sorted_set_name,
+          value,
+          "one",
+          collection_ttl: collection_ttl
+        )
+
+      assert String.contains?(
+               error.message,
+               "Sorted set elements must contain only binary values and float scores"
+             )
+    end
+
+    test "returns an error with a bad collection ttl" do
+      cache_name = "cache name"
+      sorted_set_name = "sorted set name"
+      value = "value"
+      score = 1.0
+
+      {:error, error} =
+        CacheClient.sorted_set_put_element(
+          @fake_cache_client,
+          cache_name,
+          sorted_set_name,
+          value,
+          score,
+          collection_ttl: "ttl"
+        )
+
+      assert String.contains?(
+               error.message,
+               "collection_ttl must be an Elixir.Momento.Requests.CollectionTtl"
+             )
+    end
+  end
+
+  describe "sorted_set_put_elements/5" do
+    test "returns an error with a bad cache name" do
+      sorted_set_name = "sorted set name"
+      elements = [{"key1", 1.0}]
+      collection_ttl = Momento.Requests.CollectionTtl.of(60.0)
+
+      {:error, error} =
+        CacheClient.sorted_set_put_elements(
+          @fake_cache_client,
+          nil,
+          sorted_set_name,
+          elements,
+          collection_ttl: collection_ttl
+        )
+
+      assert String.contains?(error.message, "The cache name cannot be nil")
+
+      {:error, error} =
+        CacheClient.sorted_set_put_elements(
+          @fake_cache_client,
+          12345,
+          sorted_set_name,
+          elements,
+          collection_ttl: collection_ttl
+        )
+
+      assert String.contains?(error.message, "The cache name must be a string")
+    end
+
+    test "returns an error with a bad sorted set name" do
+      cache_name = "cache name"
+      elements = [{"key1", 1.0}]
+      collection_ttl = Momento.Requests.CollectionTtl.of(60.0)
+
+      {:error, error} =
+        CacheClient.sorted_set_put_elements(
+          @fake_cache_client,
+          cache_name,
+          nil,
+          elements,
+          collection_ttl: collection_ttl
+        )
+
+      assert String.contains?(error.message, "The sorted set name cannot be nil")
+
+      {:error, error} =
+        CacheClient.sorted_set_put_elements(
+          @fake_cache_client,
+          cache_name,
+          12345,
+          elements,
+          collection_ttl: collection_ttl
+        )
+
+      assert String.contains?(error.message, "The sorted set name must be a string")
+    end
+
+    test "returns an error with bad elements" do
+      cache_name = "cache name"
+      sorted_set_name = "sorted set name"
+      collection_ttl = Momento.Requests.CollectionTtl.of(60.0)
+
+      {:error, error} =
+        CacheClient.sorted_set_put_elements(
+          @fake_cache_client,
+          cache_name,
+          sorted_set_name,
+          nil,
+          collection_ttl: collection_ttl
+        )
+
+      assert String.contains?(
+               error.message,
+               "Sorted set elements cannot be nil"
+             )
+
+      {:error, error} =
+        CacheClient.sorted_set_put_elements(
+          @fake_cache_client,
+          cache_name,
+          sorted_set_name,
+          [{nil, 1.0}],
+          collection_ttl: collection_ttl
+        )
+
+      assert String.contains?(
+               error.message,
+               "Sorted set elements must contain only binary values and float scores"
+             )
+
+      {:error, error} =
+        CacheClient.sorted_set_put_elements(
+          @fake_cache_client,
+          cache_name,
+          sorted_set_name,
+          [{12345, 1.0}],
+          collection_ttl: collection_ttl
+        )
+
+      assert String.contains?(
+               error.message,
+               "Sorted set elements must contain only binary values and float scores"
+             )
+
+      {:error, error} =
+        CacheClient.sorted_set_put_elements(
+          @fake_cache_client,
+          cache_name,
+          sorted_set_name,
+          [{"key1", nil}],
+          collection_ttl: collection_ttl
+        )
+
+      assert String.contains?(
+               error.message,
+               "Sorted set elements must contain only binary values and float scores"
+             )
+
+      {:error, error} =
+        CacheClient.sorted_set_put_elements(
+          @fake_cache_client,
+          cache_name,
+          sorted_set_name,
+          [{"key1", "one"}],
+          collection_ttl: collection_ttl
+        )
+
+      assert String.contains?(
+               error.message,
+               "Sorted set elements must contain only binary values and float scores"
+             )
+    end
+
+    test "returns an error with a bad collection ttl" do
+      cache_name = "cache name"
+      sorted_set_name = "sorted set name"
+      elements = [{"key1", 1.0}]
+
+      {:error, error} =
+        CacheClient.sorted_set_put_elements(
+          @fake_cache_client,
+          cache_name,
+          sorted_set_name,
+          elements,
+          collection_ttl: "ttl"
+        )
+
+      assert String.contains?(
+               error.message,
+               "collection_ttl must be an Elixir.Momento.Requests.CollectionTtl"
+             )
+    end
+  end
+
+  describe "sorted_set_fetch_by_rank/6" do
+    test "returns an error with a bad cache name" do
+      sorted_set_name = "sorted set name"
+      start_rank = 1
+      end_rank = 10
+      sort_order = :asc
+
+      {:error, error} =
+        CacheClient.sorted_set_fetch_by_rank(
+          @fake_cache_client,
+          nil,
+          sorted_set_name,
+          start_rank: start_rank,
+          end_rank: end_rank,
+          sort_order: sort_order
+        )
+
+      assert String.contains?(error.message, "The cache name cannot be nil")
+
+      {:error, error} =
+        CacheClient.sorted_set_fetch_by_rank(
+          @fake_cache_client,
+          12345,
+          sorted_set_name,
+          start_rank: start_rank,
+          end_rank: end_rank,
+          sort_order: sort_order
+        )
+
+      assert String.contains?(error.message, "The cache name must be a string")
+    end
+
+    test "returns an error with a bad sorted set name" do
+      cache_name = "cache name"
+      start_rank = 1
+      end_rank = 10
+      sort_order = :asc
+
+      {:error, error} =
+        CacheClient.sorted_set_fetch_by_rank(
+          @fake_cache_client,
+          cache_name,
+          nil,
+          start_rank: start_rank,
+          end_rank: end_rank,
+          sort_order: sort_order
+        )
+
+      assert String.contains?(error.message, "The sorted set name cannot be nil")
+
+      {:error, error} =
+        CacheClient.sorted_set_fetch_by_rank(
+          @fake_cache_client,
+          cache_name,
+          12345,
+          start_rank: start_rank,
+          end_rank: end_rank,
+          sort_order: sort_order
+        )
+
+      assert String.contains?(error.message, "The sorted set name must be a string")
+    end
+
+    test "returns an error with bad ranks" do
+      cache_name = "cache name"
+      sorted_set_name = "sorted set name"
+      sort_order = :asc
+
+      {:error, error} =
+        CacheClient.sorted_set_fetch_by_rank(
+          @fake_cache_client,
+          cache_name,
+          sorted_set_name,
+          start_rank: "start",
+          end_rank: 10,
+          sort_order: sort_order
+        )
+
+      assert String.contains?(error.message, "start is not an integer")
+
+      {:error, error} =
+        CacheClient.sorted_set_fetch_by_rank(
+          @fake_cache_client,
+          cache_name,
+          sorted_set_name,
+          start_rank: 1,
+          end_rank: "end",
+          sort_order: sort_order
+        )
+
+      assert String.contains?(error.message, "end is not an integer")
+
+      {:error, error} =
+        CacheClient.sorted_set_fetch_by_rank(
+          @fake_cache_client,
+          cache_name,
+          sorted_set_name,
+          start_rank: 100,
+          end_rank: 10,
+          sort_order: sort_order
+        )
+
+      assert String.contains?(
+               error.message,
+               "start_index (inclusive) must be less than end_index (exclusive)"
+             )
+    end
+
+    test "returns an error with a sort order" do
+      cache_name = "cache name"
+      sorted_set_name = "sorted set name"
+      start_rank = 1
+      end_rank = 10
+
+      {:error, error} =
+        CacheClient.sorted_set_fetch_by_rank(
+          @fake_cache_client,
+          cache_name,
+          sorted_set_name,
+          start_rank: start_rank,
+          end_rank: end_rank,
+          sort_order: :bad_order
+        )
+
+      assert String.contains?(error.message, "The sort order must be either :asc or :desc")
+    end
+  end
+end