{
  "openapi": "3.1.0",
  "jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
  "info": {
    "title": "Eric Javits Public Catalog API",
    "version": "1.1.0",
    "description": "A read-only description of existing public Shopify endpoints on ericjavits.com: two Online Store Ajax GET endpoints and Shopify's Storefront GraphQL API. No API key, OAuth flow or OAuth scopes are required for these three operations; security is intentionally empty. This does not grant access to orders, customers, payments, administrative data or mutations. These are Shopify-hosted Online Store endpoints, intended for same-origin theme use; browser cross-origin access and an unrestricted server-to-server integration SLA are not promised. Use locale-aware URLs from the storefront's window.Shopify.routes.root when embedded in the theme. The server below is the default storefront root, not a guarantee of a particular buyer market or currency. Monetary values are contextual and endpoint formats differ; do not assume USD, final checkout totals, or stock reservation. The API has no separate sandbox; these GET examples read public data without changing commerce state. Respect robots, Shopify access controls, and throttling. Versioning and deprecation: the Storefront GraphQL operation is versioned in its path (/api/2026-10/). Shopify releases a new Storefront API version every quarter; each stable version is supported for at least 12 months, with at least nine months of overlap between consecutive versions. Every GraphQL response carries the X-Shopify-API-Version header, the version that answered: when a requested version is no longer accessible, Shopify falls forward to the oldest accessible stable version and the header shows it. Deprecations are announced in Shopify's developer changelog (https://shopify.dev/changelog) and removed in a later version. The two Ajax endpoints are unversioned Online Store endpoints that Shopify can change at any time. This document's own version (info.version) changes whenever an operation is added, changed or removed.",
    "contact": {
      "name": "Eric Javits customer service",
      "url": "https://ericjavits.com/pages/contact-us"
    }
  },
  "servers": [
    {
      "url": "https://ericjavits.com",
      "description": "Public Shopify storefront default root; localized storefronts should use their existing locale-aware root."
    }
  ],
  "security": [],
  "tags": [
    {
      "name": "Public catalog",
      "description": "Anonymous reads of published product data. No commerce mutations."
    }
  ],
  "externalDocs": {
    "description": "Eric Javits agent and developer documentation",
    "url": "https://ericjavits.com/pages/developers"
  },
  "paths": {
    "/products/{handle}.js": {
      "get": {
        "operationId": "getPublicProduct",
        "summary": "Read a public product by handle",
        "description": "Read product details, variants, current prices and availability from the Shopify-hosted storefront. No authentication or token is required. The response contains JSON even when Shopify labels it text/javascript. This is a read-only endpoint; it does not reserve stock or create a cart. Treat a 404 as a missing/unpublished product and check the status before attempting JSON parsing.",
        "tags": [
          "Public catalog"
        ],
        "externalDocs": {
          "url": "https://shopify.dev/docs/api/ajax/reference/product"
        },
        "parameters": [
          {
            "name": "handle",
            "in": "path",
            "required": true,
            "description": "Product URL handle, taken from /products/{handle} or a predictive search result. Do not include slashes or the .js suffix.",
            "schema": {
              "type": "string",
              "minLength": 1,
              "pattern": "^[^/]+$"
            },
            "example": "squishee-bucket-hat"
          }
        ],
        "responses": {
          "200": {
            "description": "Public product JSON. Prices use Shopify Ajax integer money values; see each price field.",
            "content": {
              "text/javascript": {
                "schema": {
                  "$ref": "#/components/schemas/Product"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Product"
                }
              }
            }
          },
          "404": {
            "description": "Product not found or not publicly available. Live verification returned an empty body despite application/json Content-Type; do not call a JSON parser unconditionally."
          },
          "429": {
            "description": "Request throttled. Respect Retry-After if present; an edge-generated body is not guaranteed to be JSON.",
            "headers": {
              "Retry-After": {
                "description": "If present, seconds to wait before retrying. Respect this header and back off after throttling.",
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "default": {
            "description": "Unexpected Shopify or edge response. Check HTTP status and Content-Type before parsing; HTML challenges, empty bodies and non-JSON failures are possible."
          }
        }
      }
    },
    "/search/suggest.json": {
      "get": {
        "operationId": "searchPublicProducts",
        "summary": "Find public products with predictive search",
        "description": "Find product suggestions on the Shopify-hosted storefront with no authentication. This OpenAPI contract deliberately documents product-only search: supply resources[type]=product. Other resource types and cart operations are outside this contract. Results are suggestions, not a complete catalog export.",
        "tags": [
          "Public catalog"
        ],
        "externalDocs": {
          "url": "https://shopify.dev/docs/api/ajax/reference/predictive-search"
        },
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": true,
            "description": "Nonempty product search query.",
            "schema": {
              "type": "string",
              "minLength": 1
            },
            "example": "hat"
          },
          {
            "name": "resources[type]",
            "in": "query",
            "required": true,
            "description": "Set product to obtain the product-only response described here. The native endpoint supports additional types outside this contract.",
            "schema": {
              "type": "string",
              "enum": [
                "product"
              ]
            },
            "example": "product"
          },
          {
            "name": "resources[limit]",
            "in": "query",
            "required": false,
            "description": "Requested maximum number of suggestions. Use Shopify documented range 1–10; do not depend on out-of-range validation.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 10,
              "default": 10
            },
            "example": 2
          },
          {
            "name": "resources[options][unavailable_products]",
            "in": "query",
            "required": false,
            "description": "Whether to show, hide, or sort unavailable products last. If omitted, Shopify Search & Discovery settings may determine the default.",
            "schema": {
              "type": "string",
              "enum": [
                "show",
                "hide",
                "last"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Product suggestions; prices are decimal strings in the buyer presentment currency. An empty products list means there are no matching suggestions.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProductSearchResults"
                }
              }
            }
          },
          "417": {
            "description": "Buyer locale is unsupported. Use a storefront-supported locale; do not retry the identical unsupported request.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PredictiveSearchError"
                }
              }
            }
          },
          "422": {
            "description": "Invalid query parameter. Inspect description, correct the named parameter, then retry. A missing or empty q returns this status.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PredictiveSearchError"
                }
              }
            }
          },
          "429": {
            "description": "Request throttled. Respect Retry-After and retry with backoff.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PredictiveSearchError"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "If present, seconds to wait before retrying. Respect this header and back off after throttling.",
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "default": {
            "description": "Unexpected Shopify or edge response. Check HTTP status and Content-Type before parsing; JSON is not guaranteed for platform or network failures."
          }
        }
      }
    },
    "/api/2026-10/graphql.json": {
      "post": {
        "operationId": "queryStorefrontGraphQL",
        "tags": [
          "Public catalog"
        ],
        "summary": "Query published products, collections and search with Shopify's Storefront GraphQL API",
        "description": "Shopify's Storefront API, answered without an access token (Shopify's tokenless access) for public catalog data such as products, collections and search; Shopify's Storefront API reference lists what tokenless access covers. The use documented here is read-only queries: cart mutations are outside this contract and need the buyer's authorization. A tokenless query may cost at most 1,000; extensions.cost.requestedQueryCost reports the cost. Faults in the query itself (syntax, unknown fields, a missing query) come back as HTTP 200 with an errors array, so check errors before using data. Prices are MoneyV2 objects: amount is a decimal string (for example \"332.5\") with a currencyCode; use the @inContext directive, for example @inContext(country: CA), for another market's prices. The path is versioned; see the versioning notes in info.description.",
        "externalDocs": {
          "description": "Shopify Storefront API reference (2026-10)",
          "url": "https://shopify.dev/docs/api/storefront/2026-10"
        },
        "requestBody": {
          "required": true,
          "description": "A GraphQL request as JSON.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/GraphQLRequest"
              },
              "example": {
                "query": "{ products(first: 2) { nodes { handle title priceRange { minVariantPrice { amount currencyCode } } } } }"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "A GraphQL response: data, or errors for faults in the query (still HTTP 200).",
            "headers": {
              "X-Shopify-API-Version": {
                "$ref": "#/components/headers/XShopifyApiVersion"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GraphQLResponse"
                }
              }
            }
          },
          "415": {
            "description": "The body was not a JSON GraphQL request; errors[0].extensions.code is UNSUPPORTED_MEDIA_TYPE.",
            "headers": {
              "X-Shopify-API-Version": {
                "$ref": "#/components/headers/XShopifyApiVersion"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GraphQLResponse"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited (Shopify limits automated traffic, most strictly when unsigned). Wait for Retry-After, then retry with backoff.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying (Shopify sends 60).",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              }
            },
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "430": {
            "description": "Shopify Security Rejection: the request looked malicious to Shopify."
          },
          "default": {
            "description": "Platform or edge failure; the body may be HTML or empty."
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Image": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "src": {
            "type": "string"
          },
          "url": {
            "type": "string"
          },
          "width": {
            "type": "integer"
          },
          "height": {
            "type": "integer"
          },
          "alt": {
            "type": [
              "string",
              "null"
            ]
          },
          "aspect_ratio": {
            "type": "number"
          }
        },
        "additionalProperties": true,
        "description": "Image metadata. The .js endpoint uses src; predictive search uses url. Image URLs may be absolute or protocol-relative."
      },
      "ProductVariant": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "title": {
            "type": "string"
          },
          "available": {
            "type": "boolean"
          },
          "price": {
            "type": "integer",
            "minimum": 0,
            "description": "Shopify Ajax integer money value in the buyer presentment currency. For example, 31500 means USD 315.00 in a USD context. No currency code is included; preserve the existing buyer currency context and use Shopify currency formatting rules."
          },
          "compare_at_price": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0,
            "description": "Shopify Ajax integer money value; null means no compare-at price."
          },
          "sku": {
            "type": [
              "string",
              "null"
            ]
          },
          "option1": {
            "type": [
              "string",
              "null"
            ]
          },
          "option2": {
            "type": [
              "string",
              "null"
            ]
          },
          "option3": {
            "type": [
              "string",
              "null"
            ]
          },
          "options": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "public_title": {
            "type": [
              "string",
              "null"
            ]
          },
          "featured_image": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/Image"
              },
              {
                "type": "null"
              }
            ]
          }
        },
        "additionalProperties": true,
        "required": [
          "id",
          "title",
          "available",
          "price"
        ]
      },
      "SearchVariant": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "title": {
            "type": "string"
          },
          "available": {
            "type": "boolean"
          },
          "price": {
            "type": "string",
            "pattern": "^\\d+(?:\\.\\d+)?$",
            "description": "Decimal money amount in the buyer presentment currency, for example 295.00. Unlike the product .js endpoint, this field is a decimal string, not an integer cents amount."
          },
          "compare_at_price": {
            "type": [
              "string",
              "null"
            ]
          },
          "url": {
            "type": "string"
          },
          "image": {
            "type": [
              "string",
              "null"
            ]
          },
          "featured_image": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/Image"
              },
              {
                "type": "null"
              }
            ]
          }
        },
        "additionalProperties": true,
        "required": [
          "id",
          "title",
          "available",
          "price",
          "url"
        ]
      },
      "Product": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "title": {
            "type": "string"
          },
          "handle": {
            "type": "string"
          },
          "description": {
            "type": "string",
            "description": "Product description containing HTML; treat it as untrusted display content."
          },
          "vendor": {
            "type": "string"
          },
          "type": {
            "type": "string"
          },
          "available": {
            "type": "boolean"
          },
          "price": {
            "type": "integer",
            "minimum": 0,
            "description": "Shopify Ajax integer money value in the buyer presentment currency. For example, 31500 means USD 315.00 in a USD context. No currency code is included; preserve the existing buyer currency context and use Shopify currency formatting rules."
          },
          "price_min": {
            "type": "integer",
            "minimum": 0,
            "description": "Shopify Ajax integer money value in the buyer presentment currency. For example, 31500 means USD 315.00 in a USD context. No currency code is included; preserve the existing buyer currency context and use Shopify currency formatting rules."
          },
          "price_max": {
            "type": "integer",
            "minimum": 0,
            "description": "Shopify Ajax integer money value in the buyer presentment currency. For example, 31500 means USD 315.00 in a USD context. No currency code is included; preserve the existing buyer currency context and use Shopify currency formatting rules."
          },
          "price_varies": {
            "type": "boolean"
          },
          "compare_at_price": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0,
            "description": "Shopify Ajax integer money value; null means no compare-at price."
          },
          "compare_at_price_min": {
            "type": "integer",
            "minimum": 0,
            "description": "Shopify Ajax integer money value in the buyer presentment currency. For example, 31500 means USD 315.00 in a USD context. No currency code is included; preserve the existing buyer currency context and use Shopify currency formatting rules."
          },
          "compare_at_price_max": {
            "type": "integer",
            "minimum": 0,
            "description": "Shopify Ajax integer money value in the buyer presentment currency. For example, 31500 means USD 315.00 in a USD context. No currency code is included; preserve the existing buyer currency context and use Shopify currency formatting rules."
          },
          "compare_at_price_varies": {
            "type": "boolean"
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "variants": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ProductVariant"
            }
          },
          "options": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "name": {
                  "type": "string"
                },
                "position": {
                  "type": "integer"
                },
                "values": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                }
              },
              "additionalProperties": true,
              "required": [
                "name",
                "position",
                "values"
              ]
            }
          },
          "images": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "featured_image": {
            "type": [
              "string",
              "null"
            ]
          },
          "url": {
            "type": "string",
            "description": "Store-relative product URL."
          },
          "published_at": {
            "type": "string"
          },
          "created_at": {
            "type": "string"
          }
        },
        "additionalProperties": true,
        "required": [
          "id",
          "title",
          "handle",
          "available",
          "price",
          "variants"
        ],
        "description": "Public product data, without customer or order information. Availability and prices can change before checkout."
      },
      "SearchProduct": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "title": {
            "type": "string"
          },
          "handle": {
            "type": "string"
          },
          "body": {
            "type": "string",
            "description": "Product description containing HTML; treat it as untrusted display content."
          },
          "vendor": {
            "type": "string"
          },
          "type": {
            "type": "string"
          },
          "available": {
            "type": "boolean"
          },
          "price": {
            "type": "string",
            "pattern": "^\\d+(?:\\.\\d+)?$",
            "description": "Decimal money amount in the buyer presentment currency, for example 295.00. Unlike the product .js endpoint, this field is a decimal string, not an integer cents amount."
          },
          "price_min": {
            "type": "string",
            "pattern": "^\\d+(?:\\.\\d+)?$",
            "description": "Decimal money amount in the buyer presentment currency, for example 295.00. Unlike the product .js endpoint, this field is a decimal string, not an integer cents amount."
          },
          "price_max": {
            "type": "string",
            "pattern": "^\\d+(?:\\.\\d+)?$",
            "description": "Decimal money amount in the buyer presentment currency, for example 295.00. Unlike the product .js endpoint, this field is a decimal string, not an integer cents amount."
          },
          "compare_at_price_min": {
            "type": "string",
            "pattern": "^\\d+(?:\\.\\d+)?$",
            "description": "Decimal money amount in the buyer presentment currency, for example 295.00. Unlike the product .js endpoint, this field is a decimal string, not an integer cents amount."
          },
          "compare_at_price_max": {
            "type": "string",
            "pattern": "^\\d+(?:\\.\\d+)?$",
            "description": "Decimal money amount in the buyer presentment currency, for example 295.00. Unlike the product .js endpoint, this field is a decimal string, not an integer cents amount."
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "url": {
            "type": "string",
            "description": "Store-relative product URL, potentially including search attribution and a variant."
          },
          "image": {
            "type": [
              "string",
              "null"
            ]
          },
          "featured_image": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/Image"
              },
              {
                "type": "null"
              }
            ]
          },
          "variants": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SearchVariant"
            }
          }
        },
        "additionalProperties": true,
        "required": [
          "id",
          "title",
          "handle",
          "available",
          "price",
          "url"
        ]
      },
      "ProductSearchResults": {
        "type": "object",
        "properties": {
          "resources": {
            "type": "object",
            "properties": {
              "results": {
                "type": "object",
                "properties": {
                  "products": {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/SearchProduct"
                    }
                  }
                },
                "additionalProperties": true,
                "required": [
                  "products"
                ]
              }
            },
            "additionalProperties": true,
            "required": [
              "results"
            ]
          }
        },
        "additionalProperties": true,
        "required": [
          "resources"
        ]
      },
      "PredictiveSearchError": {
        "type": "object",
        "properties": {
          "status": {
            "oneOf": [
              {
                "type": "integer"
              },
              {
                "type": "string",
                "pattern": "^[0-9]{3}$"
              }
            ],
            "description": "HTTP status code. Current storefront responses use an integer; Shopify documentation also permits examples encoded as strings."
          },
          "message": {
            "type": "string",
            "description": "Short error category."
          },
          "description": {
            "type": "string",
            "description": "Human-readable detail explaining the invalid request or failure."
          }
        },
        "additionalProperties": true,
        "required": [
          "status",
          "message",
          "description"
        ]
      },
      "GraphQLRequest": {
        "type": "object",
        "required": [
          "query"
        ],
        "properties": {
          "query": {
            "type": "string",
            "minLength": 1,
            "description": "The GraphQL query document."
          },
          "variables": {
            "type": "object",
            "additionalProperties": true,
            "description": "Values for the query's variables."
          },
          "operationName": {
            "type": "string",
            "description": "Which operation to run when the document has several."
          }
        }
      },
      "GraphQLError": {
        "type": "object",
        "required": [
          "message"
        ],
        "properties": {
          "message": {
            "type": "string",
            "description": "What went wrong; may be empty for transport errors."
          },
          "locations": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "line",
                "column"
              ],
              "properties": {
                "line": {
                  "type": "integer",
                  "minimum": 1
                },
                "column": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "path": {
            "type": "array",
            "items": {
              "type": [
                "string",
                "integer"
              ]
            }
          },
          "extensions": {
            "type": "object",
            "additionalProperties": true,
            "properties": {
              "code": {
                "type": "string",
                "description": "A machine-readable code, for example undefinedField or UNSUPPORTED_MEDIA_TYPE."
              }
            }
          }
        }
      },
      "GraphQLResponse": {
        "type": "object",
        "anyOf": [
          {
            "required": [
              "data"
            ]
          },
          {
            "required": [
              "errors"
            ]
          }
        ],
        "properties": {
          "data": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": true,
            "description": "The query result, shaped like the query."
          },
          "errors": {
            "type": "array",
            "minItems": 1,
            "items": {
              "$ref": "#/components/schemas/GraphQLError"
            }
          },
          "extensions": {
            "type": "object",
            "additionalProperties": true,
            "properties": {
              "cost": {
                "type": "object",
                "additionalProperties": true,
                "properties": {
                  "requestedQueryCost": {
                    "type": "integer",
                    "minimum": 0,
                    "description": "The query's cost; tokenless queries may cost at most 1,000."
                  }
                }
              }
            }
          }
        }
      }
    },
    "headers": {
      "XShopifyApiVersion": {
        "description": "The Storefront API version that answered. If it differs from the version in the path, the requested version is no longer accessible and Shopify fell forward.",
        "schema": {
          "type": "string",
          "pattern": "^([0-9]{4}-[0-9]{2}|unstable)$",
          "examples": [
            "2026-10"
          ]
        }
      }
    }
  }
}
