PHPackages                             segwitz/basic-shopify-api - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [HTTP &amp; Networking](/categories/http)
4. /
5. segwitz/basic-shopify-api

ActiveLibrary[HTTP &amp; Networking](/categories/http)

segwitz/basic-shopify-api
=========================

A basic Shopify API wrapper with REST and GraphQL support, powered by Guzzle.

v10.0.6(4y ago)0281MITPHPPHP &gt;=7.3.0

Since Jul 6Pushed 4y agoCompare

[ Source](https://github.com/SegWitz/Basic-Shopify-API)[ Packagist](https://packagist.org/packages/segwitz/basic-shopify-api)[ RSS](/packages/segwitz-basic-shopify-api/feed)WikiDiscussions master Synced 1mo ago

READMEChangelogDependencies (6)Versions (40)Used By (1)

Basic Shopify API
=================

[](#basic-shopify-api)

[![Tests](https://github.com/osiset/Basic-Shopify-API/workflows/Package%20Test/badge.svg?branch=master)](https://github.com/osiset/Basic-Shopify-API/workflows/Package%20Test/badge.svg?branch=master)[![codecov](https://camo.githubusercontent.com/dd8ea2f32a2907e3a748ecd5f6f5365506527b383fd7045e9d350acae2211ba7/68747470733a2f2f636f6465636f762e696f2f67682f6f73697365742f42617369632d53686f706966792d4150492f6272616e63682f6d61737465722f67726170682f62616467652e7376673f746f6b656e3d717155754c4974714a6a)](https://codecov.io/gh/osiset/Basic-Shopify-API)[![License](https://camo.githubusercontent.com/372eb1ccf259c18112ca3d49751dd1077a60a258c60b17b375b2b85fff8487bd/68747470733a2f2f706f7365722e707567782e6f72672f6f73697365742f62617369632d73686f706966792d6170692f6c6963656e7365)](https://packagist.org/packages/osiset/basic-shopify-api)

A simple, tested, API wrapper for Shopify using Guzzle.

It supports both the sync/async REST and GraphQL API provided by Shopify, basic rate limiting, and request retries.

It contains helpful methods for generating a installation URL, an authorize URL (offline and per-user), HMAC signature validation, call limits, and API requests.

It works with both OAuth and private API apps.

Table of Contents
-----------------

[](#table-of-contents)

- [Installation](#installation)
- [Usage](#usage)
    - [Public API](#public-api)
        - [REST (sync)](#rest-sync)
        - [REST (async)](#rest-async)
        - [GraphQL (sync)](#graphql-sync)
        - [GraphQL (async)](#graphql-async)
        - [Getting access (offline)](#getting-access-offline)
        - [Getting access (per-user)](#getting-access-per-user)
        - [Verifying HMAC signature](#verifying-hmac-signature)
    - [Private API](#private-api)
        - [REST](#rest)
        - [GraphQL](#graphql)
    - [Making requests](#making-requests)
        - [REST](#rest-1)
            - [If sync is true (regular rest call)](#if-sync-is-true-regular-rest-call)
            - [If sync is false (restAsync call)](#if-sync-is-false-restasync-call)
            - [Overriding request type](#overriding-request-type)
            - [Passing additional request options](#passing-additional-request-options)
        - [GraphQL](#graphql-1)
    - [API Versioning](#api-versioning)
    - [Rate Limiting](#rate-limiting)
    - [page\_info / pagination Support](#page_info--pagination-support)
    - [Isolated API calls](#isolated-api-calls)
    - [Retries](#retries)
    - [Errors](#errors)
    - [Middleware](#middleware)
    - [Storage](#storage)
- [Documentation](#documentation)
- [LICENSE](#license)

Installation
------------

[](#installation)

The recommended way to install is [through composer](http://packagist.org).

```
composer require segwitz/basic-shopify-api

```

Usage
-----

[](#usage)

### Public API

[](#public-api)

This assumes you properly have your app setup in the partner's dashboard with the correct keys and redirect URIs.

#### REST (sync)

[](#rest-sync)

For REST calls, the shop domain and access token are required.

```
use Segwitz\BasicShopifyAPI\BasicShopifyAPI;
use Segwitz\BasicShopifyAPI\Options;
use Segwitz\BasicShopifyAPI\Session;

// Create options for the API
$options = new Options();
$options->setVersion('2020-01');

// Create the client and session
$api = new BasicShopifyAPI($options);
$api->setSession(new Session('example.myshopify.com', 'access-token-here'));

// Now run your requests...
$result = $api->rest(...);
```

#### REST (async)

[](#rest-async)

For REST calls, the shop domain and access token are required.

```
use Segwitz\BasicShopifyAPI\BasicShopifyAPI;
use Segwitz\BasicShopifyAPI\Options;
use Segwitz\BasicShopifyAPI\Session;

// Create options for the API
$options = new Options();
$options->setVersion('2020-01');

// Create the client and session
$api = new BasicShopifyAPI($options);
$api->setSession(new Session('example.myshopify.com', 'access-token-here'));

// Now run your requests...
$promise = $api->restAsync(...);
$promise->then(function (array $result) {
  // ...
});
```

#### GraphQL (sync)

[](#graphql-sync)

For GraphQL calls, the shop domain and access token are required.

```
use Segwitz\BasicShopifyAPI\BasicShopifyAPI;
use Segwitz\BasicShopifyAPI\Options;
use Segwitz\BasicShopifyAPI\Session;

// Create options for the API
$options = new Options();
$options->setVersion('2020-01');

// Create the client and session
$api = new BasicShopifyAPI($options);
$api->setSession(new Session('example.myshopify.com', 'access-token-here'));

// Now run your requests...
$result = $api->graph(...);
```

#### GraphQL (async)

[](#graphql-async)

For GraphQL calls, the shop domain and access token are required.

```
use Segwitz\BasicShopifyAPI\BasicShopifyAPI;
use Segwitz\BasicShopifyAPI\Options;
use Segwitz\BasicShopifyAPI\Session;

// Create options for the API
$options = new Options();
$options->setVersion('2020-01');

// Create the client and session
$api = new BasicShopifyAPI($options);
$api->setSession(new Session('example.myshopify.com', 'access-token-here'));

// Now run your requests...
$promise = $api->graphAsync(...);
$promise->then(function (array $result) {
  // ...
});
```

#### Getting access (offline)

[](#getting-access-offline)

This is the default mode which returns a permanent token.

After obtaining the user's shop domain, to then direct them to the auth screen use `getAuthUrl`, as example (basic PHP):

```
// Create options for the API
$options = new Options();
$options->setVersion('2020-01');
$options->setApiKey(env('SHOPIFY_API_KEY'));
$options->setApiSecret(env('SHOPIFY_API_SECRET'));

// Create the client and session
$api = new BasicShopifyAPI($options);
$api->setSession(new Session($_SESSION['shop']));

$code = $_GET['code'];
if (!$code) {
  /**
   * No code, send user to authorize screen
   * Pass your scopes as an array for the first argument
   * Pass your redirect URI as the second argument
   */
  $redirect = $api->getAuthUrl(env('SHOPIFY_API_SCOPES'), env('SHOPIFY_API_REDIRECT_URI'));
  header("Location: {$redirect}");
  exit;
} else {
  // We now have a code, lets grab the access token
  $api->requestAndSetAccess($code);

  // You can now make API calls
  $request = $api->rest('GET', '/admin/shop.json'); // or GraphQL
}
```

#### Getting access (per-user)

[](#getting-access-per-user)

You can also change the grant mode to be `per-user` as [outlined in Shopify documentation](https://help.shopify.com/en/api/getting-started/authentication/oauth/api-access-modes). This will receieve user info from the user of the app within the Shopify store. The token recieved will expire at a specific time.

```
// Create options for the API
$options = new Options();
$options->setVersion('2020-01');
$options->setApiKey(env('SHOPIFY_API_KEY'));
$options->setApiSecret(env('SHOPIFY_API_SECRET'));

// Create the client and session
$api = new BasicShopifyAPI($options);
$api->setSession(new Session($_SESSION['shop']));

$code = $_GET['code'];
if (!$code) {
  /**
   * No code, send user to authorize screen
   * Pass your scopes as an array for the first argument
   * Pass your redirect URI as the second argument
   * Pass your grant mode as the third argument
   */
  $redirect = $api->getAuthUrl(env('SHOPIFY_API_SCOPES'), env('SHOPIFY_API_REDIRECT_URI'), 'per-user');
  header("Location: {$redirect}");
  exit;
} else {
  // We now have a code, lets grab the access object
  $api->requestAndSetAccess($code);

  // You can now make API calls
  $request = $api->rest('GET', '/admin/shop.json'); // or GraphQL
}
```

#### Verifying HMAC signature

[](#verifying-hmac-signature)

Simply pass in an array of GET params.

```
// Will return true or false if HMAC signature is good.
$valid = $api->verifyRequest($_GET);
```

### Private API

[](#private-api)

This assumes you properly have your app setup in the partner's dashboard with the correct keys and redirect URIs.

#### REST

[](#rest)

For REST (sync) calls, shop domain, API key, and API password are request

```
// Create options for the API
$options = new Options();
$options->setType(true); // Makes it private
$options->setVersion('2020-01');
$options->setApiKey(env('SHOPIFY_API_KEY'));
$options->setApiPassword(env('SHOPIFY_API_PASSWORD'));

// Create the client and session
$api = new BasicShopifyAPI($options);
$api->setSession(new Session($_SESSION['shop']));

// Now run your requests...
$result = $api->rest(...);
```

#### GraphQL

[](#graphql)

For GraphQL calls, shop domain and API password are required.

```
// Create options for the API
$options = new Options();
$options->setVersion('2020-01');
$options->setApiPassword(env('SHOPIFY_API_PASSWORD'));

// Create the client and session
$api = new BasicShopifyAPI($options);
$api->setSession(new Session($_SESSION['shop']));

// Now run your requests...
$result = $api->graph(...);
```

### Making requests

[](#making-requests)

#### REST

[](#rest-1)

Requests are made using Guzzle.

```
$api->rest(string $type, string $path, array $params = null, array $headers = [], bool $sync = true);
// or $api->getRestClient()->request(....);
```

- `type` refers to GET, POST, PUT, DELETE, etc
- `path` refers to the API path, example: `/admin/products/1920902.json`
- `params` refers to an array of params you wish to pass to the path, examples: `['handle' => 'cool-coat']`
- `headers` refers to an array of custom headers you would like to optionally send with the request, example: `['X-Shopify-Test' => '123']`
- `sync` refers to if the request should be synchronous or asynchronous.

You can use the alias `restAsync` to skip setting `sync` to `false`.

##### If sync is true (regular rest call):

[](#if-sync-is-true-regular-rest-call)

The return value for the request will be an array containing:

- `response` the full Guzzle response object
- `body` the JSON decoded response body (\\Segwitz\\BasicShopifyAPI\\ResponseAccess instance)
- `errors` if any errors are detected, true/false
- `exception` if errors are true, exception object is available
- `status` the HTTP status code
- `link` an array of previous/next pagination values, if available

*Note*: `request()` will alias to `rest()` as well.

##### If sync is false (restAsync call):

[](#if-sync-is-false-restasync-call)

The return value for the request will be a Guzzle promise which you can handle on your own.

The return value for the promise will be an object containing:

- `response` the full Guzzle response object
- `body` the JSON decoded response body (\\Segwitz\\BasicShopifyAPI\\ResponseAccess instance)
- `errors` if any errors are detected, true/false
- `exception` if errors are true, exception object is available
- `status` the HTTP status code
- `link` an array of previous/next pagination values, if available

```
$promise = $api->restAsync(...);
$promise->then(function (array $result) {
  // `response` and `body`, etc are available in `$result`.
});
```

##### Overriding request type

[](#overriding-request-type)

If you require the need to force a query string for example on a non-GET endpoint, you can specify the type as a key.

```
$api->rest('PUT', '/admin/themes/12345/assets.json', ['query' => [...]]);
```

Valid keys are `query` and `json`.

##### Passing additional request options

[](#passing-additional-request-options)

If you'd like to pass additional request options to the Guzzle client created, pass them as the second argument of the constructor.

```
// Create options for the API
$options = new Options();
$options->setVersion('2020-01');
// ...
$options->setGuzzleOptions(['connect_timeout' => 3.0]);

// Create the client
$api = new BasicShopifyApi($options);
```

#### GraphQL

[](#graphql-1)

Requests are made using Guzzle.

```
$api->graph(string $query, array $variables = []);
```

- `query` refers to the full GraphQL query
- `variables` refers to the variables used for the query (if any)

The return value for the request will be an object containing:

- `response` the full Guzzle response object
- `body` the JSON decoded response body (\\Segwitz\\BasicShopifyAPI\\ResponseAccess instance)
- `errors` if there was errors or not, will return the errors if any are found
- `status` the HTTP status code

Example query:

```
$result = $api->graph('{ shop { product(first: 1) { edges { node { handle, id } } } } }');
echo $result['body']['shop']['products']['edges'][0]['node']['handle']; // test-product
// or echo $result['body']->shop->products->edges[0]->node->handle;
```

Example mutation:

```
$result = $api->graph(
    'mutation collectionCreate($input: CollectionInput!) { collectionCreate(input: $input) { userErrors { field message } collection { id } } }',
    ['input' => ['title' => 'Test Collection']]
);
echo $result['body']['collectionCreate']['collection']['id']; // gid://shopify/Collection/63171592234
// or echo $result['body']->collectionCreate->collection->id;
```

### API Versioning

[](#api-versioning)

This library supports [versioning the requests](https://www.shopify.com/partners/blog/api-versioning-at-shopify), example:

```
// Create options for the API
$options = new Options();
$options->setVersion('2020-01'); // YYYY-MM or "unstable" is accepted

// Create the client
$api = new BasicShopifyAPI($options);
```

You can override the versioning at anytime for specific API requests, example:

```
// Create options for the API
$options = new Options();
$options->setVersion('2020-01');

// Create the client
$api = new BasicShopifyAPI($options);
$api->rest('GET', '/admin/api/unstable/shop.json'); // Will ignore "2020-01" version and use "unstable" for this request
```

### Rate limiting

[](#rate-limiting)

This library comes with a built-in basic rate limiter which utilizes `usleep` between applicable calls.

- For REST: it ensures you do not request more than the default of 2 calls per second.
- For GraphQL: it ensures you do not use more than the default of 50 points per second.

To adjust the default limits, use the option class' `setRestLimit` and `setGraphLimit`.

#### Custom rate limiting

[](#custom-rate-limiting)

You simply need to disable the built-in rate limiter and push in a custom Guzzle middleware. Example:

```
$options = new Options();
// ...
$options->disableRateLimiting();

// ...
$api = new BasicShopifyAPI($options);
$api->addMiddleware(new CustomRateLimiter($api), 'rate:limiting');
```

### page\_info / pagination support

[](#page_info--pagination-support)

2019-07 API version introduced a new `Link` header which is used for pagination ([explained here](https://help.shopify.com/en/api/guides/paginated-rest-results)).

If an endpoint supports page\_info, you can use `$response->link` to grab the page\_info value to pass in your next request.

Example:

```
$response = $api->rest('GET', '/admin/products.json', ['limit' => 5]);
$link = $response['link']['next']; // eyJsYXN0X2lkIjo0MDkw
$link2 = $response['link']['previous']; // dkUIsk00wlskWKl
$response = $api->rest('GET', '/admin/products.json', ['limit' => 5, 'page_info' => $link]);
```

### Isolated API calls

[](#isolated-api-calls)

You can initialize the API once and use it for multiple shops. Each instance will be contained to not pollute the others. This is useful for something like background job processing.

```
$api->withSession(Session $newSession, Closure $closure);
```

`$this` will be binded to the closure. Example:

```
$api->withSession(new Session('someshop.myshopify.com', 'some-token'), function (): void {
  $request = $this->rest('GET', '/admin/shop.json');
  echo $request['body']['shop']['name']; // Some Shop
});

// $api->rest/graph will not be affected by the above code, it will use previously defined session
```

### Retries

[](#retries)

This library utilizes `caseyamcl/guzzle_retry_middleware` middleware package.

By default, `429`, '500`and`503` errors will be retried twice.

For REST calls, it will utilize Shopify's `X-Retry-After` header to wait *x* seconds before retrying the call.

When all retries are exhasted, the standard response from the library will return where you can handle the error.

To change the status codes watched or the maximum number of retries, use the option class' `setGuzzleOptions`:

```
// Create options for the API
$options = new Options();
$options->setVersion('2020-01');
// ...
$options->setGuzzleOptions(
  'max_retry_attempts' => 3, // Was 2
  'retry_on_status'    => [429, 503, 400], // Was 439, 503, 500
);

// Create the client
$api = new BasicShopifyApi($options);
```

### Errors

[](#errors)

This library internally catches only 400-500 status range errors through Guzzle. You're able to check for an error of this type and get its response status code and body.

```
$call = $api->rest('GET', '/admin/non-existant-route-or-object.json');

if ($call['errors']) {
  echo "Oops! {$call['status']} error";
  var_dump($call['body']);

  // Original exception can be accessed via `$call['exception']`
  // Example, if response body was `{"error": "Not found"}`...
  /// then: `$call['body']` would return "Not Found"
}
```

### Middleware

[](#middleware)

This library takes advantage of using Guzzle middleware for request/response checks and modifications. You're also able to inject middleware.

```
$api->addMiddleware([callable]);
```

See Guzzle's documentation on middleware. As well, you can browse this library's middleware for examples.

### Storage

[](#storage)

For storing the current request times, API limits, request costs, etc. A basic in-memory array store is used `Segwitz\BasicShopifyAPI\Store\Memory`.

If you would like to implement a more advananced store such as one with Redis, simply implement `Segwitz\BasicShopifyAPI\Contracts\StateStorage` and set the client to use it, example:

```
$timeStore = new RedisStore();
$limitStore = new RedisStore();

$api = new BasicShopifyAPI($options, $timeStore, $limitStore);
```

Documentation
-------------

[](#documentation)

Code documentation is [available here](https://github.com/SegWitz/Basic-Shopify-API) from phpDocumentor via `phpdoc -d src -t doc`.

LICENSE
-------

[](#license)

This project is released under the MIT [license](https://github.com/SegWitz/Basic-Shopify-API/blob/master/LICENSE).

Misc
----

[](#misc)

Using Python? [Check out basic\_shopify\_api](https://github.com/SegWitz/Basic-Shopify-API).

###  Health Score

30

—

LowBetter than 64% of packages

Maintenance20

Infrequent updates — may be unmaintained

Popularity7

Limited adoption so far

Community15

Small or concentrated contributor base

Maturity70

Established project with proven stability

 Bus Factor1

Top contributor holds 76.5% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~44 days

Recently: every ~62 days

Total

39

Last Release

1532d ago

Major Versions

v5.4.0 → v6.0.02019-10-15

v6.1.3 → v7.0.02020-03-02

v7.0.0 → v8.0.02020-03-17

v8.2.0 → v9.0.02020-05-26

v9.1.4 → v10.0.02020-09-14

PHP version history (4 changes)v1.0.0PHP &gt;=7.0.0

v8.0.0PHP &gt;=7.1.0

v9.0.0PHP &gt;=7.2.0

v10.0.3PHP &gt;=7.3.0

### Community

Maintainers

![](https://www.gravatar.com/avatar/d163d321a2df4a7171095e52e4355d6713242d267d050274a9209a8473117974?d=identicon)[SegWitz](/maintainers/SegWitz)

---

Top Contributors

[![gnikyt](https://avatars.githubusercontent.com/u/2420282?v=4)](https://github.com/gnikyt "gnikyt (130 commits)")[![lucasmichot](https://avatars.githubusercontent.com/u/513603?v=4)](https://github.com/lucasmichot "lucasmichot (20 commits)")[![darrynten](https://avatars.githubusercontent.com/u/3657251?v=4)](https://github.com/darrynten "darrynten (5 commits)")[![roberttolton](https://avatars.githubusercontent.com/u/3510875?v=4)](https://github.com/roberttolton "roberttolton (5 commits)")[![pagameba](https://avatars.githubusercontent.com/u/249841?v=4)](https://github.com/pagameba "pagameba (4 commits)")[![yashveersingh](https://avatars.githubusercontent.com/u/13031606?v=4)](https://github.com/yashveersingh "yashveersingh (4 commits)")[![jaffacake](https://avatars.githubusercontent.com/u/2633927?v=4)](https://github.com/jaffacake "jaffacake (1 commits)")[![jedimdan](https://avatars.githubusercontent.com/u/757825?v=4)](https://github.com/jedimdan "jedimdan (1 commits)")

---

Tags

apirestgraphqlwrappershopify

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Code StylePHP CS Fixer

Type Coverage Yes

### Embed Badge

![Health badge](/badges/segwitz-basic-shopify-api/health.svg)

```
[![Health](https://phpackages.com/badges/segwitz-basic-shopify-api/health.svg)](https://phpackages.com/packages/segwitz-basic-shopify-api)
```

###  Alternatives

[gnikyt/basic-shopify-api

A basic Shopify API wrapper with REST and GraphQL support, powered by Guzzle.

245514.8k5](/packages/gnikyt-basic-shopify-api)[shopify/shopify-api

Shopify API Library for PHP

4634.8M16](/packages/shopify-shopify-api)[zoonman/linkedin-api-php-client

LinkedIn API PHP SDK with OAuth 2.0 &amp; CSRF support. Can be used for social sign in or sharing on LinkedIn. Examples. Documentation.

127704.0k](/packages/zoonman-linkedin-api-php-client)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
