How to Get a Long-Lived Shopify Admin API Token in 2026

public
10 min read
How to Get a Long-Lived Shopify Admin API Token in 2026

Shopify used to give you an API access token in the dashboard. You opened the app settings, copied a string, pasted it into your tool, done. That's gone.

Now you have to walk through an OAuth flow to get the same token. The official docs assume you're building a real, multi-tenant app and explain it in a way that's correct but exhausting if all you want is a token for a script. This post is the short version, and you don't need to install anything. We'll use Google Colab as our runtime, which is a free Python notebook in your browser. If you can copy and paste, you can do this.

You'll need:

  • A Shopify store you own or admin
  • A Shopify Partner / Dev Dashboard account
  • A Google account, for Colab

That's it. No terminal, no Python install, no pip.

What You'll End Up With

A string that looks like shpat_abc123.... You stick it into whatever tool needs it. It calls the Shopify Admin API on your behalf. Tokens don't expire on a timer. They live until the app is uninstalled or revoked.

You'll get one token per shop per app. If you have five stores, you go through the flow five times. Same setup, just five quick repeats.

Step 1: Create the Custom App

In the Shopify Dev Dashboard, click Create app. Give it a name. Anything is fine, you'll see it in the install screen.

Two things matter on the configuration page:

App URL and Redirect URLs, both set to http://localhost. You're not actually running a server. Shopify will redirect the browser there after install, the page won't load, and that's exactly what you want. The URL bar is where the magic lives.

Uncheck "Embed app in Shopify admin". You don't want this. It'll only confuse the install flow.

Step 2: Pick Your Scopes

Under Access, select the scopes your script needs. Be specific. If you only need to read orders, don't grant write_products "just in case." Shopify treats scopes as semi-permanent: changing them later means reinstalling the app on every shop where it's already installed.

Common reads:

  • read_orders (limited to last 60 days unless you also request read_all_orders)
  • read_products
  • read_customers
  • read_inventory
  • read_analytics
  • read_reports

For write access, prefix with write_. Check the Shopify scope reference when in doubt.

Click Release to publish the config. This is the part people miss. Without releasing, the install link won't appear.

Step 3: Grab the Credentials

Open the app's Settings tab. Copy two things and keep them in a scratch doc:

  • Client ID
  • Client Secret (looks like shpss_...)

You'll paste these into Colab in a moment, then they go straight into your password manager.

Step 4: Install the App on Your Store

In the dashboard, click Install app on the app's home tab and pick the store. You'll see a permission screen listing the scopes you configured. Approve it.

You'll be redirected to a URL like:

http://localhost/?hmac=...&host=...&shop=your-store.myshopify.com&timestamp=...

That's a Shopify admin redirect. It does not contain the code parameter you need. If you stop here, you can't get a token.

This is the gotcha that wastes the most time. The install flow from the dashboard doesn't give you an OAuth code. You have to trigger the OAuth flow yourself, by visiting a specific URL in your browser.

Step 5: Trigger the OAuth Flow Manually

In your browser, while logged into the store admin, visit:

https://your-store.myshopify.com/admin/oauth/authorize?client_id=YOUR_CLIENT_ID&redirect_uri=http://localhost

Replace your-store with your store handle (the part before .myshopify.com) and YOUR_CLIENT_ID with the client ID from Step 3.

You'll see the install / permission screen again. Approve it.

Now the redirect URL will look different:

http://localhost/?code=abcd1234efgh5678...&hmac=...&host=...&shop=your-store.myshopify.com&state=...&timestamp=...

There's the code parameter. Copy its value. Do this immediately, the code expires in a few minutes.

A side note. Older Shopify docs and a lot of blog posts include a &scope=... query parameter in the authorize URL. For custom apps where the scopes are configured in the dashboard, you don't need it. The app already knows what it wants.

Step 6: Exchange the Code for a Token (in Colab)

Open colab.research.google.com and click New Notebook. You'll get an empty page with one code cell.

Paste this in, fill in the four values at the top, and click the play button on the left of the cell.

# Fill these in:
STORE = "your-store"               # Just the handle, no ".myshopify.com"
CLIENT_ID = "your_client_id"
CLIENT_SECRET = "shpss_your_secret"
CODE = "code_from_step_5"

# --- you don't need to edit anything below ---
import urllib.parse, urllib.request, json

url = f"https://{STORE}.myshopify.com/admin/oauth/access_token"
data = urllib.parse.urlencode({
    "client_id": CLIENT_ID,
    "client_secret": CLIENT_SECRET,
    "code": CODE,
}).encode("utf-8")

req = urllib.request.Request(url, data=data, method="POST")

try:
    with urllib.request.urlopen(req) as resp:
        result = json.loads(resp.read().decode("utf-8"))
    print("Access token:", result["access_token"])
    print("Scopes:      ", result.get("scope", ""))
except urllib.error.HTTPError as e:
    print(f"FAILED ({e.code}):", e.read().decode("utf-8", errors="replace"))

If it worked, the output looks like:

Access token: shpat_abc123...
Scopes:       read_orders,read_products,...

That's your long-lived token. Copy it somewhere safe.

If you see invalid_request, the code probably expired. Go back to Step 5 to get a fresh one, paste it into CODE, and run the cell again.

Step 7: Verify the Token Works

Add a new cell in Colab (click + Code under the previous cell) and paste this:

# Fill these in:
STORE = "your-store"
TOKEN = "shpat_your_token_from_step_6"

# --- nothing to edit below ---
import urllib.request, json

url = f"https://{STORE}.myshopify.com/admin/api/2025-01/shop.json"
req = urllib.request.Request(url, headers={
    "X-Shopify-Access-Token": TOKEN,
    "Accept": "application/json",
})

try:
    with urllib.request.urlopen(req) as resp:
        shop = json.loads(resp.read())["shop"]
    print("Token works.")
    print("  name:    ", shop["name"])
    print("  domain:  ", shop["myshopify_domain"])
    print("  currency:", shop["currency"])
    print("  plan:    ", shop.get("plan_display_name"))
except urllib.error.HTTPError as e:
    print(f"FAILED ({e.code}):", e.read().decode("utf-8", errors="replace"))

Click play. If the token is good, you'll see your shop's name and details. If it's bound to a different shop or the string got mangled, you'll get a 401.

/shop.json is open to any valid token regardless of scopes, which is what makes it a useful canary. Other endpoints will respect whatever you configured in Step 2.

Confirming the Granted Scopes

If you ever want to check what scopes a token actually has, drop this into a new cell:

STORE = "your-store"
TOKEN = "shpat_your_token"

import urllib.request, json

url = f"https://{STORE}.myshopify.com/admin/oauth/access_scopes.json"
req = urllib.request.Request(url, headers={
    "X-Shopify-Access-Token": TOKEN,
    "Accept": "application/json",
})
with urllib.request.urlopen(req) as resp:
    scopes = [s["handle"] for s in json.loads(resp.read())["access_scopes"]]

print(f"{len(scopes)} scopes:")
for s in sorted(scopes):
    print(" ", s)

Useful after you reinstall to confirm a new scope was actually picked up.

Multiple Stores: Same App, Different Tokens

A single custom app can be installed on multiple stores. Each install issues a separate access token. The client ID and secret stay the same, only the store handle changes in the authorize URL and the exchange cell.

If you have five regional stores, you go through Step 4 through 6 five times. Same Colab cell, different STORE and CODE values each time. You end up with something like:

SHOPIFY_TOKEN_DE = "shpat_aaaa..."
SHOPIFY_TOKEN_FR = "shpat_bbbb..."
SHOPIFY_TOKEN_UK = "shpat_cccc..."
SHOPIFY_TOKEN_PL = "shpat_dddd..."
SHOPIFY_TOKEN_US = "shpat_eeee..."

Stick those in whatever tool you're using.

Changing Scopes Later

If you realize you need an extra scope, edit the app's scope list in the dashboard, click Release again, then reinstall the app on each shop. The reinstall walks through the same OAuth flow you already know.

Often Shopify will return the same token string with the new scopes attached, sometimes a new one. Either way, the old set of permissions is replaced by the new one.

There's no "incremental scope grant" the way OAuth has on some platforms. You install fresh with the full new set every time.

Gotchas Worth Remembering

The install button alone doesn't give you a code. The dashboard's install flow redirects without ?code=. You have to visit /admin/oauth/authorize in the browser yourself. This trips up everyone the first time.

Codes expire in minutes. If your exchange request comes back with invalid_request, the code probably timed out. Redo Step 5, run the exchange immediately.

Tokens are per-shop. A token for store-a returns 401 if you call store-b with it. There's no "global" token.

Treat the client secret like a password. Don't commit it. Don't paste it into Slack. If it leaks, rotate it in the app settings, which forces you to redo the OAuth flow on every shop. It's annoying enough that you'll be careful next time.

Don't share Colab notebooks with credentials still in them. Once you've copied the token out, delete the values from the cells or close the notebook without saving. Colab notebooks are easy to share by accident.

API version matters. Shopify versions its API quarterly. The examples above use 2025-01. Always pin a version in the URL. Unversioned calls might silently change behavior on you.

read_orders only sees 60 days of history. If you need older orders, also request read_all_orders. Shopify will ask you to justify it.

Storefront API tokens are different. This whole post is about Admin API tokens (shpat_...). Storefront access tokens (shpsat_...) are a separate beast, configured under a different part of the app settings, used by frontend code, and intentionally far more limited. Don't confuse the two.

Storing the Token

Once you have it, treat it like any other secret. Whatever your team uses for passwords and API keys, put it there. 1Password, Bitwarden, Vault, AWS Secrets Manager, Doppler.

Never hardcode it. Never log it. The token grants every permission you configured, on the shop it was issued for, for as long as the app is installed. That can be a lot of access. Treat it accordingly.

Bonus: A Claude Code Skill That Does All This For You

If you're already using Claude Code, you can skip the notebook entirely. Drop the file below into your project at .claude/skills/get-shopify-token/SKILL.md, and from then on you can just say "get me a shopify token for your-store" and Claude walks you through the rest. It generates the authorize URL, waits for you to paste the code, runs the exchange, and verifies the token, all without you opening Colab or touching Python.

Save this as .claude/skills/get-shopify-token/SKILL.md in your project:

---
name: get-shopify-token
description: Guide the user through getting a long-lived Shopify Admin API access token via OAuth. Use when the user asks for a Shopify token, Shopify API key, Shopify access token, or wants to install a custom Shopify app and grab its token. Generates the OAuth authorize URL, exchanges the code for a token, and verifies it works.
---

# Get a Long-Lived Shopify Admin API Token

When invoked, walk the user through the Shopify OAuth flow to retrieve a permanent Admin API access token (`shpat_...`).

## What you need from the user

1. **Store handle** — the part before `.myshopify.com` (e.g. `acme-store`).
2. **Client ID** — from the custom app's Settings tab in the Dev Dashboard.
3. **Client Secret** — same place, looks like `shpss_...`.

If they don't have a custom app yet, point them to https://partners.shopify.com → Create app, then set App URL and Redirect URL to `http://localhost`, uncheck "Embed app in Shopify admin", configure scopes under Access, click Release, and copy the credentials from the Settings tab.

## Step 1: Generate the authorize URL

```
https://{store}.myshopify.com/admin/oauth/authorize?client_id={client_id}&redirect_uri=http://localhost
```

Important:
- Do NOT include a `scope` parameter. Custom apps have scopes pre-configured.
- Use `http://localhost`, not `https://`.

Tell the user to visit the URL while logged into the store admin, approve the install, then copy the `code=` value from the redirect URL. Codes expire in minutes, so paste it back quickly.

## Step 2: Exchange the code for a token

```python
import urllib.parse, urllib.request, json

STORE = "..."
CLIENT_ID = "..."
CLIENT_SECRET = "..."
CODE = "..."

url = f"https://{STORE}.myshopify.com/admin/oauth/access_token"
data = urllib.parse.urlencode({
    "client_id": CLIENT_ID,
    "client_secret": CLIENT_SECRET,
    "code": CODE,
}).encode("utf-8")
req = urllib.request.Request(url, data=data, method="POST")
try:
    with urllib.request.urlopen(req) as resp:
        result = json.loads(resp.read().decode("utf-8"))
    print("access_token:", result["access_token"])
    print("scopes:      ", result.get("scope", ""))
except urllib.error.HTTPError as e:
    print(f"FAILED ({e.code}):", e.read().decode("utf-8", errors="replace"))
```

If you get `HTTP 400 invalid_request`, the code expired. Have the user repeat Step 1.

## Step 3: Verify the token

```python
import urllib.request, json

STORE = "..."
TOKEN = "..."

req = urllib.request.Request(
    f"https://{STORE}.myshopify.com/admin/api/2025-01/shop.json",
    headers={"X-Shopify-Access-Token": TOKEN, "Accept": "application/json"},
)
with urllib.request.urlopen(req) as resp:
    shop = json.loads(resp.read())["shop"]
print("Token works. Shop:", shop["name"], "|", shop["currency"])
```

## Common errors

- **HTTP 400 invalid_request** → authorization code expired. Generate a fresh authorize URL.
- **HTTP 401** → token is bound to a different shop, or got copy-pasted with whitespace.
- **HTTP 403 on an endpoint** → missing scope. User needs to add it in the app config, click Release, and reinstall.

## Multi-shop

Same `client_id` and `client_secret`, different store handle. Loop Steps 1–3 once per shop. Don't chain code exchanges in one shell command — codes are single-use.

## Security

Don't log the client secret. Suggest storing the token in a gitignored `.env` or a password manager.

Once it's saved, restart Claude Code (or just open a new conversation in the project) and the skill is live. Test it with something like:

get me a shopify token for acme-store, client id xxx, secret shpss_yyy

Claude will give you the authorize URL, wait for the code, run the exchange in the background, verify the token, and hand it back. The whole flow described in this post collapses into about three replies.

This is the part of LLM tooling I find most satisfying. You write the procedure down once, the agent runs it forever. Tutorials become executable.

Wrap Up

The whole flow, once you've done it once, takes about a minute per store. Create the app, configure scopes, install, visit the authorize URL, copy the code, run the Colab cell, paste the token wherever it needs to go.

The reason this looks complicated in the docs is that Shopify is teaching you OAuth, which is the right answer if you're building a public app for thousands of merchants. For one token against your own store, almost all of that machinery is overhead. The two notebook cells above are everything you actually need.

Save the notebook, or grab the skill if you're on Claude Code. Next time you need a Shopify token, you'll know exactly where to go.