products
Search for products in the catalog.
Query Structure
query {
products(
query: String
limit: Int
offset: Int
sort: String
productGroup: String
priceMin: Float
priceMax: Float
onlyDiscounted: Boolean
onlyNew: Boolean
first: Int
after: String
last: Int
before: String
sortKey: String
reverse: Boolean
) {
edges {
cursor
node {
# See Product object for fields
}
}
nodes {
# See Product object for fields
}
pageInfo {
hasNextPage
hasPreviousPage
startCursor
endCursor
}
totalCount
}
}Arguments
| Argument | Type | Description | Required | Default |
|---|---|---|---|---|
| query | String | Search query string | No | None |
| limit | Int | Maximum number of results to return | No | None |
| offset | Int | Offset for pagination | No | None |
| sort | String | Sort order (price-ascending, price-descending, title-ascending, title-descending, etc.) | No | None |
| productGroup | String | Filter by product group | No | None |
| priceMin | Float | Minimum price filter | No | None |
| priceMax | Float | Maximum price filter | No | None |
| onlyDiscounted | Boolean | Show only products with discount | No | false |
| onlyNew | Boolean | Show only new products | No | false |
| first | Int | Returns the first n elements (cursor-based pagination) | No | None |
| after | String | Returns elements after cursor (cursor-based pagination) | No | None |
| last | Int | Returns the last n elements (cursor-based pagination) | No | None |
| before | String | Returns elements before cursor (cursor-based pagination) | No | None |
| sortKey | String | The field to sort by. Available values: default, title, price, created | No | default |
| reverse | Boolean | Reverse the order of the underlying list | No | false |
Return Type
Type: ProductConnection
A connection to a list of Product items with the following fields:
| Field | Type | Description |
|---|---|---|
| edges | [ProductEdge] | A list of edges. |
| nodes | [Product] | A list of nodes. |
| pageInfo | PageInfo | Information about pagination. |
| totalCount | Int | The total number of items. |
ProductEdge
| Field | Type | Description |
|---|---|---|
| cursor | String | A cursor for use in pagination. |
| node | Product | The item at the end of the edge. |
PageInfo
| Field | Type | Description |
|---|---|---|
| hasNextPage | Boolean | Whether there are more items after this page. |
| hasPreviousPage | Boolean | Whether there are more items before this page. |
| startCursor | String | The cursor of the first item in the page. |
| endCursor | String | The cursor of the last item in the page. |
Examples
Basic Query
query {
products(first: 10) {
nodes {
id
title
isAvailable
}
totalCount
}
}Search Query
query {
products(query: "shirt", first: 20) {
nodes {
id
title
description
isAvailable
}
pageInfo {
hasNextPage
endCursor
}
totalCount
}
}Filtered Query
query {
products(
productGroup: "clothing"
priceMin: 10.00
priceMax: 100.00
onlyDiscounted: true
first: 20
sortKey: "price"
) {
nodes {
id
title
handle
isAvailable
variants {
id
price
compareAtPrice
}
}
pageInfo {
hasNextPage
endCursor
}
totalCount
}
}Cursor-Based Pagination
query {
products(first: 10, after: "cursor_value_here") {
edges {
cursor
node {
id
title
isAvailable
}
}
pageInfo {
hasNextPage
hasPreviousPage
startCursor
endCursor
}
totalCount
}
}Advanced Query with Sorting
query {
products(
query: "shoes"
first: 20
sortKey: "price"
reverse: true
onlyNew: true
) {
nodes {
id
title
handle
seoTitle
seoDescription
isAvailable
isDirectlyBuyable
tags
rating
reviewCount
variants {
id
title
sku
price
available
}
manufacturer {
id
name
}
productGroups {
id
name
}
createdAt
updatedAt
}
pageInfo {
hasNextPage
hasPreviousPage
startCursor
endCursor
}
totalCount
}
}cURL Example
curl -X POST https://www.mystoreurl.com/graphql/0.9.0 \
-H "Content-Type: application/json" \
-d '{"query":"query { products(first: 10) { nodes { id title isAvailable } totalCount } }"}'JavaScript Example
async function searchProducts(options = {}) {
const {
query = '',
first = 10,
after = null,
productGroup = null,
priceMin = null,
priceMax = null,
sortKey = 'default',
reverse = false
} = options;
const graphqlQuery = `
query SearchProducts(
$query: String
$first: Int
$after: String
$productGroup: String
$priceMin: Float
$priceMax: Float
$sortKey: String
$reverse: Boolean
) {
products(
query: $query
first: $first
after: $after
productGroup: $productGroup
priceMin: $priceMin
priceMax: $priceMax
sortKey: $sortKey
reverse: $reverse
) {
nodes {
id
title
description
isAvailable
variants {
id
price
available
}
}
pageInfo {
hasNextPage
endCursor
}
totalCount
}
}
`;
try {
const response = await fetch('https://www.mystoreurl.com/graphql/0.9.0', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
query: graphqlQuery,
variables: {
query: query || null,
first,
after,
productGroup,
priceMin,
priceMax,
sortKey,
reverse
}
}),
});
const result = await response.json();
if (result.errors) {
console.error('GraphQL errors:', result.errors);
return null;
}
return result.data.products;
} catch (error) {
console.error('Network error:', error);
return null;
}
}
// Usage examples
// Basic search
searchProducts({ first: 10 }).then(products => {
if (products) {
console.log(`Found ${products.totalCount} products`);
products.nodes.forEach(product => {
console.log(`- ${product.title}`);
});
}
});
// Filtered search with pagination
searchProducts({
query: 'shirt',
first: 20,
priceMin: 10,
priceMax: 50,
sortKey: 'price',
reverse: false
}).then(products => {
if (products) {
console.log('Products:', products.nodes);
if (products.pageInfo.hasNextPage) {
console.log('More products available, use cursor:', products.pageInfo.endCursor);
}
}
});Response Format
The response will contain a products object with the requested fields. Example response for a basic query:
{
"data": {
"products": {
"nodes": [
{
"id": 12345,
"title": "Example Product 1",
"isAvailable": true
},
{
"id": 12346,
"title": "Example Product 2",
"isAvailable": true
},
{
"id": 12347,
"title": "Example Product 3",
"isAvailable": false
}
],
"totalCount": 150
}
}
}Example response with pagination info:
{
"data": {
"products": {
"edges": [
{
"cursor": "YXJyYXljb25uZWN0aW9uOjA=",
"node": {
"id": 12345,
"title": "Example Product 1"
}
},
{
"cursor": "YXJyYXljb25uZWN0aW9uOjE=",
"node": {
"id": 12346,
"title": "Example Product 2"
}
}
],
"pageInfo": {
"hasNextPage": true,
"hasPreviousPage": false,
"startCursor": "YXJyYXljb25uZWN0aW9uOjA=",
"endCursor": "YXJyYXljb25uZWN0aW9uOjE="
},
"totalCount": 150
}
}
}If no products match the query, nodes will be an empty array and totalCount will be 0.
Error Handling
| Error Code | Description |
|---|---|
BAD_USER_INPUT | One or more provided arguments are invalid. |
INTERNAL_SERVER_ERROR | An unexpected error occurred on the server. |
RATE_LIMIT_EXCEEDED | The client has exceeded the allowed number of requests. |
Clients should check for errors in the GraphQL response’s errors array and handle accordingly.
Performance Considerations
- Use pagination (
first/afterorlast/before) to limit result set size. - Limit requested fields to only those needed to reduce response size and improve performance.
- Avoid requesting deeply nested fields unless necessary.
- Use filters (
productGroup,priceMin,priceMax,onlyDiscounted,onlyNew) to narrow results server-side. - The API enforces rate limiting; clients should implement retry logic with exponential backoff.
- Responses may be cached by clients or intermediaries; consider cache headers if provided.
- For large catalogs, prefer cursor-based pagination (
first/after) over offset-based pagination (limit/offset) for better performance.
Authentication
This API currently does not require authentication. However, authentication requirements may be introduced in future versions. Monitor API updates for any changes to authentication requirements.
Related Types
Notes
- All arguments are optional; calling
productswithout arguments returns a default paginated list of products. - The
queryargument performs a text search across product titles and descriptions. - Use
sortKeywith valuesdefault,title,price, orcreatedto sort results. - The
reverseargument reverses the sort order when set totrue. - Both offset-based (
limit/offset) and cursor-based (first/after/last/before) pagination are supported. - Cursor-based pagination is recommended for large result sets as it provides more consistent results.
- The
totalCountfield returns the total number of products matching the query, regardless of pagination limits. - Filter arguments can be combined to create complex queries.
- The
onlyDiscountedfilter shows products where the current price is less than the compare-at price. - The
onlyNewfilter shows products marked as new in the catalog.
Last updated on