¶ ↑
shopify-api-limitsMy friend Dave (aka “hunkybill”) posted a problem to me one day about ShopifyAPI call limits, offering a case of beer if I could find a solution: forums.shopify.com/categories/9/posts/49003
So in the HTTP headers, the ShopifyAPI
will return to you in each API request how many calls you’ve made, as well as the maximum number of calls available.
¶ ↑
ProblemActiveResource
does not make it easy to read the HTTP response headers, since the method #request
in ActiveResource::Connection
does not save a reference to the HTTP response:
# Makes a request to the remote service. def request(method, path, *arguments) result = ActiveSupport::Notifications.instrument("request.active_resource") do |payload| payload[:method] = method payload[:request_uri] = "#{site.scheme}://#{site.host}:#{site.port}#{path}" payload[:result] = http.send(method, path, *arguments) end handle_response(result) # <-- right here: handle_response returns an instance of HTTPResponse but doesn't save a ref to it! rescue Timeout::Error => e raise TimeoutError.new(e.message) rescue OpenSSL::SSL::SSLError => e raise SSLError.new(e.message) end
¶ ↑
SolutionHack ActiveResource::Connection
to introduce a new attr_reader :response
and capture the returned instance of HTTPResponse
provided by net/http
from the #handle_response
method.
module ActiveResource class Connection # HACK: Add an attr_reader for response attr_reader :response # capture the original #handle_response as an unbound method instead of using alias handle_response = self.instance_method(:handle_response) # re-implement #handle_response to capture the returned HTTPResponse to an instance var. define_method(:handle_response) do |response| @response = handle_response.bind(self).call(response) end end end
Now it’s possible to access the HTTPResponse
instance directly from ActiveResource
, via:
foo = ActiveResource::Base.connection.response['http-header-param-foo']
¶ ↑
Installationgem "shopify_api" gem "shopify-api-limits"
¶ ↑
Usagecount_shop = ShopifyAPI.credit_used :shop limit_shop = ShopifyAPI.credit_limit :shop count_global = ShopifyAPI.credit_used :global limit_global = ShopifyAPI.credit_limit :global
Generally, you shouldn’t need to use the methods above directly – rather, they’re used under-the-hood by the following helpful methods which don’t require a scope (:shop/:global
): If the :global scope has fewer calls available than the :local
scope, the methods will operate upon the :global
scope; otherwise, values will be returned based upon the :shop
scope.
unless ShopifyAPI.credit_maxed? #make a ShopifyAPI call end until ShopifyAPI.credit_maxed? || stop_condition # make some ShopifyAPI calls end while ShopifyAPI.credit_left || stop_condition # make some ShopifyAPI calls end
¶ ↑
A special bonus for retrieving large recordsets > 250 recordsShopify places a hard limit of 250
on the number of records returned in a single request. There are ways around this, including one listed here bit.ly/kgwCRc which involves manually calculating the total & number of pages then iterating to make several API calls. This gem encapsulates this behaviour allowing you to make what feels like a single call to the ShopifyAPI
. Simply set :params => {:limit => false}
. False as in, “no limit”
For example, imagine a store which has 251 orders and you want to fetch them all. Since the maximum number of records returned in a single request is 250
, 2 requests must be made to the API in order to fetch all 251
records.
records = ShopifyAPI::Order.all(:params => {:limit => false}) puts records.count => 251
Without {:limit => false}
, the normal behaviour will operate (ie: just one request with 250 records returned):
records = ShopifyAPI::Order.all(:params => {:limit => 250}) puts records.count => 250
If you don’t have enough API credits to perform the multiple requests to serve your desired recordset, a ShopifyAPI::Limits::Error
will be raised:
puts ShopifyAPI::Order.count => 251 puts ShopifyAPI.credit_left => 1 begin rs = ShopifyAPI::Order.all(:params => {:limit => false}) rescue ShopifyAPI::Limits::Error puts "Uhoh...didn't have enough credits to do that. Maybe you wanna' queue your task for a later date." end