Skip to main content

shopifyql

Python SDK for running ShopifyQL queries from Python. It aims to make it fast and easy to get your store data with minimal setup, while staying flexible for power users.

Key points:

  • No hard runtime deps by default (only requests).
  • Choose your result backend: dependency-free records (default), pandas, or polars.
  • Optional OAuth helper to obtain an access token during development.

Quick setup with CLI template

For the easiest way to test this package, you can use our CLI template that sets up everything for you to start working in a Jupyter Notebook with access to your store:

shopify app init --template=https://github.com/Shopify/shopify-app-notebooks-template

This template will:

  • Set up a Python 3.11+ environment with all dependencies needed to run shopifyql inside Jupyter notebooks
  • Handle app configuration and authentication automatically
  • Provide a ready-to-use development environment powered by Shopify CLI

See the template repository for more details.

Requirements

  • Python 3.11+
  • A Shopify shop (e.g., your-shop-name) and an Admin API access token with access to the read_reports scope.

Installation

Base (no heavy dependencies):

pip install shopifyql

With pandas support:

pip install "shopifyql[pandas]"

With polars support:

pip install "shopifyql[polars]"

With both pandas and polars:

pip install "shopifyql[all]"

Using uv for local development of this repo:

uv venv
uv sync --group dev

Quick start

The default return type is a list of dict records, so you can use the library without dependencies if needed.

from shopifyql import ShopifyQLClient

client = ShopifyQLClient(shop="your-shop", access_token="shpat_...")

records = client.query("FROM sales SHOW total_sales SINCE 2025-01-01 UNTIL 2025-12-31")
print(records[:2])  # e.g., [{"total_sales": 123.45}, {"total_sales": 67.89}]

Using pandas

pip install "shopifyql[pandas]"
from shopifyql import ShopifyQLClient, ShopifyQLPandasResult

client = ShopifyQLClient(shop="your-shop", access_token="shpat_...")
df = client.query_pandas("FROM sales SHOW orders TIMESERIES DAY SINCE -30d")
print(df.head())

Using polars

pip install "shopifyql[polars]"
from shopifyql import ShopifyQLClient, ShopifyQLPolarsResult

client = ShopifyQLClient(shop="your-shop", access_token="shpat_...")
df = client.query_polars("FROM sales SHOW total_sales group by product_title")
print(df.head())

OAuth helper (optional)

If you don’t have a token handy, you can use a local browser OAuth helper to obtain one during development:

from shopifyql import ShopifyQLClient

client = ShopifyQLClient.from_oauth(
    shop="your-shop",
    key="your_api_key",
    secret="your_api_secret",
    port = 4545
)

df = client.query_pandas("FROM sales SHOW total_sales SINCE -7d")

Steps for oauth

  • Requires read_reports access scope. Also: Level 2 access to Customer data including name, address, phone, and email fields. Please refer to https://shopify.dev/docs/apps/launch/protected-customer-data. Scopes caan be modified in your app version settings: https://dev.shopify.com/dashboard/ -> Your App -> Versions -> Create a version
  • A valid redirect_uri is needed for this oauth flow to work, please use http://localhost:4545/callback
  • You can only receive scopes that are enabled in your app settings, if you need more you will want to submit a new app version.
  • If your browser lands on admin.shopify.com/.../oauth/authorize and the helper never returns to http://localhost:4545/callback, your app’s Redirect URLs probably don’t include the exact redirect_uri. Add http://localhost:4545/callback (or the port you pass, configurable in from_oauth) to App setup → Redirect URLs, then try again. The redirect_uri must match exactly.
  • After auth, you can confirm what the scopes are currently available with:
scopes = client.get_current_scopes()
print(scopes)

Context manager and connection reuse

When running multiple queries, use the client as a context manager to reuse a keep-alive requests.Session per thread and avoid repeated TLS/HTTP setup costs:

from shopifyql import ShopifyQLClient

SHOP = "your-shop"
ACCESS_TOKEN = "shpat_..."

with ShopifyQLClient(SHOP, ACCESS_TOKEN) as client:
    df1 = client.query_pandas("FROM sales SHOW total_sales GROUP BY product_title SINCE -30d UNTIL now")
    df2 = client.query_pandas("FROM sales SHOW net_sales GROUP BY product_title SINCE -30d UNTIL now")
    print(df1.merge(df2, on="product_title"))

Custom result classes

You can provide your own result transformer by implementing ShopifyQLResult:

from typing import Any
from shopifyql import ShopifyQLClient, ShopifyQLResult

class MyResult(ShopifyQLResult):
    @classmethod
    def from_table_data(cls, table_data: dict[str, Any]) -> Any:
        # Transform the ShopifyQL tableData into your preferred structure
        return {c["name"]: [row[i] for i, _ in enumerate(table_data["columns"])] for i, c in enumerate(table_data["columns"])}

client = ShopifyQLClient(shop="your-shop", access_token="shpat_...")
custom = client.query("from sales show total_sales", result_class=MyResult)

Error handling & rate limits

The client will:

  • Gate requests with a fixed-window rate limiter to avoid 429s; if the window is exhausted, it sleeps until the next window (with jitter).
  • Backoff and retry on transient request errors (and other request exceptions) up to max_retries.

You may see:

  • requests.exceptions.HTTPError for non-2xx HTTP responses (after retries as applicable).
  • requests.exceptions.RequestException for network errors (after retries).
  • ValueError("No valid table data found in response") when the ShopifyQL response is malformed.

Configuration

  • API version: defaults to 2025-10. Override via constructor version="YYYY-MM".
  • Timeout: defaults to 10s per request via constructor connect_timeout.
  • Retries: defaults to max_retries=3 with exponential backoff and jitter.
  • Rate limiting: FixedWindowConfig(window_seconds=60, max_requests=1000) by default; override via rate_limit_config.
  • Connections: when used as a context manager, the client reuses a per-thread requests.Session (keep-alive); outside a context it uses ephemeral sessions.
  • Connection pool: pool_maxsize=10 by default.

Development

Clone and develop with uv or pip:

uv venv
uv sync --group=dev
pytest -q

Metadata

Release files for shopifyql 0.1.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for shopifyql 0.1.2
File Size Uploaded
shopifyql-0.1.2.tar.gz 13.1 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for shopifyql 0.1.2
File Interpreter ABI Platform
shopifyql-0.1.2-py3-none-any.whl Python 3 none any Details

Total release size: 25.8 kB

Release files / shopifyql-0.1.2.tar.gz

Download URL shopifyql-0.1.2.tar.gz
Size 13.1 kB
Tags Source
SHA-256 checksum
How to use checksums
de3c73d05347e81f7e7d18393cdd82eee703fba269956637e26d5727934295d8
BLAKE2b-256 checksum
How to use checksums
6d491c543a924b702f08e2894b06e27ea930cc31180e15c7b10b8f8e4d7a4c2d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Dec 9, 2025.

Transparency log

Release files / shopifyql-0.1.2-py3-none-any.whl

Download URL shopifyql-0.1.2-py3-none-any.whl
Size 12.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
0bc463fdb13a07d488e30555cdd515058e74980700fc417d01d0d19e3ab355da
BLAKE2b-256 checksum
How to use checksums
667d045afbf7ac712c99af7e892b9e92661fcd509a6f900fcf0910310ba0affd
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Dec 9, 2025.

Transparency log

Release history Release notifications | RSS feed

This release

0.1.2 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page