Shopify Custom Reports: ShopifyQL, the API and When to Build

How to build custom reports in Shopify Analytics with ShopifyQL, query the same data from an app with shopifyqlQuery, export it, and when a custom report is worth it.

In this guide
  1. Custom reports in Shopify Analytics
  2. How ShopifyQL reads
  3. Querying the same data from an app
  4. Exporting reports
  5. When a custom back-office report is worth building
  6. Have it built
  7. FAQ

Most Shopify reporting questions start the same way: “Can I get a report that shows X by Y, only for Z?” Often the answer is already in your admin. Shopify Analytics lets you build your own reports on every plan, and it uses a query language, ShopifyQL, that you can also run from an app through the GraphQL Admin API.

This guide covers what you can do in Analytics today, how ShopifyQL reads, how an app queries the same data, how to export, and the point where a custom back-office report is worth building.

Custom reports in Shopify Analytics

Shopify’s help center says creating a new data exploration is available to merchants on all Shopify plans. There are two ways to get a report of your own:

Start from a default report. Open any report under Analytics > Reports, change its filters, columns or date range, then choose Save as and name the copy. Edited defaults become custom reports.

Start from a blank exploration.

  1. In the Shopify admin, go to Analytics > Reports and select New exploration.
  2. Write a query in the ShopifyQL query editor, or describe what you want and let Sidekick generate the query.
  3. In the configuration panel, pick an exploration method and add at least one metric, then any dimensions, comparisons and filters.
  4. Choose a visualization if the default does not suit you.
  5. Select Save.

A few limits to know: some reports cannot be filtered, edited or duplicated, and an exploration needs at least one metric before it runs.

How ShopifyQL reads

ShopifyQL is not SQL, though it looks close. A query names a dataset with FROM and the columns with SHOW, then narrows, groups and orders. The ShopifyQL reference lists these clauses:

Clause What it does Example
FROM Picks the dataset (required) FROM sales
SHOW Metrics and columns to return SHOW total_sales
WHERE Filters rows
GROUP BY Breaks a metric down by a dimension GROUP BY sales_channel
TIMESERIES One row per period TIMESERIES day
SINCE / UNTIL Date range SINCE -30d UNTIL today
DURING Named period DURING last_month
COMPARE TO Compares with another period COMPARE TO previous_period
ORDER BY Sorts ORDER BY total_sales DESC
LIMIT Caps rows LIMIT 10
VISUALIZE Picks the metric to chart, with TYPE for chart type
WITH Applies an option WITH LAST_CLICK_ATTRIBUTION

Two queries from Shopify’s docs show the shape:

FROM sales
  SHOW total_sales
  DURING last_month
FROM sales
  SHOW total_sales
  TIMESERIES day
  SINCE -30d UNTIL today

Shopify says the schemas cover sales, orders, customers, marketing, inventory, payments and more. The exact field names differ per dataset, so the quickest way to learn them is to build a report in the editor, look at the query it generates, and adjust from there.

Querying the same data from an app

Since GraphQL Admin API version 2025-10, apps can run ShopifyQL with the shopifyqlQuery query. The developer changelog announced it on October 1, 2025.

What it needs, per Shopify’s ShopifyQL in the GraphQL Admin API guide:

Requirement Detail
Access scope read_reports
Protected customer data Level 2 access (name, address, phone, email fields). Shopify advises requesting it early, since approval can take time
API version 2025-10 or later

The request is one string:

query {
  shopifyqlQuery(query: "FROM sales SHOW total_sales GROUP BY month SINCE -3m ORDER BY month") {
    tableData {
      columns { name dataType displayName }
      rows
    }
    parseErrors
  }
}

The response gives you tableData with typed columns and rows, parseErrors when the query is wrong, and parseWarnings for non-fatal issues such as a deprecated field.

Budgets and errors

Shopify’s errors and limits guide describes a few behaviours to design around:

  • A separate budget. ShopifyQL has its own complexity-based budget on top of the usual GraphQL cost limit, and it is usually the first to run out. Cost goes up with more clauses, more metrics in SHOW and more dimensions in GROUP BY. Over budget, the API returns a 429; wait until windowResetAt before retrying.
  • Response too large. A RESPONSE_TOO_LARGE error is not a rate limit, so retrying will not help. Shorten the date range, drop a GROUP BY dimension or add a smaller LIMIT.
  • Not real time. The current period is still filling in. Shopify suggests caching monthly or all-time totals for hours and daily or hourly views for a few minutes.

What it is for, and what it is not

shopifyqlQuery returns aggregates: totals, trends, rankings. Shopify is explicit that it is not for reading or changing a single order, customer or product; use the normal Admin API objects for that. If your report needs one row per order or per line item with custom fields, that is a different job, closer to an order export than to analytics.

Exporting reports

From the export help page, most reports can be exported as:

Format Good for
CSV Spreadsheets
XML Older import tools
JSON Lines (JSONL) Scripts and data pipelines
Apache Parquet Data warehouses and analytics tools

Open the report, choose the export option from the menu, pick a format if offered and confirm. The file lands in your downloads folder. The legacy Finances reports (Sales, Taxes, Payments) export as CSV with a choice between the current page and the full report.

Exporting by hand is fine for a monthly look. It becomes a chore when someone downloads the same report every Monday, pastes it into a spreadsheet and fixes the same columns. That is the usual signal that the report should be automated.

When a custom back-office report is worth building

Analytics answers “how much, by what, over when” for one store. A custom report earns its place when one of these is true:

Situation Why Analytics falls short What a custom report does
Several stores, one view Each store has its own Analytics; combined analytics across stores is a Shopify Plus organization feature Queries each store and merges results
Shopify data plus other data Analytics only sees Shopify Joins Shopify totals with ad spend, costs from your ERP or warehouse data
The same report, every week, to the same people Exports are manual Runs on a schedule and sends the table or chart
Rules specific to your business Metrics and dimensions are Shopify’s Applies your own grouping, targets or margin rules on top
People who should see one number, not the admin Sharing a report means giving admin access Shows that report, nothing else

For the multi-store case in particular, refund and order reports across Shopify stores compares the options, including organization analytics and spreadsheets, so we will not repeat them here.

How it is usually built

  1. Prototype the query in Analytics. Build the report in the ShopifyQL editor until the numbers match what you expect. This is free and fast, and it settles the definition of each metric before any code exists.
  2. Create the app with the read_reports scope and request Level 2 protected customer data. A custom app is the normal choice for one business; see create a Shopify custom app in the Dev Dashboard.
  3. Run the same query through shopifyqlQuery, and handle parseErrors, the 429 budget response and RESPONSE_TOO_LARGE.
  4. Cache results according to how fresh they need to be.
  5. Deliver the result where people already look: an admin page, an email, a chat message or a sheet.
  6. Keep the API version current, since a custom app that stops being updated eventually breaks. See maintaining a custom Shopify app.

The prototype step matters most. Most reporting disagreements are about definitions: whether sales include tax, how refunds are dated, which channel an order belongs to. Settling that in the editor first saves a lot of rework.

When not to build

  • If the report is for one store and you look at it now and then, save a custom exploration and stop there.
  • If you need a one-off answer, ask Sidekick to draft the query in the editor.
  • If you need row-level data for accounting, a dedicated export is the better fit; see Shopify order export for accounting.

Have it built

If the report you need combines stores or systems, runs on a schedule, or goes to people who should not have Analytics access, it is a reasonable custom project. Our custom solutions page covers the internal tools and reports the Storefleet Store Ops team takes on and what to send us; Shopify back-office tools for teams shows where reports fit alongside other internal tools. If the tool is for customers or store staff rather than the back office, see checkout UI extensions, customer account extensions and POS UI extensions.

FAQ

Can I create custom reports on the Basic plan?

Yes. Shopify’s help center says creating a new data exploration is available on all Shopify plans.

What scope does an app need to run ShopifyQL?

read_reports, plus Level 2 access to protected customer data and GraphQL Admin API version 2025-10 or later.

Is shopifyqlQuery the same as the old ShopifyQL API?

It is a new shopifyqlQuery field on the GraphQL Admin API, added in version 2025-10 and announced on October 1, 2025. Developers use it to query merchant analytics such as sales, customer and product data.

Can ShopifyQL return a list of individual orders?

It is designed for aggregated metrics. Shopify recommends the regular Admin API objects for reading individual orders, customers or products.

What file formats can I export a report in?

Most reports export as CSV, XML, JSON Lines or Apache Parquet. The legacy Finances reports export as CSV.

Sign in to your workspace

Each workspace has its own address: <workspace>.getstorefleet.com. Enter your workspace name, or paste its address, and we take you to its sign-in page.

You will go to

New to StoreFleet? Create a workspace