EzyStudio ADF

Buy X Get Y

Overview

The Buy X Get Y function implements the classic promotional pattern: “Buy a certain quantity of items, and get other items discounted or free.” The buy items and get items can be the same products or entirely different products, enabling a wide range of promotional strategies.

  • Discount Classes: Product, Shipping
  • Billing: Starter plan and above
  • Condition Support: Cart-level only — customerTag, cartSubtotal, cartTotalQuantity, market

Buy X Get Y supports Product and Shipping discount classes. The function’s logic is centered around counting qualifying “buy” items, calculating how many “get” items earn a discount, and allocating that discount starting from the cheapest eligible items.


Key Concepts

ConceptDescription
buyQuantityThe number of items the customer must purchase to form one “set.”
getQuantityThe number of items that receive a discount per set.
maxSetsThe maximum number of sets the function will apply. Set to 0 for unlimited.
buyItemFilterDefines which products count as “buy” items. Uses filter types like all, collection, or productTag.
getItemFilterDefines which products are eligible as “get” items. Same filter types as buy.
Cheapest-first allocationGet items are sorted by price in ascending order. The discount is applied starting from the cheapest qualifying item. This ensures the merchant gives away the least expensive items first.
Buy/Get overlapThe same physical items in the cart can serve as both “buy” and “get” items. For example, in a “Buy 2 Get 1 Free” offer where all products qualify, a cart of 3 identical items uses 2 as the buy and 1 (the third) as the get.

Real-Life Use Cases

1. Buy 2 Get 1 Free (Same Product)

The most common BOGO variant. All products in the store qualify, and the cheapest item in the cart is the one discounted.

  • buyQuantity: 2
  • getQuantity: 1
  • buyItemFilter: all
  • getItemFilter: all
  • getDiscount: Percentage, 100%
  • maxSets: 0 (unlimited)

A cart with 6 items gets 2 free items (2 sets of “Buy 2 Get 1”).

2. Buy 3 Coffees Get 1 Pastry 50% Off

A cross-category promotion where the buy and get items come from different product groups. This drives discovery by encouraging customers to try a new category.

  • buyQuantity: 3
  • buyItemFilter: Collection “Coffee”
  • getQuantity: 1
  • getItemFilter: Product tag “pastry”
  • getDiscount: Percentage, 50%
  • maxSets: 1

3. Buy Laptop Get Case Free

A targeted accessory promotion tied to a high-value purchase. The buy filter targets a specific product or collection, and the get filter targets the accessory.

  • buyQuantity: 1
  • buyItemFilter: Collection “Laptops”
  • getQuantity: 1
  • getItemFilter: Collection “Laptop Cases”
  • getDiscount: Percentage, 100%
  • maxSets: 1

4. Buy 2 Get 1 Free (Max 3 Sets)

Same as use case 1, but capped at 3 sets to limit exposure. A cart with 20 items would still only get 3 free items.

  • buyQuantity: 2
  • getQuantity: 1
  • maxSets: 3

Algorithm

The Buy X Get Y function follows a precise allocation algorithm:

Step 1: Filter Buy Lines

Evaluate each cart line against buyItemFilter. Sum the quantities of all matching lines to get the total buy quantity.

Step 2: Calculate Sets

sets = floor(total_buy_qty / buyQuantity)

If maxSets > 0, cap the result:

sets = min(sets, maxSets)

Step 3: Filter Get Lines

Evaluate each cart line against getItemFilter. Collect all matching lines.

Step 4: Sort by Price Ascending

Sort the eligible get lines by their unit price in ascending order. This is the cheapest-first allocation strategy.

Step 5: Allocate Discounts

Calculate the total quantity to discount:

total_to_discount = sets * getQuantity

Walk through the sorted get lines from cheapest to most expensive. For each line, allocate up to the line’s available quantity (or the remaining total_to_discount, whichever is smaller). Subtract the allocated amount from total_to_discount and move to the next line. Stop when total_to_discount reaches 0.

Walkthrough Example

Offer: Buy 2 Get 1 Free, all products, unlimited sets.

Cart contents:

ItemPriceQuantity
Socks$52
T-Shirt$203
Jacket$801

Step 1: All items match the buy filter. Total buy quantity = 2 + 3 + 1 = 6.

Step 2: Sets = floor(6 / 2) = 3. maxSets = 0 (unlimited), so sets remain 3.

Step 3: All items also match the get filter.

Step 4: Sort by price ascending: Socks ($5), T-Shirt ($20), Jacket ($80).

Step 5: Total to discount = 3 * 1 = 3.

  • Socks: allocate min(2, 3) = 2 units discounted. Remaining = 1.
  • T-Shirt: allocate min(3, 1) = 1 unit discounted. Remaining = 0. Stop.

Result: 2 Socks and 1 T-Shirt receive the 100% discount (free). The customer saves $30 ($5 + $5 + $20).


Configuration Example: Buy 2 Get 1 Free

{
  "version": "1.0",
  "strategy": "first",
  "ruleGroups": [
    {
      "id": "rg_001",
      "name": "Buy 2 Get 1 Free",
      "enabled": true,
      "conditionLogic": "and",
      "conditions": [],
      "buyQuantity": 2,
      "getQuantity": 1,
      "maxSets": 0,
      "buyItemFilter": { "filterType": "all" },
      "getItemFilter": { "filterType": "all" },
      "getDiscount": {
        "type": "percentage",
        "value": 100,
        "message": "Buy 2 Get 1 FREE"
      }
    }
  ],
  "rejectionRules": []
}

Key Fields

FieldPurpose
buyQuantityNumber of items required to form one buy set.
getQuantityNumber of items discounted per set.
maxSetsMaximum number of sets allowed. 0 means unlimited.
buyItemFilterFilter object determining which cart lines count as buy items.
getItemFilterFilter object determining which cart lines are eligible for the get discount.
getDiscountThe discount applied to get items. Supports percentage and fixedAmount types.
filterTypeFilter mode: "all" (every product qualifies), "collection" (match by collection ID), or "productTag" (match by product tag).

Cross-Category Example

Buy 3 items from the Coffee collection, get 1 pastry-tagged item at 50% off, limited to 1 set per cart.

{
  "version": "1.0",
  "strategy": "first",
  "collectionIds": ["gid://shopify/Collection/coffee"],
  "productTags": ["pastry"],
  "ruleGroups": [
    {
      "id": "rg_001",
      "name": "Buy 3 Coffees Get 1 Pastry 50% Off",
      "enabled": true,
      "conditionLogic": "and",
      "conditions": [],
      "buyQuantity": 3,
      "getQuantity": 1,
      "maxSets": 1,
      "buyItemFilter": {
        "filterType": "collection",
        "collectionIds": ["gid://shopify/Collection/coffee"]
      },
      "getItemFilter": {
        "filterType": "productTag",
        "tags": ["pastry"]
      },
      "getDiscount": {
        "type": "percentage",
        "value": 50,
        "message": "Buy 3 Coffees, Get a Pastry 50% OFF"
      }
    }
  ],
  "rejectionRules": []
}

In this configuration, the collectionIds and productTags arrays at the top level tell the Shopify input query which collections and tags the function needs access to. The buyItemFilter and getItemFilter inside the rule group define the actual matching logic.


Conditional Buy X Get Y

You can add cart-level conditions to restrict who qualifies for the offer. For example, to limit a BOGO deal to customers tagged “member”:

{
  "conditions": [
    {
      "type": "customerTag",
      "operator": "hasAny",
      "tags": ["member"]
    }
  ]
}

Add this conditions array to the rule group alongside the buy/get configuration. Only customers with the “member” tag will see the discount applied.


Next Steps