Hash#fetch is not relevant here. That's because fetch is the same as Hash#[] when, as here, fetch has only a single argument. So let's concentrate on dig.
A family of three dig methods were introduced in Ruby v2.3: Hash#dig, Array#dig and OpenStruct#dig. An interesting thing about these methods is that they invoke each other (but that's not explained in the docs, not even in the examples). In your problem we can write:
response.dig(:results, 0, :value, :destination)
  #=> "Rome" 
response is a hash so Hash#dig evaluates response[:results]. If it's value is nil then the expression returns nil. For example,
response.dig(:cat, 0, :value, :destination)
  #=> nil
In fact, response[:results] is an array:
arr = response[:results]
  #=> [{:type=>"product_group", :value=>{:destination=>"Rome"}},
  #    {:type=>"product_group", :value=>{:destination=>"Paris"}},
  #    {:type=>"product_group", :value=>{:destination=>"Madrid"}}]
Hash#dig therefore invokes Array#dig on arr, obtaining the hash
h = arr.dig(0)
  #=> {:type=>"product_group", :value=>{:destination=>"Rome"}} 
Array#dig then invokes Hash#dig on h:
g = h.dig(:value)
  #=> {:destination=>"Rome"}
Lastly, g being a hash, Hash#dig invokes Hash#dig on g:
g.dig(:destination)
  #=> "Rome"
Caution needs to be exercised when using any of the dig's. Suppose
arr = [[1,2], [3,[4,5]]]
and we wish to pull out the object that is now occupied by 4. We could write either
arr[1][1][0]
  #=> 4
or
arr.dig(1,1,0)
  #=> 4
Now suppose arr were changed as follows:
arr = [[1,2]]
Then
arr[1][1][0]
  #=> NoMethodError: undefined method `[]' for nil:NilClass
and
arr.dig(1,1,0)
  #=> nil
Both are indicative of there being an error in our code, so raising an exception would be preferable to nil being returned, which may go unnoticed for some time.