Ruby private app access to Shopify Resources

Topic summary

A developer shares working Ruby code for accessing Shopify resources via a private app using the shopify_api gem (version 13.0.1 with Ruby 2.2.3). The solution involves configuring ShopifyAPI::Context with is_private: true and using the app’s access token as the api_secret_key.

Key Implementation Details:

  • Set api_key, scope, and host_name to “DUMMY” for private apps
  • Use the private app’s access token (found in API credentials section when creating the app)
  • Create a session with the shop URL and access token
  • Use ShopifyAPI::Clients::Rest::Admin client for API calls
  • Implement pagination using next_page_info and rate limiting with sleep intervals

Troubleshooting:
One user encountered “Invalid API key or access token” errors, which was resolved by clarifying that the app token (not API key) should be used as api_secret_key.

Update (2024):
Due to breaking changes in the shopify_api gem, the original poster migrated to using direct REST API calls with HTTParty instead of the Ruby gem, implementing custom pagination and rate limit handling by parsing response headers.

Summarized with AI on November 5. AI used: claude-sonnet-4-5-20250929.

You want a private app, simple REST Shopify script in Ruby using the latest shopify_api gem to download resources like orders, customers, products etc. [Maybe to store in a Database, do something with them].

After much trial/error and reading the official docs on Rubydoc.info, here is what works for me:

def get_orders
        puts "Starting ..."

        ShopifyAPI::Context.setup(
            api_key: "DUMMY",
            api_secret_key: _token,
            scope: "DUMMY",
            host_name: "DUMMY",
            private_shop: "#{@shopname}.myshopify.com",
            #session_storage: ShopifyAPI::Auth::FileSessionStorage.new,
            #session_storage: CustomSessionStorage.new,
            is_embedded: false, 
            is_private: true, 
            api_version: "2023-07"
        
        )

        

        session = ShopifyAPI::Auth::Session.new(shop: "#{@shopname}.myshopify.com", access_token: _token)

        puts session.inspect

        client = ShopifyAPI::Clients::Rest::Admin.new(session: session)

        

        

        response = client.get(path: "orders", query: { created_at_min: '2023-05-01T00:59:59-08:00', created_at_max: '2023-07-18T23:59:59-08:00', status: 'any', limit: 250 })

        response.body['orders'].each do |myr|
            puts "-----------"
            puts myr.inspect
            puts "-----------"
        end

        loop do
            puts "sleeping 10 seconds ..."
            sleep 10

            break unless response.next_page_info
            response = client.get(path: "orders", query: {  limit: 250, page_info: response.next_page_info })

            response.body['orders'].each do |myr|
                puts "-----------"
                puts myr.inspect
                puts "-----------"
            end

        end

    end

Hope this helps someone.

Forgot to add, works with shopify_api (13.1.0), ruby 3.2.2

Hello @fwallace I am trying to replicate your code, but IM getting an weird error when fetching the data:

eval error: {“errors”:“[API] Invalid API key or access token (unrecognized login or wrong password)”,“error_reference”:“If you report this error, please include this id: 4bb181fe-6d59-4377-893a-c70bdea43fef.”}

The data into api_key: “DUMMY”, api_secret_key: @app_token,

is the one here right?

Hi, the @app _token is the app token in setting up a private app:

You can find it when you add a private app in the API credentials sections of creating the app.

Thanks FWallace. I really cant find where I can create those private apps. It doesnt appears for me on my Admin screen.

Hello,
thanks for sharing!

Is this method is still working in October 2024 ?

thanks for your help

Hi! I really don’t know, there were so many breaking changes in the shopify_api gem that I ended up redoing the code using just the REST api no ruby gems:

store_url = ApiShopify.new(ENV['SHOPIFY_SHOP_NAME']).store_url
      store_header = ApiShopify.new(ENV['SHOPIFY_SHOP_NAME']).get_header
      store_url = store_url + "products.json?limit=250"

      time_start = Time.now

      my_products = HTTParty.get(store_url, :headers => store_header)

      #puts my_products.inspect

      my_products.parsed_response['products'].each do |myp|
        puts "-----------"
        puts myp.inspect
        puts "-------------"
        product_array.push(create_product_hash(myp))
        my_variants = myp['variants']
        my_variants.each do |myvar|
          puts "    **************"
          puts "       #{myvar.inspect}"
          variant_array.push(create_variant_hash(myvar))
          puts "    *************"
        end
        

      end

ApiShopify.determine_sleep(my_products.headers['x-shopify-shop-api-call-limit'])

      if my_products.headers.key?('link')
        local_next_page = LinkParser.parse_next_page(my_products.headers['link'])

        loop do
          my_products = HTTParty.get(local_next_page, :headers => store_header)
          ApiShopify.determine_sleep(my_products.headers['x-shopify-shop-api-call-limit'])
          puts "---- subsequent runs -----------"
          my_products.parsed_response['products'].each do |myp|
            puts "-----------"
            puts myp.inspect
            puts "-------------"
            product_array.push(create_product_hash(myp))
            my_variants = myp['variants']
            my_variants.each do |myvar|
              puts "    **************"
              puts "       #{myvar.inspect}"
              puts "    *************"
              variant_array.push(create_variant_hash(myvar))
            end
            
    
          end
            
    
          # end
          puts "*******************************"
          if my_products.headers['link'] !~ /rel="next"/i
            break
          else
            puts "next_page info = #{my_products.headers['link']}"
            local_next_page = LinkParser.parse_next_page_subsequent(my_products.headers['link'])
            puts "||||||| #{local_next_page} ||||||||||"
            
          end

        end        

      else
        puts "No more products"
      end
#file lib/api_shopify.rb

class ApiShopify

  def initialize(shop_name)
    _url = "https://#{shop_name}.myshopify.com/admin/api/#{ENV['API_VERSION']}/"
     = ENV['SHOPIFY_API_TOKEN']
    _header = {"X-Shopify-Access-Token" => }
    _header = {"X-Shopify-Access-Token" => , 
      "Content-Type" => "application/json"}

  end

  def store_url
    return _url
  end

  def get_header
    return _header
  end

  def get_change_header
    return _header
  end

  def self.determine_sleep(header)
    # puts "received header #{header}"
    # puts header.inspect
    parts_of_header = header.split("/")
    # puts parts_of_header.inspect
    numerator = parts_of_header[0].to_i
    denominator = parts_of_header[1].to_f
    percentage_used = (numerator/denominator)*100
    puts "percentage_used = #{percentage_used}"
    if percentage_used > 65.0
      puts "sleeping 10 secs"
      sleep 10

    else
      puts "not sleeping"
    end
    

  end
  
end