   end
 
   @doc """
-  Fetches rates from `carriers` for a given `Shipment`.
-  """
-  @spec fetch_rates(Shipment.t(), [Carrier.t()] | nil) :: [{atom, Rate.t()}]
-  def fetch_rates(%Shipment{} = shipment, carriers \\ nil) do
+  Fetches rates for a given `shipment`. Possible options:
+
+    * `carriers` - Fetches rates for *all* services for the given carriers
+    * `services` - Fetches rates only for the given services
+
+  These may be used in combination. To fetch rates for *all* UPS services, as
+  well as USPS Priority, for example:
+
+      Shippex.fetch_rates(shipment, carriers: :ups, services: [:usps_priority])
+
+  If no options are provided, Shippex will fetch rates for every service from
+  every available carrier.
+  """
+  @spec fetch_rates(Shipment.t(), Keyword.t()) :: [{atom, Rate.t()}]
+  def fetch_rates(%Shipment{} = shipment, opts \\ []) do
     # Convert the atom to a list if necessary.
+    carriers = Keyword.get(opts, :carriers)
+    services = Keyword.get(opts, :services)
+
     carriers =
-      cond do
-        is_nil(carriers) ->
-          Shippex.carriers()
-
-        is_atom(carriers) ->
-          [carriers]
-
-        is_list(carriers) ->
-          carriers
-
-        true ->
+      if is_nil(carriers) and is_nil(services) do
+        Shippex.carriers()
+      else
+        cond do
+          is_nil(carriers) ->
+            []
+
+          is_atom(carriers) ->
+            [carriers]
+
+          is_list(carriers) ->
+            carriers
+
+          true ->
+            raise """
+            #{inspect(carriers)} is an invalid carrier or list of carriers.
+            Try using an atom. For example:
+
+                Shippex.fetch_rates(shipment, carriers: :usps)
+            """
+        end
+      end
+
+    services =
+      case services do
+        nil ->
+          []
+
+        service when is_atom(service) ->
+          [service]
+
+        services when is_list(services) ->
+          services
+
+        services ->
           raise """
-          #{inspect(carriers)} is an invalid carrier or list of carriers.
+          #{inspect(services)} is an invalid service or list of services.
           Try using an atom. For example:
 
-              Shippex.fetch_rates(shipment, :ups)
+              Shippex.fetch_rates(shipment, services: :usps_priority)
           """
       end
-      |> Enum.map(&Shippex.Carrier.module/1)
-
-    rates = Enum.reduce(carriers, [], &(&1.fetch_rates(shipment) ++ &2))
+      |> Enum.reject(&(Service.get(&1).carrier in carriers))
+
+    carrier_tasks =
+      Enum.map(carriers, fn carrier ->
+        Task.async(fn ->
+          Carrier.module(carrier).fetch_rates(shipment)
+        end)
+      end)
+
+    service_tasks =
+      Enum.map(services, fn service ->
+        Task.async(fn ->
+          fetch_rate(shipment, service)
+        end)
+      end)
+
+    rates =
+      (carrier_tasks ++ service_tasks)
+      |> Task.yield_many(5000)
+      |> Enum.map(fn {task, rates} ->
+        rates || Task.shutdown(task, :brutal_kill)
+      end)
+      |> Enum.filter(fn
+        {:ok, _} = result -> true
+        _ -> false
+      end)
+      |> Enum.map(fn {:ok, rates} -> rates end)
+      |> List.flatten()
+      |> Enum.reject(fn
+        {atom, _} -> not (atom in [:ok, :error])
+        _ -> true
+      end)
+
     oks = Enum.filter(rates, &(elem(&1, 0) == :ok))
     errors = Enum.filter(rates, &(elem(&1, 0) == :error))
 

  Returns the configured currency code. Raises an error if an invalid code was
  used.

      config :shippex, :currency, :can

      Shippex.currency_code() #=> "CAN"
  """
  @spec currency_code() :: String.t()
  def currency_code() do
    case Application.get_env(:shippex, :currency, :usd) do
      code when code in [:usd, :can, :mxn] ->

    defexception [:message]

    def exception(message) do
      %InvalidConfigError{message: message}
    end
  end

