Shopify API 10 min read

How to Get Shopify API Access: A Step-by-Step Guide (2026)

Connecting to the Shopify Admin API changed twice in 2026, and most guides still describe the removed route. Here is the whole process as it works now — Partner organization, app, scopes, custom distribution, install link, first GraphQL call — plus the two limits that decide what you can actually pull.

Updated Aug 2026
How to Get Shopify API Access: A Step-by-Step Guide (2026)

What You Are Actually Trying to Get #

One thing: an offline access token for one store. It looks like shpat_…, it goes in an X-Shopify-Access-Token header, and it does not expire.

Everything below exists to produce that token legitimately for a store you do not own. If you only ever call your own store, it is much shorter — but almost nobody's situation stays that way.

In one line

Create an app in a Partner organization, select custom distribution, send the merchant the generated install link, and exchange the code Shopify hands back for a non-expiring token.

Two things to unlearn first, because they are what most search results still tell you to do, and both were removed in 2026:

  • The admin custom app is gone. "Settings → Apps → Create an app", then copy a static token out of the admin — that route was removed for new apps on 1 January 2026. Apps created before then still work, which is why the advice persists.
  • REST is legacy. The REST Admin API has been legacy since 1 October 2024, and apps created after 1 April 2025 must use GraphQL. Do not start a REST client in 2026.

Before You Start: What You Need #

Gather these first. Two of them are the usual reasons a setup stalls halfway.

WhatWhy
A Shopify Partner accountFree. An app in a merchant organization cannot install on anyone else's store — see the trap section below.
The store's .myshopify.com handleNot the customer-facing domain. Shopify issues random handles now, so you cannot derive it from the brand name — ask for the exact one.
A public HTTPS callback URLIt must be reachable by Shopify and registered verbatim. Localhost works for testing, but the redirect allowlist must contain the exact string.
The merchant available for one clickThat is genuinely their whole involvement. No password, no code to send back.

Gotcha: the store handle is not derived from the store name. A real store we connected had the brand name in its admin and a handle that read like bfe437-e4.myshopify.com. Guessing wastes an app, because binding is permanent.

The Six Steps #

  1. Create a Partner organization

    At partners.shopify.com. Free, and it takes a couple of minutes. You will be asked for a business address and to accept the Partner Program agreement.

  2. Create an app in that organization's Dev Dashboard

    Use the "Start from Dev Dashboard" option rather than the CLI — the CLI scaffolds an embedded app project, which is not what a server-side data integration needs.

  3. Configure the version, then release it

    This is where the flow is decided. Details in the next section — get these wrong and the install silently behaves differently.

  4. Select a distribution method

    Custom distribution for client work. This choice is permanent per app.

  5. Generate the install link and send it

    Shopify produces a signed install_custom_app URL bound to one store, valid for about seven days. The merchant opens it and clicks Install.

  6. Exchange the code for a token

    Shopify redirects to your App URL; you start OAuth, receive a code at your callback, and exchange it. Store the token encrypted.

The Shopify install consent screen showing an unreviewed-app warning and three permission groups: view customer data, view staff and contributor data, and view store data

That is what the merchant sees at step 5. The "This app hasn't been reviewed" banner is expected for a custom-distribution app and is not an error — custom apps do not go through App Store review. Warn the merchant it will appear, or you will get a nervous message back.

The App Config That Decides Everything #

Four settings on the app version. Two of them change the entire install flow, and neither is obvious from the form.

SettingValueWhy
EmbeddedOffYou are not rendering UI inside Shopify admin. Leaving it on pushes you toward session-token auth designed for embedded apps.
use_legacy_install_flowOnGives you the classic authorization code grant with your own redirect. Off means Shopify-managed installation and token exchange.
App URLA real endpointNot a marketing page. Shopify sends the merchant here mid-install and expects your server to start OAuth.
Redirect URLsYour callback, verbatimAn exact string match. A trailing slash difference is a failed install.

Scopes for read-only order sync — request the minimum, because every extra one shows on the merchant's consent screen:

read_orders,read_products,read_inventory,read_fulfillments,read_customers,read_shipping

Leave read_all_orders out for now. It needs Shopify's approval, and requesting an ungranted scope fails the whole install. Add it once approved.

Mental model: "embedded off + legacy install flow on" is the combination that means "I am a server that wants a token, not an app that renders inside Shopify." Almost every confusing install symptom traces back to those two.

The Trap: Merchant Orgs Can't Install on Client Stores #

The single most expensive mistake, and worth knowing before you start rather than after.

Shopify has two kinds of organization. A merchant organization is created automatically around a store you own. A Partner organization is one you create deliberately. The Dev Dashboard looks nearly identical in both.

An app created in a merchant org can only install on stores in that same org. Build there, test on your own store, everything works — then send a client the link and they get a bare HTTP 500 with no explanation.

The tell is a section that is missing rather than one that errors: in a merchant org there is no Distribution setting anywhere. In a Partner org it appears on the app overview.

Shopify Partner dashboard distribution options: public distribution for the App Store, and custom distribution limited to one store or one Plus organization

Note the wording on the custom option. Installs are limited to one store, unless every store belongs to a single Shopify Plus organization. So a client on Basic with six storefronts needs six apps and six credential pairs — which is an architectural decision, not a chore, because it means the client ID and secret become per-store data rather than deployment config.

The full diagnosis, including the second error you hit after fixing this one, is in why your Shopify custom app won't install on a client's store.

Getting the Token #

Once the merchant clicks Install, Shopify redirects them to your App URL with shop, hmac and timestamp, and waits for you to start OAuth. Verify the signature, mint a single-use state, and redirect to the grant screen:

https://{shop}/admin/oauth/authorize
    ?client_id={client_id}
    &scope={comma_separated_scopes}
    &redirect_uri={your_callback}
    &state={single_use_nonce}

Shopify then calls your callback with a code. Exchange it:

resp = httpx.post(
    f"https://{shop}/admin/oauth/access_token",
    data={
        "client_id": os.environ["SHOPIFY_CLIENT_ID"],
        "client_secret": os.environ["SHOPIFY_CLIENT_SECRET"],
        "code": code,
    },
)
# {"access_token": "shpat_...", "scope": "read_orders,..."}
# note: no expires_in — offline tokens do not expire

Verify the HMAC on both hops. The callback is a public endpoint, so every parameter is attacker-supplied — and validate the shop domain against an anchored pattern, or a forged shop sends your token exchange, carrying the client secret, to someone else's host.

The token has no expiry. It stops working only when the merchant uninstalls the app or you change its scopes, which forces a re-install.

Your First Call #

Everything is one endpoint. Confirm the token works by asking who you are talking to:

POST https://{shop}/admin/api/2026-07/graphql.json
X-Shopify-Access-Token: shpat_...

{ shop { name myshopifyDomain currencyCode ianaTimezone
         plan { displayName shopifyPlus } } }

The plan field is worth pulling on day one: it tells you whether the merchant is on Plus, which is what decides one-app-per-store versus one app for all of them.

Then orders, filtered so an incremental sync also catches status changes rather than only new orders:

{ orders(first: 50, query: "updated_at:>=2026-08-01T00:00:00Z",
         sortKey: UPDATED_AT) {
    pageInfo { hasNextPage endCursor }
    nodes {
      id name createdAt displayFulfillmentStatus
      totalPriceSet { presentmentMoney { amount currencyCode } }
      shippingAddress { name address1 city zip countryCodeV2 phone }
      lineItems(first: 50) { nodes { sku title quantity
        customAttributes { key value } } }
    }
} }

Gotcha: a GraphQL 200 can still be a failure. Auth, scope and validation errors come back as HTTP 200 with an errors array, and so does throttling (THROTTLED). Check errors on every response or you will treat a permission failure as an empty result set.

Rate limits are cost-based, not request-based: a 2000-point bucket refilling at 100/second on Standard, and each response reports its own cost. A 50-order page with nested line items is cheap — we measured 23 points for five orders with line items. Read extensions.cost.throttleStatus and back off from the reported bucket rather than guessing.

The Two Limits That Decide What You Can Pull #

Both are worth knowing before you promise anyone a dashboard.

Orders older than 60 days need approval

The read_orders scope only returns the last 60 days. Anything older requires read_all_orders, which Shopify grants on request with written justification. Until it is approved there is no historical backfill — no historical P&L, no forecast baseline. It is the only part of a Shopify integration with a waiting period, so request it on day one.

Customer PII is tiered

Shopify splits customer data into two levels. Level 1 excludes name, address, phone and email; Level 2 is exactly those fields — everything a shipping label needs. Access depends on app type: partner-created custom apps get both levels, while public apps go through a data-protection review. See Shopify's protected customer data requirements.

On a custom-distribution app we saw full ship-to — name, street, city, postcode, country and phone — return with no extra review step. Verify it yourself against one real order before building a labelling pipeline on the assumption.

One field that is genuinely often empty: the buyer's phone. Shopify only collects it if the merchant enables it at checkout, so it is absent at source rather than withheld, and no scope change produces it. Carriers usually require one, so plan a fallback. The same problem exists on other marketplaces — here is how the others handle it.

How This Changes Your Database #

Three schema decisions worth making before the second store, not after.

Credentials are per store, not per deployment. On a non-Plus client, each store has its own app, so client_id and client_secret belong on the store row. Keep the global setting as a fallback so a single-store deployment still works.

The callback must verify the HMAC with the right secret. One public endpoint receives installs from many apps. Resolve the store from the shop parameter before verifying, then use that store's secret. Verify everything against one global secret and every per-store install is rejected as forged.

Key orders on the numeric ID, scoped by store. Shopify's order.name is the merchant-facing #1001 and it restarts in every store, so a multi-store sync collides on the first order. Parse the numeric ID out of the GID:

# gid://shopify/Order/1111111111111 -> "1111111111111"
def gid_num(gid: str) -> str:
    return gid.rstrip("/").split("/")[-1]

There is no refresh token and no expiry column, which is a pleasant change from every other marketplace. Store the token encrypted and that is the whole credential lifecycle.

Wiring Shopify alongside other channels? How four marketplace APIs differ on authentication covers the token lifetimes side by side, and why order IDs are not unique per seller is the same keying trap on Amazon. Need it built properly? Tell me what you are connecting.

FAQ #

How do I get Shopify API access?

Create a free Shopify Partner account, create an app in that organization's Dev Dashboard, set its scopes and release a version, select custom distribution, and send the merchant the generated install link. When they click Install you exchange the returned code for a non-expiring offline access token. Since 1 January 2026 you can no longer create the old admin custom app with a static token.

Is the Shopify API free?

Yes. There is no charge for a Partner account or for Admin API access, and custom distribution needs no App Store listing or review. Rate limits are cost-based rather than paid tiers, though the allowance rises on Advanced and Plus plans.

Do Shopify access tokens expire?

Offline access tokens do not expire. The exchange returns access_token and scope with no expires_in, so there is no refresh grant to implement. A token stops working only if the merchant uninstalls the app or you change its scopes.

Can one app connect to multiple Shopify stores?

Only if the stores belong to one Shopify Plus organization. With custom distribution on Basic, Grow or Advanced plans, each app binds permanently to a single store, so a multi-store client needs one app and one credential pair per store.

Should I use the REST or GraphQL Shopify API?

GraphQL. The REST Admin API has been legacy since 1 October 2024 and apps created after 1 April 2025 must use GraphQL, which is also the only API receiving new features. The endpoint is /admin/api/{version}/graphql.json, and the version should live in config because Shopify releases quarterly and supports each version for about a year.

Why do I only get 60 days of Shopify orders?

The read_orders scope is limited to the last 60 days. Older history needs the read_all_orders scope, which Shopify grants on request with written justification. Request it early — it is the only part of the process with a waiting period.

Don't want to build this yourself?

I set this up for sellers and agencies every week. Book a free 30-minute audit of your Amazon data & PPC setup — you'll leave with a plan either way, whether we work together or not.

Found this helpful? Share it:

WhatsApp