For AI agents: the complete documentation index is available at https://docs.ovhcloud.com/en/llms.txt, the full documentation bundle is available at https://docs.ovhcloud.com/en/llms-full.txt, and this page is available as Markdown at https://docs.ovhcloud.com/en/guides/public-cloud/data-platform/connectors-sources-shopify.md.

Extract Shopify store data with the GraphQL Admin API

View as Markdown

The Shopify connector extracts data from your Shopify store via the GraphQL Admin API: products, customers, orders, inventory, marketing, B2B, payments

Objective

The Shopify connector extracts data from your Shopify store via the GraphQL Admin API: products, customers, orders, inventory, marketing, B2B, payments, content, and more.

40 built-in endpoint types + a custom query escape hatch for arbitrary GraphQL.

The connector returns raw JSON from the Shopify API. The platform automatically flattens nested fields into columns and stores the result in the lakehouse. No manual schema definition needed.

1. Get an Admin API Access Token

You install your own custom app on your store. The connector then uses that app's credentials to read your data, nothing else.

Two paths to get the credentials, both supported:

  1. Go to partners.shopify.com and sign in (free, no card required).
  2. Open the Dev Dashboard, click Create app, name it (e.g. data-connector).
  3. In the new app, go to Versions → Create a new version.
  4. Under Access → Access scopes, paste the recommended scope list below.
  5. Open Request access at the top of the Access section → enable Customer data (and Order data, etc.) → check Data analytics → save. On a development store this is approved instantly.
  6. Publish the version.
  7. Install the app on your store from the Distribution section.
  8. Open Settings of the app → copy Client ID and Client Secret.
  9. In the connector UI, fill Shop, leave Access Token empty, and paste Client ID + Client Secret. The connector will exchange them for an offline token at first run.

Path B: Existing token (shorter, if you already have one)

If you've already obtained an Admin API access token through the legacy "Develop apps" flow (token starts with shpat_) or by running the OAuth client_credentials exchange yourself (shpua_), paste it directly into the Access Token field, leave Client ID / Client Secret empty.

2. Configure Credentials

The connector accepts two interchangeable auth modes:

FieldMode A (direct token)Mode B (OAuth exchange)
Shopmy-store or my-store.myshopify.comsame
Access Tokenshpat_xxx… or shpua_xxx…empty
Client IDemptyhex string from Dev Dashboard
Client Secretemptystarts with shpss_

If both are filled, the direct token takes priority.

Paste this comma-separated list into the Access scopes field of your custom app version (Dev Dashboard → Access → Access scopes):

read_products, read_customers, read_orders, read_draft_orders,
read_inventory, read_locations, read_discounts,
read_shopify_payments_payouts, read_shopify_payments_disputes,
read_shopify_payments_accounts, read_marketing_events,
read_content, read_users, read_shipping, read_metaobjects,
read_metaobject_definitions, read_publications, read_locales,
read_markets, read_files, read_companies, read_assigned_fulfillment_orders,
read_audit_events

If a scope name is rejected, drop it. Shopify renames scopes between API versions, but the connector handles missing scopes gracefully (returns [] with a warning instead of crashing).

For Customer / Order / Draft Order / Abandoned Checkout data you also need to enable the Protected Customer Data approval (see Path A, step 5). Without it, those endpoints return rows but with PII fields blanked out.

4. Add a Shopify source on Data Platform

  1. In the Data Platform Connectors, find Shopify in the source store and click Select.
  2. Fill in the connection fields using either Mode A (direct token) or Mode B (Client ID + Client Secret) from step 2.
  3. Click Connect. With Mode B, the connector exchanges the Client ID/Secret for an offline access token at this step.
  4. Click Add an Endpoint, choose an endpoint type from the dropdown (e.g. products, orders, custom_query), then fill in any required parameters (e.g. segment_id, query_filter, max_items).
  5. Repeat step 4 for every additional endpoint you want to ingest in this source. Each endpoint becomes a separate table in the lakehouse.
  6. Name the source and click Create.
Warning

The technical name cannot be changed after the source is created. It is used when opening the source via the Data Platform SDK.

5. Available Endpoint Types

Core commerce (14)

EndpointDescriptionRequired parameters
productsCatalog products
product_variantsAll variants across all products
collectionsManual + smart collections
customersCustomer records
customer_segment_membersMembers of a specific segmentsegment_id
ordersOrders (filterable)optional query_filter
draft_ordersPending unconverted orders
abandoned_checkoutsFunnel data
fulfillment_ordersLogistics queue
tender_transactionsPayment-type breakdown
locationsStores / warehouses
inventory_itemsSKU + cost master data
segmentsCustomer segment definitions
companiesB2B companies

B2B & markets (4)

EndpointDescriptionRequired parameters
company_locationsB2B shipping addresses
price_listsB2B / wholesale pricing
catalogsB2B catalog assignments
marketsMulti-region configuration

Promotions & loyalty (4)

EndpointDescriptionRequired parameters
discount_nodesAll discounts (automatic + code + manual)
code_discount_nodesCode discounts only
gift_cardsGift cards (Shopify Plus only)
selling_plan_groupsSubscription plans

Marketing & Shopify Payments (5)

EndpointDescriptionRequired parameters
marketing_eventsCampaign tracking
publicationsSales-channel publications
payoutsShopify Payments payouts
disputesShopify Payments disputes
balance_transactionsShopify Payments fine-grained ledger

Metadata & custom data (3)

EndpointDescriptionRequired parameters
metaobjectsCustom data instancesmetaobject_type
metaobject_definitionsMetaobject schemas
metafield_definitionsMetafield schemas (per owner type)owner_type (PRODUCT, CUSTOMER, ORDER, ...)

Content (5)

EndpointDescriptionRequired parameters
articlesBlog posts
blogsBlog containers
pagesOnline store static pages
url_redirectsURL redirect rules
filesUploaded media assets

Admin & operations (4)

EndpointDescriptionRequired parameters
eventsShop audit log
staff_membersStore team
delivery_profilesShipping zones & rates
fulfillment_services3PL integrations

Single object (1)

EndpointDescriptionRequired parameters
shopStore-level settings (returns one record)

Custom GraphQL (1)

EndpointDescriptionRequired parameters
custom_queryRun any GraphQL query you writequery (your GraphQL), optional variables, optional connection_path

For custom_query, your GraphQL must declare $first: Int! and $after: String as variables and paginate a connection with pageInfo { hasNextPage endCursor }. The connector injects cursors automatically.

6. Pagination & max_items

Shopify's GraphQL API never returns all results in one shot. Every list is paginated. The connector handles pagination automatically; the only knob you control is the max_items field in the UI.

The max_items field

Every endpoint (built-in and custom_query) exposes a Max Items field. It tells the connector "stop after collecting N records, even if more are available."

What you wantSet Max Items toWhat happens
All recordsemptyConnector loops until Shopify says hasNextPage: false. Could be 1 request (small store) or 1000+ requests (large store).
Just the first record1One request with page size 1. Stops immediately.
The first 5 records5One request with page size 5. Stops.
The first 100 records100One request with page size 100. Stops.
The first 250 records250One request with page size 250 (the Shopify max). Stops.
The first 1000 records1000Four requests of 250 each.
50 records to test, then everything50 first, then re-run with emptyCommon pattern while iterating.

How it works under the hood

The connector picks the page size dynamically:

page_size = min(100, max_items - already_collected)   # if max_items is set
page_size = 100                                        # if max_items is empty

Each request asks Shopify for that many records. Once max_items is reached or hasNextPage: false, the loop stops.

So max_items: 5 triggers exactly one HTTP request (first=5, after=null). No pagination loop, no second call. You don't need to write any pagination logic yourself.

"Just give me the first page"

There's no explicit "first page only" toggle. But because Shopify caps page size at 250, setting max_items to anything ≤ 250 guarantees a single HTTP request. If you want "the natural Shopify page size" (50–100 by default), just set max_items: 50 or 100.

Applies to every endpoint

This works the same way for:

  • The 40 built-in endpoints (products, orders, customers, ...).
  • The custom_query endpoint: the connector still injects first and after into your query and respects max_items.

You never write first: 5 in your GraphQL query yourself. You write first: $first and let the connector inject the right value based on max_items.

7. Custom Query: Examples

The custom_query endpoint takes any GraphQL you write against the Admin API. Three rules apply:

  1. Declare $first: Int! and $after: String in the query variables.
  2. Paginate one connection with pageInfo { hasNextPage endCursor }.
  3. Either set the Connection Path field (e.g. products) or let the connector auto-detect the connection.

Here are concrete templates for common cases. Paste any of them in the GraphQL Query field, fill the Variables field if needed, and run.

Example 1: Bare-bones products (just a few fields)

Useful when you only need IDs + titles for downstream joins, instead of the 20-field default products endpoint.

query ($first: Int!, $after: String) {
  products(first: $first, after: $after) {
    edges {
      node {
        id
        title
        handle
        vendor
        createdAt
      }
    }
    pageInfo { hasNextPage endCursor }
  }
}
  • Variables: leave empty
  • Connection Path: products (or leave empty for auto-detect)

Example 2: Orders with a date filter

Replicates the built-in orders endpoint but lets you adjust both the filter and the field selection. Pass the filter via Variables.

query ($first: Int!, $after: String, $query: String) {
  orders(first: $first, after: $after, query: $query, sortKey: CREATED_AT) {
    edges {
      node {
        id
        name
        createdAt
        displayFinancialStatus
        totalPriceSet { shopMoney { amount currencyCode } }
        customer { id email }
      }
    }
    pageInfo { hasNextPage endCursor }
  }
}
  • Variables: {"query": "created_at:>=2026-04-01 AND financial_status:paid"}
  • Connection Path: orders

The full search syntax is documented at shopify.dev/docs/api/usage/search-syntax.

Example 3: Products with their variants and metafields

Cases where the default products endpoint isn't enough. This pulls each product's variants inline and a few specific metafields.

query ($first: Int!, $after: String) {
  products(first: $first, after: $after) {
    edges {
      node {
        id
        title
        variants(first: 50) {
          edges {
            node { id sku price inventoryQuantity }
          }
        }
        metafields(first: 10, namespace: "custom") {
          edges {
            node { key value type }
          }
        }
      }
    }
    pageInfo { hasNextPage endCursor }
  }
}
  • Variables: leave empty
  • Connection Path: products
Warning

Each nested first: adds to the query cost. On a Standard plan (100-pt budget per query), products(first: $first) with variants(first: 50) and metafields(first: 10) consumes roughly first × (50 + 10 + 1) points. Keep the parent page size small (e.g. max_items: 20) for this kind of nested query.

Example 4: Order line items (a sub-resource not exposed as a top-level endpoint)

Each order's line items aren't a built-in endpoint. The only way to get them is via a custom query.

query ($first: Int!, $after: String) {
  orders(first: $first, after: $after, query: "status:any", sortKey: CREATED_AT) {
    edges {
      node {
        id
        name
        lineItems(first: 50) {
          edges {
            node {
              id
              title
              quantity
              originalUnitPriceSet { shopMoney { amount currencyCode } }
              variant { id sku }
            }
          }
        }
      }
    }
    pageInfo { hasNextPage endCursor }
  }
}
  • Variables: leave empty
  • Connection Path: orders

Example 5: Nested connection (using Connection Path)

When the connection isn't at the root, e.g. the orders of a specific Shopify Payments balance transaction. Set Connection Path explicitly.

query ($first: Int!, $after: String) {
  shopifyPaymentsAccount {
    payouts(first: $first, after: $after) {
      edges {
        node {
          id
          status
          issuedAt
          summary {
            chargesGross { amount currencyCode }
            refundsFee { amount currencyCode }
          }
        }
      }
      pageInfo { hasNextPage endCursor }
    }
  }
}
  • Variables: leave empty
  • Connection Path: shopifyPaymentsAccount.payouts (required, auto-detect would still work here, but explicit is clearer when there are multiple connections at different depths)

Tips

  • Test in the GraphiQL Explorer first. Shopify ships an in-store GraphiQL at https://{shop}.myshopify.com/admin/api/explorer. Build and validate your query there before pasting it in the connector. The auto-completion and schema docs make iteration much faster.
  • Field discovery. The full schema is documented at shopify.dev/docs/api/admin-graphql/latest. Every type page lists the available fields and their cost.
  • Two places, two roles. Your query string MUST declare $first: Int! and $after: String in its variable list. That's the GraphQL declaration so Shopify knows to expect them. The connector then injects their values at runtime, page after page. The Variables UI field is for the values of additional variables your query declares (e.g. $query, $ownerType, $segmentId), never put first or after there, the connector overwrites those.
  • max_items applies just like for built-in endpoints: see section 6.

8. Quick Example: Extract Products

  1. Endpoint type: products
  2. Max Items: 1000 (or empty to extract everything)
  3. Run the table extraction.

The connector returns raw JSON. The platform automatically flattens nested fields (e.g. featuredImage.url becomes a column featuredimage_url).

For a focused query: e.g. only orders from the last 30 days:

  1. Endpoint type: orders
  2. Query filter: created_at:>=2026-04-01
  3. Max Items: empty

The query_filter field accepts the Shopify search syntax.

9. Best Practices

Normalize the shop name once

The connector accepts my-store, my-store.myshopify.com, or https://my-store.myshopify.com/. All three resolve to the same host. Pick whichever is most readable.

Use custom_query for narrow extractions

The default queries return ~10–20 fields per record. If you only need a handful of fields and have millions of records, write a custom_query with just those fields, query cost is roughly proportional to the field count.

Refresh tokens via Client ID + Secret

If you set up Mode B (Client ID + Client Secret), the connector re-runs the OAuth exchange at every job start. This means a leaked / rotated token can be replaced by re-issuing the secret in the Dev Dashboard, without touching the connector configuration.

Default query_filter for orders

The orders endpoint uses status:any by default to include all orders (open, closed, cancelled). If you want only open orders, set query_filter: status:open.

10. Plan & Scope Gating

Some endpoints are gated by plan or scope:

EndpointGating
gift_cardsShopify Plus only
payouts, disputes, balance_transactionsShopify Payments must be enabled on the store
companies, company_locations, catalogs, price_listsB2B must be enabled
customers, orders, draft_orders, abandoned_checkouts, fulfillment_orders, tender_transactionsProtected Customer Data approval (see Path A step 5)

When a scope or plan is missing, the connector logs a warning and returns an empty list for that endpoint, your other tables keep working.

Info

For detailed technical information (auth internals, full endpoint reference, pagination, rate limits, output format, limitations), see the Shopify Technical Reference.

Go further

If you need training or technical assistance to implement our solutions, contact your sales representative or click on this link to get a quote and ask our Professional Services experts for a custom analysis of your project.

Ask questions, give your feedback and interact directly with the team building the Data Platform on the dedicated Discord channel.

If you need support with your OVHcloud services, create a request in our Help Centre.

Join our community of users.

Was this page helpful?