Выполняйте запросы к GraphQL API административной панели и интернет-магазина Shopify с помощью curl
Работайте с магазином Shopify напрямую через GraphQL API: просматривайте товары, управляйте запасами, получайте заказы, обновляйте данные клиентов и читайте метаполя с помощью curl и токена доступа пользовательского приложения.
Что понадобится
- Требуется наличие curl и jq, а также переменных окружения SHOPIFY_ACCESS_TOKEN (токен Admin API, начинающийся с shpat_), SHOPIFY_STORE_DOMAIN (постоянный домен вида myshopify.com, а не пользовательский домен) и SHOPIFY_API_VERSION (по умолчанию 2026-01; можно указать более старую версию). Работает на Linux, macOS и Windows.
- SDK или фреймворк приложения не требуется — достаточно конечной точки GraphQL и токена доступа пользовательского приложения.
- Требуется доступ к административной панели Shopify с правом создавать пользовательское приложение или управлять им.
- Требуется право настраивать области действия Admin API, необходимые для выбранных операций.
- Для работы с товарами и коллекциями требуются соответствующие области действия.
- Для работы с запасами нужны области действия, связанные с управлением запасами.
- Для работы с заказами требуются области действия, связанные с заказами; 30 последних заказов можно получить без области read_all_orders.
- Для работы с клиентами нужны области действия, связанные с клиентами.
- Для работы с черновиками заказов требуются области действия, связанные с черновиками заказов.
- Для работы с процессом отгрузки нужны области действия, связанные с отгрузками.
- Метаданные и метаобъекты охватываются соответствующими областями действия ресурсов.
Как применить
REST API административной панели устарел с апреля 2024 года и получает лишь обновления по безопасности; для всех административных задач используйте GraphQL Admin, а для чтения данных, видимых клиентам (товары, коллекции, корзина), — Storefront GraphQL.
Внимание: с 1 января 2026 года в административной панели Shopify больше нельзя создавать новые пользовательские приложения старого типа; для новых установок следует использовать Dev Dashboard, а существующие приложения, созданные в административной панели, продолжат работать. Если в магазине нет такого существующего приложения и дата позже 2026-01-01, используйте процесс Dev Dashboard вместо процесса в административной панели.
Ограничения скорости основаны на алгоритме «текучего ведра», рассчитанном исходя из стоимости запроса; каждый ответ содержит объект расширений по ограничению скорости с параметрами maximumAvailable, currentlyAvailable и restoreRate. Снижайте частоту запросов, когда значение currentlyAvailable станет меньше стоимости следующего запроса. У стандартных магазинов «ведро» вмещает 100 единиц и пополняется на 50 единиц в секунду; у магазинов типа Plus — 1000/100.
Передавайте вывод через jq для получения читаемого формата; добавление параметра -sS в curl позволяет видеть ошибки, но скрывает индикатор прогресса.
Синтаксис запросов к товарам поддерживает сопоставление полей, фразы в кавычках, префиксное сопоставление, логические операторы AND и OR, отрицание NOT и скобки для группировки.
Полезные фильтры для запросов заказов включают financial_status, fulfillment_status, status, created_at и test.
Каждая строка JSONL представляет собой узел, а связанные элементы выводятся как отдельные строки с идентификатором родительского узла; при необходимости их можно собрать обратно на стороне клиента.
GID против числового идентификатора. В устаревшем REST использовались числовые идентификаторы; в GraphQL требуются полные строки GID. Для преобразования используйте: gid://shopify/Product/<numeric>.
Следите за ограничениями скорости: один запрос к товарам с глубокой вложенностью может стоить более 1000 единиц, что приведёт к немедленному ограничению скорости в магазине стандартного тарифа. Начинайте с узких запросов, анализируйте объекты расширений по ограничению скорости в каждом ответе и корректируйте запросы.
Для получения исторических данных запрашивайте область действия read_all_orders; без неё запрос заказов автоматически ограничивается 60-дневным периодом, при этом ошибка не выдаётся, а результатов просто меньше. Для владельцев магазинов Shopify Plus с большим количеством заказов запрашивайте эту область через настройки защищённых данных приложения.
Мутации — это реальные действия: они создают товары, оформляют возвраты, отменяют заказы и осуществляют отгрузки. Перед выполнением операций productCreate, orderCancel, refundCreate или любой массовой мутации чётко укажите, какие изменения будут внесены и в каком магазине, и получите подтверждение от пользователя. Клон рабочей базы данных для тестирования отсутствует, если только у пользователя нет отдельного тестового магазина.
Сначала настройте токен: установите приложение в админ-панели Shopify, и токен доступа к Admin API появится лишь один раз — сразу же скопируйте его, так как Shopify больше никогда его не покажет. Токены начинаются с shpat_.
В админ-панели Shopify перейдите в раздел Настройки → Приложения и каналы продаж → Разработка приложений → Создать приложение.
Нажмите «Настроить области действия Admin API», выберите необходимые параметры (примеры ниже) и сохраните изменения.
Установите приложение, и токен доступа к Admin API появится лишь один раз. Сразу же скопируйте его — Shopify больше никогда его не покажет. Токены начинаются с shpat_.
Сохраните эти значения в файл .env:
SHOPIFY_ACCESS_TOKEN=shpat_xxxxxxxxxxxxxxxxxxxx
SHOPIFY_STORE_DOMAIN=my-store.myshopify.com
SHOPIFY_API_VERSION=2026-01
Основы API для повторно используемого шаблона curl:
Конечная точка Admin — https://$SHOPIFY_STORE_DOMAIN/admin/api/$SHOPIFY_API_VERSION/graphql.json.
Используйте заголовок авторизации X-Shopify-Access-Token, а не Authorization.
Метод всегда POST, тип контента — application/json, а тело запроса — это объект JSON с параметрами query и variables.
Код ответа HTTP 200 не означает успех: GraphQL возвращает ошибки в массиве errors на верхнем уровне и в поле userErrors по каждому полю — всегда проверяйте оба варианта.
Идентификаторы представляют собой строки вида gid://shopify/Product/123; передавайте их без изменений и не удаляйте префикс.
Базовый шаблон curl (для повторного использования):
shop_gql() {
local query="$1"
local variables="${2:-{}}"
curl -sS -X POST \
"https://${SHOPIFY_STORE_DOMAIN}/admin/api/${SHOPIFY_API_VERSION:-2026-01}/graphql.json" \
-H "Content-Type: application/json" \
-H "X-Shopify-Access-Token: ${SHOPIFY_ACCESS_TOKEN}" \
--data "$(jq -nc --arg q "$query" --argjson v "$variables" '{query: $q, variables: $v}')"
}
Получить информацию о магазине и текущую версию API.
shop_gql '{ shop { name myshopifyDomain primaryDomain { url } currencyCode plan { displayName } } }' | jq
Вывести список всех поддерживаемых версий API.
shop_gql '{ publicApiVersions { handle supported } }' | jq '.data.publicApiVersions[] | select(.supported)'
Найти товары, вернув первые 20 результатов, соответствующих запросу.
shop_gql '
query($q: String!) {
products(first: 20, query: $q) {
edges { node { id title handle status totalInventory variants(first: 5) { edges { node { id sku price inventoryQuantity } } } } }
pageInfo { hasNextPage endCursor }
}
}' '{"q":"hoodie status:active"}' | jq
Пагинировать товары с помощью курсора.
shop_gql '
query($cursor: String) {
products(first: 100, after: $cursor) {
edges { cursor node { id handle } }
pageInfo { hasNextPage endCursor }
}
}' '{"cursor":null}'
# subsequent calls: pass the previous endCursor
Получить информацию о товаре вместе с его вариантами и метаданными.
shop_gql '
query($id: ID!) {
product(id: $id) {
id title handle descriptionHtml tags status
variants(first: 20) { edges { node { id sku price compareAtPrice inventoryQuantity selectedOptions { name value } } } }
metafields(first: 20) { edges { node { namespace key type value } } }
}
}' '{"id":"gid://shopify/Product/10079467700516"}' | jq
Создать товар с одним вариантом.
shop_gql '
mutation($input: ProductCreateInput!) {
productCreate(product: $input) {
product { id handle }
userErrors { field message }
}
}' '{"input":{"title":"Test Hoodie","status":"DRAFT","vendor":"Hermes","productType":"Apparel","tags":["test"]}}'
В последних версиях у вариантов теперь есть собственные операции изменения.
# Add variants after creating the product
shop_gql '
mutation($productId: ID!, $variants: [ProductVariantsBulkInput!]!) {
productVariantsBulkCreate(productId: $productId, variants: $variants) {
productVariants { id sku price }
userErrors { field message }
}
}' '{"productId":"gid://shopify/Product/...","variants":[{"optionValues":[{"optionName":"Size","name":"M"}],"price":"49.00","inventoryItem":{"sku":"HD-M","tracked":true}}]}'
Изменить цену или артикул варианта.
shop_gql '
mutation($productId: ID!, $variants: [ProductVariantsBulkInput!]!) {
productVariantsBulkUpdate(productId: $productId, variants: $variants) {
productVariants { id sku price }
userErrors { field message }
}
}' '{"productId":"gid://shopify/Product/...","variants":[{"id":"gid://shopify/ProductVariant/...","price":"55.00"}]}'
Вывести список последних заказов — по умолчанию последние 30 штук, если не указан параметр read_all_orders.
shop_gql '
{
orders(first: 20, reverse: true, query: "financial_status:paid") {
edges { node {
id name createdAt displayFinancialStatus displayFulfillmentStatus
totalPriceSet { shopMoney { amount currencyCode } }
customer { id displayName email }
lineItems(first: 10) { edges { node { title quantity sku } } }
} }
}
}' | jq
Получить информацию об отдельном заказе вместе с адресом доставки.
shop_gql '
query($id: ID!) {
order(id: $id) {
id name email
shippingAddress { name address1 address2 city province country zip phone }
lineItems(first: 50) { edges { node { title quantity variant { sku } originalUnitPriceSet { shopMoney { amount currencyCode } } } } }
transactions { id kind status amountSet { shopMoney { amount currencyCode } } }
}
}' '{"id":"gid://shopify/Order/...."}' | jq
Найти и создать клиентов.
# Search
shop_gql '
{
customers(first: 10, query: "email:*@example.com") {
edges { node { id email displayName numberOfOrders amountSpent { amount currencyCode } } }
}
}'
# Create
shop_gql '
mutation($input: CustomerInput!) {
customerCreate(input: $input) {
customer { id email }
userErrors { field message }
}
}' '{"input":{"email":"test@example.com","firstName":"Test","lastName":"User","tags":["api-created"]}}'
Работа с запасами.
Запасы хранятся в элементах запасов, привязанных к вариантам товаров, причем количество учитывается для каждого места хранения отдельно.
# Get inventory for a variant across all locations
shop_gql '
query($id: ID!) {
productVariant(id: $id) {
id sku
inventoryItem {
id tracked
inventoryLevels(first: 10) {
edges { node { location { id name } quantities(names: ["available","on_hand","committed"]) { name quantity } } }
}
}
}
}' '{"id":"gid://shopify/ProductVariant/..."}'
Корректировать остатки товара на величину изменения — для этого используется inventoryAdjustQuantities:
shop_gql '
mutation($input: InventoryAdjustQuantitiesInput!) {
inventoryAdjustQuantities(input: $input) {
inventoryAdjustmentGroup { reason changes { name delta } }
userErrors { field message }
}
}' '{
"input": {
"reason": "correction",
"name": "available",
"changes": [{"delta": 5, "inventoryItemId": "gid://shopify/InventoryItem/...", "locationId": "gid://shopify/Location/..."}]
}
}'
Устанавливать абсолютное количество товара (а не разницу) — для этого используется inventorySetQuantities:
shop_gql '
mutation($input: InventorySetQuantitiesInput!) {
inventorySetQuantities(input: $input) {
inventoryAdjustmentGroup { id }
userErrors { field message }
}
}' '{"input":{"reason":"correction","name":"available","ignoreCompareQuantity":true,"quantities":[{"inventoryItemId":"gid://shopify/InventoryItem/...","locationId":"gid://shopify/Location/...","quantity":100}]}}'
Читать и записывать метаполя и метаобъекты.
Метаданные позволяют прикреплять дополнительную информацию к ресурсам, таким как товары, клиенты, заказы и магазин.
# Read
shop_gql '
query($id: ID!) {
product(id: $id) {
metafields(first: 10, namespace: "custom") {
edges { node { key type value } }
}
}
}' '{"id":"gid://shopify/Product/..."}'
# Write (works for any owner type)
shop_gql '
mutation($metafields: [MetafieldsSetInput!]!) {
metafieldsSet(metafields: $metafields) {
metafields { id key namespace }
userErrors { field message code }
}
}' '{"metafields":[{"ownerId":"gid://shopify/Product/...","namespace":"custom","key":"care_instructions","type":"multi_line_text_field","value":"Wash cold. Tumble dry low."}]}'
Для публичного доступа только для чтения используйте Storefront API.
У него другая конечная точка и токен; применяется для приложений, ориентированных на клиентов, и для головных решений типа Hydrogen; заголовки запросов также отличаются.
Конечная точка — https://$SHOPIFY_STORE_DOMAIN/api/$SHOPIFY_API_VERSION/graphql.json.
Для публичного доступа используйте X-Shopify-Storefront-Access-Token — его можно встраивать в браузер.
Для приватного доступа используйте заголовок с приватным токеном — только для сервера.
curl -sS -X POST \
"https://${SHOPIFY_STORE_DOMAIN}/api/${SHOPIFY_API_VERSION:-2026-01}/graphql.json" \
-H "Content-Type: application/json" \
-H "X-Shopify-Storefront-Access-Token: ${SHOPIFY_STOREFRONT_TOKEN}" \
-d '{"query":"{ shop { name } products(first: 5) { edges { node { id title handle } } } }"}' | jq
Выполнять операции пакетной обработки.
Применяйте их для экспорта данных, превышающих лимиты частоты запросов, например, полный каталог товаров или все заказы за год.
# 1. Start bulk query
shop_gql '
mutation {
bulkOperationRunQuery(query: """
{ products { edges { node { id title handle variants { edges { node { sku price } } } } } } }
""") {
bulkOperation { id status }
userErrors { field message }
}
}'
# 2. Poll status
shop_gql '{ currentBulkOperation { id status errorCode objectCount fileSize url partialDataUrl } }'
# 3. When status=COMPLETED, download the JSONL file
curl -sS "$URL" > products.jsonl
Подписаться на вебхуки.
Подписывайтесь на события, чтобы не приходилось постоянно опрашивать сервер.
shop_gql '
mutation($topic: WebhookSubscriptionTopic!, $sub: WebhookSubscriptionInput!) {
webhookSubscriptionCreate(topic: $topic, webhookSubscription: $sub) {
webhookSubscription { id topic endpoint { __typename ... on WebhookHttpEndpoint { callbackUrl } } }
userErrors { field message }
}
}' '{"topic":"ORDERS_CREATE","sub":{"callbackUrl":"https://example.com/webhook","format":"JSON"}}'
Проверяйте HMAC входящего вебхука с помощью секретного ключа приложения (а не токена доступа).
echo -n "$REQUEST_BODY" | openssl dgst -sha256 -hmac "$APP_SECRET" -binary | base64
# Compare to X-Shopify-Hmac-Sha256 header
Конечные точки REST всё ещё существуют, но более не развиваются: не создавайте новые интеграции на основе REST; используйте GraphQL.
Проверьте формат токенов: токены Admin начинаются с shpat_, публичные токены Storefront имеют другой префикс. Если вы используете неверный заголовок при наличии правильного токена, каждый запрос будет возвращать ошибку 401 без полезного текста.
Ошибка 403 при наличии действительного токена означает отсутствие необходимых прав: Shopify возвращает ошибку access-denied. Перенастройте области действия Admin API приложения, затем переустановите его, чтобы сгенерировать новый токен.
Пустой массив userErrors не означает успех: также проверьте, что поле data не равно null. В некоторых случаях ошибки вообще не заполняют этот массив — изучите всю структуру ответа.
GID против числового идентификатора. В устаревшем REST использовались числовые ID; в GraphQL требуются полные строки GID. Для преобразования используйте: gid://shopify/Product/<numeric>.
Порядок пагинации: по умолчанию товары сортируются по id, а не по дате создания. Чтобы получить самые новые товары первыми, установите reverse: true.
Валюты представлены строками: суммы возвращаются как строки, а не числа, поэтому не стоит слепо конвертировать их, если важно сохранение нулевых разрядов.
Поля типа Money с поддержкой нескольких валют содержат как presentmentMoney (валюта клиента), так и shopMoney (валюта магазина); выбирайте одно значение для последовательного использования.
Источник: nousresearch/hermes-agent и участники проекта. Лицензия: MIT.
Copyright (c) 2025 Nous Research
Перевод и редакционная адаптация: ClawCases.
>Ресурсы
>Похожие юзкейсы
Арбитр разрешения конфликтов слияния агентов
Разрешает конфликт слияния Git между ветками двух агентов в качестве беспристрастной третьей стороны: нейтральный арбитр, который принимает обе версии изменений и указанные намерения, после чего формирует итоговый результат слияния, не перезаписывая ни одну из сторон.
Автоматические заметки и задачи по итогам встреч
Преобразуйте любую расшифровку встречи в структурированные заметки с решениями и задачами, а также автоматически создавайте задачи в вашем инструменте управления проектами.
Еженедельный обзор и планирование
Провести ограниченный еженедельный обзор и перезагрузку планирования, при которых анализируются взятые обязательства, заблокированные проекты и отложенные задачи, после чего формируется план на следующую неделю с учётом реальной вместимости календаря.