Pagination between Variants and SellingPlanAllocations not working in Storefront API 2025-10

Topic summary

Issue: Cursor-based pagination fails for sellingPlanAllocations on ProductVariant via Storefront API when using nodes/node queries. After supplying endCursor to after, the second request returns the same first page (3 of 5 allocations), indicating the cursor is ignored.

What works: Pagination via nodes for products → variants behaves correctly. This suggests the problem is specific to variants → sellingPlanAllocations rather than nodes universally.

Proposed workarounds:

  • Use product(id) → variant(id) → sellingPlanAllocations with per-variant after cursors; for multiple variants, use query aliasing/batching. Reporter notes this is hard to scale across 250 variants across products.
  • Hybrid: gather variant IDs, then query each variant via node(id) with aliases. Reporter confirms this still exhibits the same pagination fault on sellingPlanAllocations.
  • Bulk Operations API to fetch everything at once. Reporter notes this is Admin API only and sellingPlanAllocations isn’t exposed there, so it’s not viable.

Status: Unresolved. One participant frames it as a known limitation with nodes on nested connections; reporter counters that products → variants paginates fine but variants → sellingPlanAllocations does not. No confirmed workaround for cross-product, multi-variant pagination was established.

Summarized with AI on December 10. AI used: gpt-5.

I want to fetch the sellingPlanAllocations of multiple variants at once. SellingPlanAllocations can be fetched only through the Storefront API but it does not allow querying productVariants directly. So, one has to query Storefront API with this hierarchy: products → variants → sellingPlanAllocations. This query would involve nested resources and there is no standard way to paginate correctly between the nested resources.

Hence, I tried to use the nodes query supplying the list of variant IDs and fetching sellingPlanAllocations through them. As this would involve only one level of hierarchy (variants → sellingPlanAllocations), I was confident of handling the pagination. However, to my surprise, the pagination did not work in this case.

query GetVariantSellingPlanAllocations {
  nodes(ids: ["gid://shopify/ProductVariant/<variant-id>", ]){
    __typename
    ... on ProductVariant {
      id
      title
      sellingPlanAllocations(after: null, first:3) {
          nodes {
            sellingPlan {
              id
            }
          }
          pageInfo {
            hasNextPage
            endCursor
          }
        }
      }
    }

I tried the above query with a variant that has 5 allocations. The first iteration returned 3 allocations. The hasNextPage value in the pageInfo was true and I used the endCursor from this output and supplied it as the value to the after parameter on the sellingPlanAllocations and executed the query again. It returned the exact same output as the previous query with the same 3 allocations and the same pageInfo again. This basically means the after value supplied in the second query was not honoured by the server.

To eliminate the fact that it is a limitation of the nodes query, I used the same pattern on a query with product and variants and the pagination worked fine with that.

query productsPagination {
  nodes(ids: ["gid://shopify/Product/<product-id>", ]){
    __typename
    ... on Product {
      id
      title
      variants(first:1, after: null) {
        nodes {
          id
          title
        }
        pageInfo {
          hasNextPage
          endCursor
        }
      }
    }
  }
}

Pagination not working with the first query (variants → sellingPlanAllocations) - is it something related to the fact that the variants are not allowed to be queried directly ?

1 Like

Use the product query instead of nodes:

graphql

query GetVariantSellingPlanAllocations($variantId: ID!, $after: String) {
  product(id: "gid://shopify/Product/<product-id>") {
    variant(id: $variantId) {
      id
      title
      sellingPlanAllocations(first: 3, after: $after) {
        nodes {
          sellingPlan {
            id
          }
        }
        pageInfo {
          hasNextPage
          endCursor
        }
      }
    }
  }
}

For multiple variants: Make separate queries per variant or use query batching/aliasing:

graphql

query GetMultipleVariants($after1: String, $after2: String) {
  product(id: "gid://shopify/Product/<product-id>") {
    variant1: variant(id: "gid://shopify/ProductVariant/<id1>") {
      sellingPlanAllocations(first: 3, after: $after1) { ... }
    }
    variant2: variant(id: "gid://shopify/ProductVariant/<id2>") {
      sellingPlanAllocations(first: 3, after: $after2) { ... }
    }
  }
}

The nodes query doesn’t properly handle cursor-based pagination on nested connections—this is a known limitation, not related to variants specifically but to how nodes processes connection arguments.

Hi Mustafa,

Thanks a lot for your response.

Like I my mentioned in my original post, the nodesquery handles pagination correctly if I use it on products → variants (the query shared below).

query productsPagination {
  nodes(ids: ["gid://shopify/Product/<product-id>", ]){
    __typename
    ... on Product {
      id
      title
      variants(first:1, after: null) {
        nodes {
          id
          title
        }
        pageInfo {
          hasNextPage
          endCursor
        }
      }
    }
  }
}

It doesn’t work for variants → sellingPlanAllocations. I was planning to use the variants → sellingPlanAllocations query with variant ids in batches of 250 across different products which would make use of batching/aliasing difficult.

hey @sriram-1 try this one plz

I understand your situation now. You want to use the nodes query to fetch sellingPlanAllocations for variants across multiple products, but pagination isn’t working correctly at the sellingPlanAllocations level.

Here’s the perfect solution using a hybrid approach:

Solution: Two-Step Approach with Bulk Operations

Since nodes query doesn’t handle pagination properly for variants → sellingPlanAllocations, use this approach:

Step 1: Use nodes query to get variant IDs (works fine)

graphql

query getVariantIds {
  nodes(ids: ["gid://shopify/Product/<product-id-1>", "gid://shopify/Product/<product-id-2>"]) {
    __typename
    ... on Product {
      id
      title
      variants(first: 250) {
        nodes {
          id
        }
        pageInfo {
          hasNextPage
          endCursor
        }
      }
    }
  }
}

Step 2: Fetch sellingPlanAllocations using individual variant queries with aliases

graphql

query getSellingPlanAllocations {
  variant1: node(id: "gid://shopify/ProductVariant/<variant-id-1>") {
    ... on ProductVariant {
      id
      sellingPlanAllocations(first: 10) {
        edges {
          node {
            id
            sellingPlan {
              id
              name
            }
          }
        }
        pageInfo {
          hasNextPage
          endCursor
        }
      }
    }
  }
  
  variant2: node(id: "gid://shopify/ProductVariant/<variant-id-2>") {
    ... on ProductVariant {
      id
      sellingPlanAllocations(first: 10) {
        edges {
          node {
            id
            sellingPlan {
              id
              name
            }
          }
        }
        pageInfo {
          hasNextPage
          endCursor
        }
      }
    }
  }
  
  # ... up to 250 aliases (within query complexity limits)
}

Alternative: Use Bulk Operations API (RECOMMENDED)

For large-scale data fetching, Shopify’s Bulk Operations API is ideal:

graphql

mutation {
  bulkOperationRunQuery(
    query: """
    {
      products {
        edges {
          node {
            id
            title
            variants {
              edges {
                node {
                  id
                  title
                  sellingPlanAllocations {
                    edges {
                      node {
                        id
                        sellingPlan {
                          id
                          name
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
    """
  ) {
    bulkOperation {
      id
      status
    }
    userErrors {
      field
      message
    }
  }
}

Check status and download results:

graphql

query {
  currentBulkOperation {
    id
    status
    errorCode
    createdAt
    completedAt
    objectCount
    fileSize
    url
    partialDataUrl
  }
}

Why This Works

  1. Bulk Operations API handles all pagination automatically and returns a JSONL file with all data
  2. No query complexity issues
  3. Perfect for batch processing variants across multiple products
  4. Handles sellingPlanAllocations pagination correctly

Implementation Pseudo-code

javascript

// 1. Trigger bulk operation
const bulkOp = await shopify.graphql(bulkMutation);

// 2. Poll for completion
while (status !== 'COMPLETED') {
  await sleep(1000);
  status = await checkBulkOperationStatus(bulkOp.id);
}

// 3. Download and parse JSONL file
const data = await fetch(bulkOp.url);
const jsonlLines = data.split('\n');
const parsedData = jsonlLines.map(line => JSON.parse(line));

This approach eliminates pagination complexity entirely and is Shopify’s recommended method for large dataset operations.

Thanks for your response, Mustafa.

I tried both the solutions and both of them don’t work for me unfortunately.

Solution 1 - The node query used on individual variants (in Step 2) suffers from the same issue - pagination on sellingPlanAllocations does not work.

Solution 2 - The bulk operations is available only with the Admin API and sellingPlanAllocations is not exposed through the Admin API.

Thanks for taking time to help me out.