laravel-shopify icon indicating copy to clipboard operation
laravel-shopify copied to clipboard

Too Many Requests Issue On Pagination

Open mehmet-gimo opened this issue 1 year ago • 1 comments

#The REST Admin API supports a limit of 40 requests per app per store per minute. This allotment replenishes at a rate of 2 requests per second. The rate limit is increased by a factor of 10 for Shopify Plus stores.

Is it possible to add 1 second sleep() between 2 requests

Many thanks

mehmet-gimo avatar Nov 12 '23 23:11 mehmet-gimo

I created a trait to handle this and then manually call it AFTER each Shopify request. It would be ideal to do it BEFORE making a request, but the structure of MakesHttpRequests class and its lastResponse variable make it impossible with the current code base.

I took the approach of adding configuration settings for the threshold and sleep time, so that you can configure it differently depending on the environment. Here it is, in case it can help you out until there's an official solution:

trait HandlesShopifyRateLimit
{
    protected Shopify $shopify;

    protected int $rateLimitThreshold;
    protected int $rateLimitSleepTime;

    /**
     * WARNING: Do NOT call this before the first `$this->shopify->___` call, because there will not yet be
     * an existing last response.
     *
     * Check the API call rate limit based on the last response, to see if we're approaching our limit, and
     * to sleep if so, to recover our calls.
     * This needs to be called before any `$this->shopify->___` calls that you want to protect.
     *
     * @return void
     */
    protected function handleRateLimit(): void
    {

        $limit = $this->shopify->getLastResponse()?->headers()["X-Shopify-Shop-Api-Call-Limit"][0] ?? null;
        if (is_null($limit)) {
            return;
        }

        // set up the configurable values once, sort of hacking around a constructor
        if (!isset($this->rateLimitThreshold)) {
            $this->rateLimitThreshold = $this->getRateLimitThreshold();
        }
        if (!isset($this->rateLimitSleepTime)) {
            $this->rateLimitSleepTime = $this->getRateLimitSleepTime();
        }

        $current = intval(Str::before($limit, "/"));
        $max = intval(Str::after($limit, "/"));

        if ($max - $current <= $this->rateLimitThreshold) {
            sleep($this->rateLimitSleepTime);
        }
    }

    /**
     * Get the threshold of what we'll allow the rate limit to get within
     *
     * @return int
     */
    protected function getRateLimitThreshold(): int
    {
        return config('shopify.rate_limit.threshold');
    }

    /**
     * Get the number of seconds that we'll sleep for when we hit the rate limit threshold
     *
     * @return int
     */
    protected function getRateLimitSleepTime(): int
    {
        return config('shopify.rate_limit.sleep_time');
    }
}

Then I can just make the call like this:

$shopifyProduct = $this->shopify->getProduct($shopifyProductId);
$this->handleRateLimit();

BrandonKerr avatar Nov 20 '23 15:11 BrandonKerr