General Information

Shopfront was built from the ground up to be multi-store compatible and as simple as possible for the user. Because of this, our product (and thus APIs) have some intricacies that may differ from other applications.

Each business is referred to as a Vendor, this Vendor has its own unique URL which contains all of its data (e.g. https://example.onshopfront.com).

Each Vendor contains at least one Outlet (typically a physical location) and each Outlet can contain multiple Registers (but can have zero).

It should also be noted that Vendors typically move around between our server pools due to regional requirements, load balancing and certain server executing certain tasks. Therefore, we recommend avoiding using IP addresses directly or as a method of identifying our servers.

Rate Limiting

We implement a number of different rate limiters that apply to your requests (depending on what URLs are queried).

The first level of rate limiters is based on the URL you are calling, currently these are: 1. GraphQL Complexity Limits 2. Reporting Limits

The second level of rate limiters the general API limit.

The third level is at our firewall level.

Your requests will count towards each of the rate limits that are applicable for the request, not just one rate limiter.

If you exceed the maximum amount of requests for any of the limiters, you will receive an HTTP 429 error.

We'll work back to front to describe the limits which apply to each area.

Firewall Limit

This is the broadest limit and predominantly there to avoid runaway queries in infinite loops from hitting our application server.

The rate limit for this server does vary over time due depending on expected load, and we find that it is nearly never hit by any application.

Under normal load this limits any IP address to 1200 requests in any five-minute rolling period, most applications will scale to multiple servers before hitting this limit.

General API Limit

The general limit works similar to the firewall limit, but is handled on our application servers. It's a fixed limit based on the number of Registers and Outlets the Vendor has over a one-minute period, using the following calculation:

10 + 20 * register count + 30 * outlet count

So if you're requesting data for a single store with one register you'll have the rate limit of 10 + 20 * 1 + 30 * 1 = 50 requests per minute.

If you're requesting data for a Vendor with three stores, with each containing two registers (therefore, six registers overall), you'll have a rate limit of 10 + 20 * 6 + 30 * 3 = 220 requests per minute.

We've also created several headers to assist with determining how many requests you have left.

  • X-RateLimit-Limit: This shows the maximum amount of requests
  • X-RateLimit-Remaining: How many requests you have left in the rolling window
  • Retry-After: The number of seconds until you can retry again
  • X-RateLimit-Reset: The POSIX timestamp when you can start making requests again

GraphQL Limit

If you're using the GraphQL API, you're also limited by how complex your queries can be. You receive a total number of complexity points to use across any request in a 30-second period. When you make a request your complexity points will be reduced by how complex the query is. These points are then refunded periodically across the 30-second window.

The total number of complexity points you have available uses a similar calculation to the General API Limit, with a couple of different values:

5,000 + 1,000 * register count + 2,000 * outlet count

Using the examples from above, if you have a single store with a single register you would have a complexity rate limit of 5,000 + 1,000 * 1 + 2,000 * 1 = 8,000 complexity points and if you have a Vendor with three stores, each containing two registers, you'll have a rate limit of 5,000 + 1,000 * 6 + 2,000 * 3 = 17,000 complexity points.

For the most part, one relationship is worth one complexity point and one field returned is worth one complexity point, so the following query is worth three complexity points:

{
    product(id: "123456") {
        id,
        name,
    }
}

Whilst this may seem like a huge amount of points to use, these points can quickly become exhausted when you request a list or a connection. For example, the following query is worth 151 points:

{
    products(first: 50) {
        edges {
            node {
                id,
                name
            }
        }   
    }
}

This is because the fields edges, id and name are each returned 50 times (50 * 3) and you're requesting the query products (worth one complexity point). The node field is a special field that costs 0 complexity points.

Some other fields or queries may also use up more than one complexity point (for example, generating an order from sales). To assist with working these points out and tracking them, we have included the following headers:

  • X-GraphQL-RateLimit-Consumed: The total amount of complexity points that have been consumed
  • X-GraphQL-RateLimit-Remaining: The total amount of complexity points you have remaining
  • X-GraphQL-RateLimit-Used: The amount of complexity points the current query used

When you have run out of complexity points you'll receive an HTTP 429 error which will contain the following body:

{
    "errors": [
        {
            "message": "Throttled"
        }
    ]
}

When the 429 error is produced the refund of points will not be evident in the X-GraphQL-RateLimit-Consumed or X-GraphQL-RateLimit-Remaining provided back to you. The refunded points accumulate over time and the details will be relayed back to the user once the next successful query occurs.

Reporting Limit

We also have a limit imposed on the number of reports that can be run in any one-minute period through the Reporting API, again based on the number of Outlets & Registers a Vendor has.

The report limit per minute is calculated as follows:

5 + register count + outlet count

Using the examples from above, if you have a single store with a single outlet you can run 5 + 1 + 1 = 7 reports in a one-minute window, if you have three stores each containing two registers you can run 5 + 3 + 6 = 14 reports in a one-minute window.

We've also included the following headers to assist with tracking the number of reports that have been used:

  • X-Reporting-RateLimit-Remaining: The remaining number of reports you are able to run within the one-minute window

Status Codes

We attempt to return HTTP status codes according to RCF 7231. Therefore 200 is success, 404 is resource not found, 403 is forbidden, etc.