How to Use a Shopify Access Token
Once you have an Admin API access token, you call Shopify with it in a single header. Below: copy-paste examples in cURL, Node.js, Python, and PHP — both REST and GraphQL.
By Datora Team · Updated
What you need to know first
Three things that trip people up before any code runs:
- The header is
X-Shopify-Access-Token— notAuthorization: Bearer. Bearer headers return 401. - Use the
.myshopify.comdomain. The Admin API only responds at the canonical{shop}.myshopify.comdomain. Custom domains likeexample.comdon’t work for the API, even if they redirect to the storefront. - Pin an API version. URLs include the version like
/admin/api/2026-04/.... Never useunstablein production. New stable versions ship quarterly; pinning protects you from breaking changes.
Don’t commit your token. Read it from an environment variable (SHOPIFY_ACCESS_TOKEN in the examples below). Tokens look like shpat_ followed by a hex string and grant full access for every scope they were issued with.
Copy-paste examples
Replace {shop} / your-store.myshopify.com with your store’s canonical domain and set SHOPIFY_ACCESS_TOKEN in your environment.
cURL
REST: get shop info
curl -X GET \
"https://{shop}.myshopify.com/admin/api/2026-04/shop.json" \
-H "X-Shopify-Access-Token: shpat_xxxxxxxxxxxx" \
-H "Content-Type: application/json"REST: list products
curl -X GET \
"https://{shop}.myshopify.com/admin/api/2026-04/products.json?limit=10" \
-H "X-Shopify-Access-Token: shpat_xxxxxxxxxxxx"GraphQL: query shop
curl -X POST \
"https://{shop}.myshopify.com/admin/api/2026-04/graphql.json" \
-H "X-Shopify-Access-Token: shpat_xxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{"query":"{ shop { name myshopifyDomain plan { displayName } } }"}'Node.js
REST with fetch
// Node 18+ has fetch built-in
const SHOP = "your-store.myshopify.com";
const TOKEN = process.env.SHOPIFY_ACCESS_TOKEN;
const API_VERSION = "2026-04";
const res = await fetch(
`https://${SHOP}/admin/api/${API_VERSION}/products.json?limit=10`,
{
headers: {
"X-Shopify-Access-Token": TOKEN,
"Content-Type": "application/json",
},
}
);
if (!res.ok) {
throw new Error(`Shopify API error ${res.status}: ${await res.text()}`);
}
const { products } = await res.json();
console.log(products);GraphQL with fetch
const SHOP = "your-store.myshopify.com";
const TOKEN = process.env.SHOPIFY_ACCESS_TOKEN;
const API_VERSION = "2026-04";
const query = `
query getProducts($first: Int!) {
products(first: $first) {
edges {
node { id title status totalInventory }
}
pageInfo { hasNextPage endCursor }
}
}
`;
const res = await fetch(
`https://${SHOP}/admin/api/${API_VERSION}/graphql.json`,
{
method: "POST",
headers: {
"X-Shopify-Access-Token": TOKEN,
"Content-Type": "application/json",
},
body: JSON.stringify({ query, variables: { first: 10 } }),
}
);
const { data, errors } = await res.json();
if (errors) throw new Error(JSON.stringify(errors));
console.log(data.products.edges);Python
REST with requests
import os
import requests
SHOP = "your-store.myshopify.com"
TOKEN = os.environ["SHOPIFY_ACCESS_TOKEN"]
API_VERSION = "2026-04"
resp = requests.get(
f"https://{SHOP}/admin/api/{API_VERSION}/products.json",
headers={"X-Shopify-Access-Token": TOKEN},
params={"limit": 10},
timeout=10,
)
resp.raise_for_status()
print(resp.json()["products"])GraphQL with requests
import os
import requests
SHOP = "your-store.myshopify.com"
TOKEN = os.environ["SHOPIFY_ACCESS_TOKEN"]
API_VERSION = "2026-04"
query = """
query getProducts($first: Int!) {
products(first: $first) {
edges { node { id title status totalInventory } }
}
}
"""
resp = requests.post(
f"https://{SHOP}/admin/api/{API_VERSION}/graphql.json",
headers={
"X-Shopify-Access-Token": TOKEN,
"Content-Type": "application/json",
},
json={"query": query, "variables": {"first": 10}},
timeout=10,
)
resp.raise_for_status()
body = resp.json()
if "errors" in body:
raise RuntimeError(body["errors"])
print(body["data"]["products"]["edges"])PHP
REST with cURL
<?php
$shop = 'your-store.myshopify.com';
$token = getenv('SHOPIFY_ACCESS_TOKEN');
$version = '2026-04';
$ch = curl_init("https://{$shop}/admin/api/{$version}/products.json?limit=10");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"X-Shopify-Access-Token: {$token}",
"Content-Type: application/json",
],
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($status >= 400) {
throw new Exception("Shopify API error {$status}: {$body}");
}
$data = json_decode($body, true);
print_r($data['products']);Patterns you’ll need
Pagination
REST: Shopify returns a Link response header containing rel="next" / rel="previous" URLs. Follow the URL as-is — it includes a cursor. Don’t try to construct paging manually.
GraphQL: connections expose pageInfo.hasNextPage and pageInfo.endCursor. Pass the cursor as the after argument on the next query.
Rate limits
REST uses a leaky-bucket per shop. The X-Shopify-Shop-Api-Call-Limit response header shows current/max usage like 5/40. When you hit the limit you get 429 Too Many Requests with a Retry-After header in seconds — respect it.
GraphQL uses a query-cost system. Each response includes extensions.cost with requestedQueryCost, actualQueryCost, and throttleStatus.currentlyAvailable. Watch the bucket and back off when it gets low instead of waiting for a 429.
Error handling
Shopify usually returns useful JSON error bodies. Always log the full body, not just the status code — the message tells you exactly what went wrong (missing scope, invalid shop, etc.).
For GraphQL, a 200 OK can still contain an errors array in the body. Check it before consuming data.
See the Shopify OAuth and access token errors reference for symptoms, causes, and fixes for the most common ones.
Webhooks vs polling
If you find yourself polling for changes — new orders, inventory updates, customer signups — switch to webhooks. Polling burns rate limit budget and runs late. Webhooks are push-based and free against your quota.
Don’t have a token yet?
Generate one in 60 seconds with your own Shopify app credentials. Pick scopes from the reference, approve on Shopify, copy the token. Nothing stored.
Frequently asked questions
Which header should the Shopify access token go on?+
Always send the token on the X-Shopify-Access-Token header. Do not use Authorization: Bearer — that's OAuth-style and Shopify's Admin API will return 401. Format: 'X-Shopify-Access-Token: shpat_xxxxxxxxxxxx'.
What's the URL format for the Shopify Admin API?+
https://{shop}.myshopify.com/admin/api/{version}/{resource}.json for REST, or https://{shop}.myshopify.com/admin/api/{version}/graphql.json for GraphQL. Use the canonical .myshopify.com domain — custom domains don't work for the Admin API. Always pin a version like 2026-04, never use 'unstable' in production.
Should I use REST or GraphQL for the Shopify Admin API?+
Shopify is moving toward GraphQL — newer features land there first and some REST endpoints are being deprecated. For new code, prefer GraphQL. REST is fine for small scripts that read or write a few records. Both accept the same X-Shopify-Access-Token header.
How do I handle Shopify API rate limits?+
REST uses a leaky-bucket model with a per-shop quota; GraphQL uses query cost points. Both return retry information in response headers (X-Shopify-Shop-Api-Call-Limit for REST, extensions.cost in the body for GraphQL). On 429, back off and retry. Don't fire hundreds of requests in parallel — the bucket fills fast.
Do Shopify Admin API access tokens expire?+
Offline tokens (the default for custom apps) don't expire — they stay valid until the merchant uninstalls or you explicitly revoke them. Online tokens (used by embedded apps) do expire, typically within 24 hours. Most non-embedded tools use offline tokens.