ShipHero API v2 Beta

Note: This is currently a private beta release and is only available to ShipHero customers who are provided access. If you want early access while this is in beta, please send an email to with a request for access. Your request will be reviewed.

The Shiphero API allows you to directly consume your data programmatically, allowing you to automate your data processing or enrich and aggregate it with any custom logic or solutions you have implemented.

On this page

Getting Started

The API is open to every ShipHero customer, meaning if you are a brand or a 3PL then you can use it with your existing credentials. With them you will be able to obtain a session token that is required in order to make requests to the public api.

Once you have you token, every request made to the api has to include an  Authentication header with the token like Bearer xxxxxxxxxxxxxx

Note: Api keys are no longer needed for the new api, just using your credentials to get the token is enough to start making requests. If you would like to have a special or unique user for the public api, you can always create one and use it to get the tokens and make requests.


In order to authenticate with the api you will have to provide the user and password to get a token, before start making requests to the api.

curl -X POST -H "Content-Type: application/json" -d '{
"username": "YOUR EMAIL", 
"password": "YOUR PASSWORD"
 }' ""

The response should look something like

{   "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6IlJUQXlOVU13T0Rrd09ETXhSVVZDUXpBNU5rSkVOVVUxUmtNeU1URTRNMEkzTWpnd05ERkdNdyJ9.aktgc3MiOiJodHRwczovL3NoaXBoZXJvLmF1dGgwLmNvbS8iLCJzdWIiOiJhdXRoMHw1YmI3YTI4MjY4YTU2YzRjNTEzMTIxMWIiLCJhdWQiOiJzaGlwaGVyby1wdWJsaWMtYXBpIiwiaWF0IjoxNTU0OTEwODc0LCJleHAiOjE1NTczMzAwNzQsImF6cCI6Im10Y2J3cUkycjYxM0RjT04zOAMRYUhMcVF6UTRka2huIiwic2NvcGUiOiJlbWFpbCBwcm9maWxlIG9mZmxpbmVfYWNjZXNzIiwiZ3R5IjoicGFzc3dvcmQifQ.lW2UalihR5msHKhJzDPvy5SCKxSPyUCMuQ7RXyP2ZNQ2gENjGF2nmdsYlF2CqxH_wITcK10CproQErMK_yAWUSEck8qfC1Fu_UNc9-xW55ALeCk09ZZD--aB_QFjLVM-ooawby7y4Ysf8H4yEBQpoPwZoQ3DQnu5QBNxd5oOLIP2ezzNYvrwjpm-uNN8II5sK9U075Mx1HH31KG14iFt5sEZQmYOz-oSWweVuY6Sd61VFD02sncXOmEZIxu3bdaZSn1JYaM-ilLce4s748iv75BVDgqj1b2A1lyITeqvFoYWl3PKV56fOlfm8v9QnkSqR0iTGENgV6zZq3rPRsBLTw",   "expires_in": 2419200,   "refresh_token": "cBWV3BROyQn_TMxETqr7ALQBaoFgIzkC-8KkJaIq2HmK_",   "scope": "email openid profile offline_access",   "token_type": "Bearer" }

To refresh it

curl -X POST -H "Content-Type: application/json" -d '{
	"refresh_token": "YOUR REFRESH TOKEN"
}' ""


The new api is built with GraphQL making it quite different from previous REST versions, and hopefully you will find this new version a lot more user friendly, faster and easier to use.

In order to request data or make modifications you will always have to hit

One of the greatest benefits of GraphQL is its self-documenting nature, which allows the users of the api to be able to navigate through the schema, queries, mutations and types so you know exactly what can be requested, which parameters to pass and what to expect in return.

There are several apps that can be used to interact with the api and navigate it, the most used ones are:


As in any GraphQL api you will have queries and mutations, and in both of them the result will  always be an object. In GraphQL, queries or even mutations can return any type of object, even Connection Fields (which are the way to have paginated results), but we have made a design decision to make every operation return a BaseResponse. This object allows us to include extra information or metadata to the responses, without polluting the resource types (more on this later).


When executing mutations that will modify your customer's data you have two ways to achieve that.

  1. requesting a token using your customer credentials and making the requests with that token
  2. specify the customer account you are operating on behalf of by using the customer_account_id in each mutation's input.

3PL customers

You can always check your customers list by doing

query {
  account {
    customers {
      edges {
        node {


When making queries there are 2 extra parameters that can be defined  sortand analyze.

Sort will be applicable only when requesting queries that return multiple results (Connection Fields). Here you can specify a comma separated list of attributes to sort the results. The default is to sort by ascending, but you can specify a different criteria on each field, by pre-pending (+ or -). Ex:  sort: "name, -price"

Analyze is a boolean flag and will only compute the complexity of the query, without executing it (For more info check the Rate Limiting section)

The  BaseResponse of a query will always have the following fields:

  • request_id: An unique request identifier
  • complexity: The complexity of the query
  • data: The actual results of the query (a Field, List or Connection Field)

Getting all products

Getting a Product by SKU


For mutations, the same rule applies, but in this case the result is not called  data, its defined on each mutation, so usually if you create a product, you will have a product field in the response.

Creating a product

Rate Limiting

In order to avoid overloading the API and to prevent possible attacks we have implemented a rate limiting based on user quotas.

Each operation performed by the public api has a calculated complexity, representing the cost of executing that particular action. Following this, users are given an amount of credits to be used within one hour. How many operations you can execute will depend on how you build them. The same query can have a different complexity if you decide to fetch a lot of information from the results, or if you navigate across relationships to enrich the results.

Given the dynamic nature of graphql operations it might be hard to imagine the cost of an operation, so here's where the  analyze parameter available in queries becomes relevant. If you send analyze: true the query won't be executed, but instead only the complexity will be calculated. You can then retrieve the cost of that query by accessing the complexity field from the response.

Analyzing query

Throttling error
User Quota

After a few operations you might want to know how many credits you have available and for how long. That's exactly what the  user_quota query will provide you.

Consuming the API

The api can be used from any client that supports GraphQL, here we'll show examples to make some requests in python

Using gql

from gql import gql, Client
from gql.transport.requests import RequestsHTTPTransport

_transport = RequestsHTTPTransport(
_transport.headers = {
  "User-Agent": "Mozilla/5.0 (X11; buntu; " +
  "Linux x86_64; rv:58.0) Gecko/0100101 Firefox/58.0",
  "Authorization": "Bearer {}".format(YOUR_TOKEN),
  "content-type": "application/json",

client = Client(
query = gql("""
  products {
    data(first: 10) {
      edges {
        node {


Using sgqlc

First you need to generate the schema

python3 -m sgqlc.introspection \
  --exclude-deprecated \
  --exclude-description \
  -H "Authorization: Bearer YOUR_TOKEN" \ \

Now you can autogenerate the types

sgqlc-codegen sh_public_api.json

Now start making requests

from sgqlc.operation import Operation
from sgqlc.endpoint.http import HTTPEndpoint
from sh_public_api_schema import sh_public_api_schema as schema

endpoint = HTTPEndpoint(
  base_headers={'Authorization': 'Bearer YOUR_TOKEN'}

# Build the query
op = Operation(schema.Query)

# Building the products query
ps = op.products()

# Make sure to request the complexity and request_id

# Get the first 10 and define the selections
p_data =

# Executing the call
data = endpoint(op)

# Converting results to entities
for p in (op + data).edges:
  print(p.node)  # will return a ProductInfo entity

For more info check the docs

Error Handling & Support

When errors occur, they are reported on the  errors section of the response. If an unexpected error happened or you feel something didn't go as expected, make sure to grab the request_id from the error's detail. You can share it with our customer support and this will help us univocally identify your request along with its original payload.