← Shopify App BuilderCONTENT HISTORY

Update to Shopify App Builder

Snapshot Sep 30, 2026 · 23:13 UTC · version 1.4.1

Collection source: not recorded for this historical snapshot.

WHAT CHANGED · RULE-BASED ANALYSIS

First saved snapshot

No earlier snapshot is available to establish a change.

Compare saved observations

Download comparison JSON
Full technical diff · 0 changed fields
Full snapshot data
{
  "description": "Use the legacy REST Admin API only when maintaining an existing integration. Covers common resources, the 40-request bucket with a 2-request-per-second standard restore rate, and migration to GraphQL. New public apps must use the GraphQL Admin API.",
  "included_files": [],
  "name": "admin-rest",
  "skill_md_contents": "---\nname: admin-rest\ndescription: \"Use the legacy REST Admin API only when maintaining an existing integration. Covers common resources, the 40-request bucket with a 2-request-per-second standard restore rate, and migration to GraphQL. New public apps must use the GraphQL Admin API.\"\n---\n\n## When to Use REST API (Rarely)\n\n**LEGACY API WARNING:** Shopify has classified the REST Admin API as legacy since October 1, 2024. Since April 1, 2025, new public apps must use the GraphQL Admin API exclusively. Shopify has not published a blanket December 2026 shutdown date for every REST resource.\n\nUse REST API ONLY for:\n- Maintaining existing legacy applications built before 2024\n- Simple read-only queries from archived systems\n- Temporary compatibility layers during GraphQL migration\n- Systems that cannot be updated to use GraphQL\n\n**MIGRATE TO GRAPHQL FOR:** All new features, bulk operations, cost efficiency, and latest Shopify functionality.\n\n---\n\n## REST vs GraphQL Comparison\n\n| Feature | REST (Legacy) | GraphQL (Current) |\n|---------|---------------|-------------------|\n| Rate Limiting | 40-request standard bucket, restored at 2 requests/sec | Cost-based; restore rate varies by plan |\n| Pagination | Limit/offset (inefficient) | Cursor-based (Relay) |\n| Field Selection | Fixed response (bloated) | Precise fields only |\n| Bulk Operations | Sequential requests | Native JSONL bulk |\n| Latest Features | Some newer features are GraphQL-only | Current platform features |\n| Support Window | Versioned; verify the selected version and resource | Versioned; verify the selected version |\n| Status | Maintenance only | Production active |\n\n---\n\n## API Endpoint Structure\n\n**Base URL:** `https://{shop}.myshopify.com/admin/api/2025-10/`\n\n**Authentication (Header):**\n```bash\ncurl -X GET \"https://store.myshopify.com/admin/api/2025-10/products.json\" \\\n  -H \"X-Shopify-Access-Token: {access_token}\"\n```\n\nThe examples below use `2025-10` for compatibility with older integrations. Before deploying, select a currently supported API version from Shopify's version schedule and test the exact resources you use.\n\n---\n\n## Rate Limiting\n\n**REST limits:**\n- Standard limit: a 40-request bucket per app and store\n- Standard restore rate: 2 requests per second\n- Shopify Plus: limits are typically 10 times the standard limit\n- Read `X-Shopify-Shop-Api-Call-Limit` and honor `Retry-After`; Shopify can reduce limits temporarily\n\n**Rate limit headers:**\n```\nX-Shop-API-Call-Limit: 30/40\nX-Inventory-API-Call-Limit: 20/40\nRetry-After: 2\n```\n\n**Handling 429 (Too Many Requests):**\n```javascript\nasync function executeWithRetry(url, options, maxRetries = 5) {\n  for (let attempt = 1; attempt <= maxRetries; attempt++) {\n    const response = await fetch(url, options);\n\n    if (response.status === 429) {\n      const retryAfter = parseInt(response.headers.get('Retry-After')) || Math.pow(2, attempt - 1);\n      console.log(`Rate limited. Waiting ${retryAfter} seconds...`);\n      await new Promise(resolve => setTimeout(resolve, retryAfter * 1000));\n      continue;\n    }\n\n    return response;\n  }\n  throw new Error('Max retries exceeded');\n}\n```\n\n---\n\n## Common Legacy REST Resources\n\n### Products\n\n**1. List Products**\n```bash\nGET /admin/api/2025-10/products.json?limit=50&status=active\n```\n\nResponse:\n```json\n{\n  \"products\": [\n    {\n      \"id\": 123456789,\n      \"title\": \"Wireless Headphones\",\n      \"handle\": \"wireless-headphones\",\n      \"status\": \"active\",\n      \"vendor\": \"TechBrand\",\n      \"created_at\": \"2024-01-01T12:00:00Z\",\n      \"updated_at\": \"2025-10-15T14:30:00Z\"\n    }\n  ]\n}\n```\n\n**2. Get Single Product**\n```bash\nGET /admin/api/2025-10/products/{id}.json\n```\n\n**3. Create Product**\n```bash\nPOST /admin/api/2025-10/products.json\n```\n\nBody:\n```json\n{\n  \"product\": {\n    \"title\": \"New Product\",\n    \"product_type\": \"Electronics\",\n    \"vendor\": \"MyVendor\",\n    \"status\": \"active\"\n  }\n}\n```\n\n**4. Update Product**\n```bash\nPUT /admin/api/2025-10/products/{id}.json\n```\n\nBody:\n```json\n{\n  \"product\": {\n    \"id\": 123456789,\n    \"title\": \"Updated Title\",\n    \"status\": \"active\"\n  }\n}\n```\n\n### Variants\n\n**5. List Product Variants**\n```bash\nGET /admin/api/2025-10/products/{product_id}/variants.json?limit=50\n```\n\n**6. Create Variant**\n```bash\nPOST /admin/api/2025-10/products/{product_id}/variants.json\n```\n\nBody:\n```json\n{\n  \"variant\": {\n    \"title\": \"Red / Small\",\n    \"sku\": \"WH-RED-S\",\n    \"price\": \"59.99\",\n    \"option1\": \"Red\",\n    \"option2\": \"Small\"\n  }\n}\n```\n\n**7. Update Variant**\n```bash\nPUT /admin/api/2025-10/products/{product_id}/variants/{id}.json\n```\n\n### Orders\n\n**8. List Orders**\n```bash\nGET /admin/api/2025-10/orders.json?status=any&limit=50\n```\n\nResponse:\n```json\n{\n  \"orders\": [\n    {\n      \"id\": 987654321,\n      \"order_number\": 1001,\n      \"email\": \"customer@example.com\",\n      \"created_at\": \"2025-10-01T10:00:00Z\",\n      \"total_price\": \"99.99\",\n      \"currency\": \"USD\",\n      \"fulfillment_status\": \"fulfilled\",\n      \"financial_status\": \"paid\"\n    }\n  ]\n}\n```\n\n**9. Get Single Order**\n```bash\nGET /admin/api/2025-10/orders/{id}.json\n```\n\n**10. Update Order**\n```bash\nPUT /admin/api/2025-10/orders/{id}.json\n```\n\nBody:\n```json\n{\n  \"order\": {\n    \"id\": 987654321,\n    \"tags\": \"wholesale,vip\"\n  }\n}\n```\n\n### Customers\n\n**11. List Customers**\n```bash\nGET /admin/api/2025-10/customers.json?limit=50\n```\n\n**12. Create Customer**\n```bash\nPOST /admin/api/2025-10/customers.json\n```\n\nBody:\n```json\n{\n  \"customer\": {\n    \"first_name\": \"John\",\n    \"last_name\": \"Doe\",\n    \"email\": \"john@example.com\",\n    \"phone\": \"+1234567890\"\n  }\n}\n```\n\n**13. Update Customer**\n```bash\nPUT /admin/api/2025-10/customers/{id}.json\n```\n\n### Shop\n\n**14. Get Shop Information**\n```bash\nGET /admin/api/2025-10/shop.json\n```\n\nResponse:\n```json\n{\n  \"shop\": {\n    \"id\": 123456,\n    \"name\": \"My Store\",\n    \"email\": \"shop@example.com\",\n    \"domain\": \"mystore.myshopify.com\",\n    \"currency\": \"USD\",\n    \"timezone\": \"America/New_York\"\n  }\n}\n```\n\n### Webhooks\n\n**15. List Webhooks**\n```bash\nGET /admin/api/2025-10/webhooks.json\n```\n\n---\n\n## REST to GraphQL Migration Recipes\n\n### Recipe 1: Listing Products with Variants\n\n**Old REST approach (inefficient):**\n```javascript\n// Step 1: Fetch products\nconst productsRes = await fetch(\n  'https://store.myshopify.com/admin/api/2025-10/products.json?limit=250',\n  { headers: { 'X-Shopify-Access-Token': token } }\n);\nconst { products } = await productsRes.json();\n\n// Step 2: For each product, fetch variants separately (N+1 problem)\nconst productData = await Promise.all(\n  products.map(p =>\n    fetch(`https://store.myshopify.com/admin/api/2025-10/products/${p.id}/variants.json`,\n      { headers: { 'X-Shopify-Access-Token': token } }\n    ).then(r => r.json())\n  )\n);\n```\n\n**New GraphQL approach (efficient):**\n```javascript\nconst query = `\n  query {\n    products(first: 250) {\n      edges {\n        node {\n          id\n          title\n          variants(first: 250) {\n            edges {\n              node {\n                id\n                sku\n                price\n              }\n            }\n          }\n        }\n      }\n    }\n  }\n`;\n\nconst response = await fetch(\n  'https://store.myshopify.com/admin/api/2026-01/graphql.json',\n  {\n    method: 'POST',\n    headers: {\n      'Content-Type': 'application/json',\n      'X-Shopify-Access-Token': token,\n    },\n    body: JSON.stringify({ query }),\n  }\n);\n```\n\n**Benefits:** Single request, no N+1 problem, precise field selection, cost-aware rate limiting.\n\n### Recipe 2: Creating Order with Line Items\n\n**Old REST (multiple requests):**\n```javascript\n// REST doesn't support creating orders with line items directly\n// Must create as draft order, then convert\nconst draftRes = await fetch(\n  'https://store.myshopify.com/admin/api/2025-10/draft_orders.json',\n  {\n    method: 'POST',\n    headers: { 'X-Shopify-Access-Token': token, 'Content-Type': 'application/json' },\n    body: JSON.stringify({\n      draft_order: {\n        line_items: [{ variant_id: 123, quantity: 2 }]\n      }\n    })\n  }\n);\n```\n\n**New GraphQL (atomic operation):**\n```graphql\nmutation CreateDraftOrder($input: DraftOrderInput!) {\n  draftOrderCreate(input: $input) {\n    draftOrder {\n      id\n      draftOrderLineItems(first: 10) {\n        edges {\n          node {\n            id\n            title\n            quantity\n          }\n        }\n      }\n    }\n  }\n}\n```\n\n### Recipe 3: Bulk Update Variant Prices\n\n**Old REST (request-by-request update):**\n```javascript\nconst updates = [\n  { id: 1, price: '49.99' },\n  { id: 2, price: '59.99' },\n  // ... 1000 more\n];\n\nfor (const update of updates) {\n  await fetch(\n    `https://store.myshopify.com/admin/api/2025-10/variants/${update.id}.json`,\n    {\n      method: 'PUT',\n      headers: { 'X-Shopify-Access-Token': token, 'Content-Type': 'application/json' },\n      body: JSON.stringify({ variant: { price: update.price } })\n    }\n  );\n  await new Promise(resolve => setTimeout(resolve, 100)); // Delay to avoid rate limit\n}\n// Time for 1000 updates: ~100 seconds\n```\n\n**New GraphQL with bulk operations (fast):**\n```javascript\nconst jsonl = updates\n  .map(u => ({\n    __typename: 'ProductVariant',\n    id: `gid://shopify/ProductVariant/${u.id}`,\n    price: u.price\n  }))\n  .map(o => JSON.stringify(o))\n  .join('\\n');\n\nconst bulkRes = await fetch(\n  'https://store.myshopify.com/admin/api/2026-01/graphql.json',\n  {\n    method: 'POST',\n    headers: { 'X-Shopify-Access-Token': token, 'Content-Type': 'application/json' },\n    body: JSON.stringify({\n      query: `mutation { bulkOperationRunMutation(input: \"${jsonl}\") { bulkOperation { id status } } }`\n    })\n  }\n);\n// Time for 1000 updates: ~10-30 seconds (with polling)\n```\n\n---\n\n## Red Flags: When REST Fails\n\n| Red Flag | Symptom | Solution |\n|----------|---------|----------|\n| **N+1 problem** | 1000+ requests for simple data fetch | Migrate to GraphQL single query |\n| **Rate limit throttling** | Constant 429 responses during bulk operations | Use GraphQL bulk operations |\n| **Missing fields** | REST returns data you don't need (bloated) | Use GraphQL precise field selection |\n| **Slow pagination** | Offset/limit pagination on large dataset | Use GraphQL cursor-based pagination |\n| **No bulk update endpoint** | Creating/updating 100+ records slowly | Use GraphQL bulkOperationRunMutation |\n| **Feature not in REST** | Trying to use 2025+ features | Must migrate to GraphQL |\n| **Resource or version retirement** | A resource is unavailable or an API version is no longer supported | Track the version schedule and migrate to GraphQL before support ends |\n\n---\n\n## Critical Migration Checklist\n\nFor every maintained REST integration:\n\n- [ ] Audit all REST API calls in your codebase\n- [ ] Count requests per day (compare to GraphQL cost)\n- [ ] Create GraphQL equivalents for each REST endpoint\n- [ ] Test GraphQL mutations with real data\n- [ ] Replace REST calls one endpoint at a time\n- [ ] Monitor error rates during migration\n- [ ] Remove REST calls once GraphQL is stable\n- [ ] Set an internal GraphQL migration deadline based on the versions and resources you actually use\n\n---\n\n## Reference URLs\n\n- [Shopify Admin REST API (Legacy)](https://shopify.dev/docs/api/admin-rest/latest)\n- [API version schedule](https://shopify.dev/docs/api/usage/versioning)\n- [Migration guide: REST to GraphQL](https://shopify.dev/docs/apps/build/graphql/migrate)\n- [Shopify API limits](https://shopify.dev/docs/api/usage/limits)\n"
}

SHA-256 of public snapshot: 797bd3ceb31895cf0bbac4bd4b10d61259e0f7f35c65ff76dffce63550d606d2