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
shopifyqlinside 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 theread_reportsscope.
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/authorizeand the helper never returns tohttp://localhost:4545/callback, your app’s Redirect URLs probably don’t include the exactredirect_uri. Addhttp://localhost:4545/callback(or the port you pass, configurable in from_oauth) to App setup → Redirect URLs, then try again. Theredirect_urimust 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.HTTPErrorfor non-2xx HTTP responses (after retries as applicable).requests.exceptions.RequestExceptionfor 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 constructorversion="YYYY-MM". - Timeout: defaults to 10s per request via constructor
connect_timeout. - Retries: defaults to
max_retries=3with exponential backoff and jitter. - Rate limiting:
FixedWindowConfig(window_seconds=60, max_requests=1000)by default; override viarate_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=10by 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)
| File | Size | Uploaded | |
|---|---|---|---|
| shopifyql-0.1.2.tar.gz | 13.1 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| 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 logRelease 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