Grand Exchange API

Category: API Reference

Access and manage Grand Exchange offers for buying and selling items.

Core Functions

offers

grand_exchange:offers() -> table

Returns all Grand Exchange offers (slots 0-7 for members, 0-2 for free-to-play).

Returns:

  • table - Array of GrandExchangeOffer objects

Example:

local all_offers = grand_exchange:offers()
logger:info("Total offers: " .. #all_offers)

for _, offer in ipairs(all_offers) do
    if offer:valid() and not offer:is_available() then
        logger:info("Slot " .. offer:index() .. ": " .. offer:name())
    end
end

offer

grand_exchange:offer(index: number) -> GrandExchangeOffer?

Gets a specific offer by slot index.

Parameters:

ParameterTypeRequiredDescription
indexnumberYesSlot index (0-7 for members, 0-2 for free-to-play)

Returns:

  • GrandExchangeOffer? - The offer at the specified slot, or nil if invalid

Example:

local offer = grand_exchange:offer(0)
if offer and offer:valid() then
    logger:info("Slot 0: " .. offer:name())
end

opened

grand_exchange:opened() -> boolean

Checks if the Grand Exchange interface is open.

Returns:

  • boolean - True if the Grand Exchange interface is visible

Example:

if grand_exchange:opened() then
    logger:info("Grand Exchange is open")
end

open

grand_exchange:open() -> boolean

Opens the Grand Exchange interface by finding and interacting with a Grand Exchange Clerk. If no clerk is nearby, automatically navigates to the GE area using available teleports (Ring of Wealth, etc.).

Returns:

  • boolean - True if the interface was opened successfully

Example:

if not grand_exchange:opened() then
    if grand_exchange:open() then
        logger:info("Grand Exchange opened successfully")
    else
        logger:warn("Failed to open Grand Exchange")
    end
end

close

grand_exchange:close() -> boolean

Closes the Grand Exchange interface.

Returns:

  • boolean - True if the interface was closed successfully

Example:

if grand_exchange:opened() then
    grand_exchange:close()
end

available_slot

grand_exchange:available_slot() -> GrandExchangeOffer?

Gets the first available (empty) offer slot.

Returns:

  • GrandExchangeOffer? - The first empty slot, or nil if all slots are full

Example:

local slot = grand_exchange:available_slot()
if slot then
    logger:info("Empty slot found: " .. slot:index())
else
    logger:warn("All slots are full")
end

active_offers

grand_exchange:active_offers() -> table

Gets all active (in progress) offers.

Returns:

  • table - Array of GrandExchangeOffer objects that are currently in progress

Example:

local active = grand_exchange:active_offers()
logger:info("Active offers: " .. #active)

for _, offer in ipairs(active) do
    local progress = offer:current_quantity() .. "/" .. offer:total_quantity()
    logger:info("Active: " .. offer:name() .. " (" .. progress .. ")")
end

completed_offers

grand_exchange:completed_offers() -> table

Gets all completed offers ready to collect.

Returns:

  • table - Array of GrandExchangeOffer objects that are complete

Example:

local completed = grand_exchange:completed_offers()
logger:info("Completed offers: " .. #completed)

for _, offer in ipairs(completed) do
    logger:info("Ready to collect: " .. offer:name())
end

aborted_offers

grand_exchange:aborted_offers() -> table

Gets all aborted/cancelled offers.

Returns:

  • table - Array of GrandExchangeOffer objects that were aborted

Example:

local aborted = grand_exchange:aborted_offers()
for _, offer in ipairs(aborted) do
    logger:info("Aborted: " .. offer:name())
end

has_completed_offers

grand_exchange:has_completed_offers() -> boolean

Checks if there are any completed or aborted offers ready to collect.

Returns:

  • boolean - True if there are offers ready to collect

Example:

if grand_exchange:has_completed_offers() then
    logger:info("Collecting completed offers...")
    grand_exchange:collect_all_to_bank()
end

view_offer

grand_exchange:view_offer(index: number) -> boolean

Opens the details view for an offer.

Parameters:

ParameterTypeRequiredDescription
indexnumberYesSlot index (0-7)

Returns:

  • boolean - True if the offer details were opened successfully

Example:

local offer = grand_exchange:offer(0)
if offer and offer:is_in_progress() then
    grand_exchange:view_offer(0)
end

abort_offer

grand_exchange:abort_offer(index: number) -> boolean

Aborts/cancels an offer.

Parameters:

ParameterTypeRequiredDescription
indexnumberYesSlot index (0-7)

Returns:

  • boolean - True if the offer was aborted successfully

Example:

local offer = grand_exchange:offer(0)
if offer and offer:is_in_progress() then
    logger:info("Aborting offer: " .. offer:name())
    grand_exchange:abort_offer(0)
end

collect_offer

grand_exchange:collect_offer(index: number) -> boolean

Collects items from a completed or aborted offer to inventory.

Parameters:

ParameterTypeRequiredDescription
indexnumberYesSlot index (0-7)

Returns:

  • boolean - True if the offer was collected successfully

Example:

local offer = grand_exchange:offer(0)
if offer and offer:is_complete() then
    logger:info("Collecting: " .. offer:name())
    grand_exchange:collect_offer(0)
end

buy

grand_exchange:buy(filter: ItemFilter, opts: table) -> GrandExchangeOffer?

Buys an item from the Grand Exchange using item definition filters. Automatically opens the interface, searches for the item, sets quantity and price, and confirms the offer.

Parameters:

ParameterTypeRequiredDescription
filterItemFilter or tableYesItem filter to find the item to buy (e.g., {id = 440} or {name = "Iron ore"})
optstableYesOptions table with amount (number) - The quantity to buy, and price (number) - The price per item in gp

Options (opts table):

  • amount (number) - The quantity to buy
  • price (number) - The price per item in gp

Returns:

  • GrandExchangeOffer? - The offer slot if successful, nil otherwise

Example:

-- Buy 1000 iron ore at 200 gp each using item ID filter
local offer = grand_exchange:buy({id = 440}, {amount = 1000, price = 200})
if offer then
    logger:info("Buy offer placed in slot " .. offer:index())
else
    logger:warn("Failed to place buy offer")
end

-- Buy using item name filter
local offer = grand_exchange:buy({name = "Iron ore"}, {amount = 1000, price = 200})

sell

grand_exchange:sell(filter: ItemFilter, opts: table) -> GrandExchangeOffer?

Sells an item to the Grand Exchange using item filters. The item must be in your inventory. Automatically opens the interface, finds the item in inventory, sets quantity and price, and confirms the offer.

Parameters:

ParameterTypeRequiredDescription
filterItemFilter or tableYesItem filter to find the item to sell (e.g., {id = 440} or {name = "Iron ore"})
optstableYesOptions table with amount (number, optional) - The quantity to sell (default: sell all), price (number, optional) - The price per item in gp (default: use guide price), and price_reduction_percent (number, optional) - Price reduction percentage from guide price (e.g., 5.0 = 5% reduction)

Options (opts table):

  • amount (number, optional) - The quantity to sell (default: sell all)
  • price (number, optional) - The price per item in gp (default: use guide price)
  • price_reduction_percent (number, optional) - Price reduction percentage from guide price (e.g., 5.0 = 5% reduction)

Returns:

  • GrandExchangeOffer? - The offer slot if successful, nil otherwise

Example:

-- Sell 1000 iron ore at 200 gp each using item ID filter
local offer = grand_exchange:sell({id = 440}, {amount = 1000, price = 200})
if offer then
    logger:info("Sell offer placed in slot " .. offer:index())
else
    logger:warn("Failed to place sell offer")
end

-- Sell using item name filter with price reduction
local offer = grand_exchange:sell({name = "Iron ore"}, {price_reduction_percent = 5.0})

collect_all_to_inventory

grand_exchange:collect_all_to_inventory() -> boolean

Collects all completed and aborted offers to inventory.

Returns:

  • boolean - True if collection was successful

Example:

if grand_exchange:has_completed_offers() then
    logger:info("Collecting all offers to inventory...")
    grand_exchange:collect_all_to_inventory()
end

collect_all_to_bank

grand_exchange:collect_all_to_bank() -> boolean

Collects all completed and aborted offers directly to bank.

Returns:

  • boolean - True if collection was successful

Example:

if grand_exchange:has_completed_offers() then
    logger:info("Collecting all offers to bank...")
    grand_exchange:collect_all_to_bank()
end

sell_all

grand_exchange:sell_all(items: table) -> boolean

Sells items matching filters in a loop. For each item:

  • Finds the first matching item from inventory (uses same ItemFilter as inventory API)
  • Collects to bank if any completed offers
  • Waits if all slots are full
  • Sells item with specified amount and price
  • Continues until no more items match
  • Waits for all placed offers to complete at the end

Parameters:

ParameterTypeRequiredDescription
itemstableYesAn array of tables, each with filter (ItemFilter) and optional opts table

Options (opts table):

  • amount (number, optional) - Quantity to sell per item (default: sell all)
  • price (number, optional) - Fixed price per item (default: use guide price)
  • price_reduction_percent (number, optional) - Price reduction percentage from guide price (e.g., 5.0 = 5% reduction)

Returns:

  • boolean - True if all items were processed successfully

Example:

-- Sell multiple item types
grand_exchange:sell_all({
    {
        filter = {name = "Iron ore"},
        opts = {amount = 1000, price = 200}
    },
    {
        filter = {name = "Coal"},
        opts = {price_reduction_percent = 3.0}
    },
    {
        filter = {id = 440}  -- Iron ore by ID
    }
})

-- Sell a single item type (still use array)
grand_exchange:sell_all({
    {
        filter = {name_contains = "ore"},
        opts = {
            price_reduction_percent = 5.0  -- Sell at 5% below guide price
        }
    }
})

Common Patterns

Basic Buy and Sell

-- Open Grand Exchange
if not grand_exchange:opened() then
    grand_exchange:open()
    wait:until(function() return grand_exchange:opened() end, 3000)
end

-- Buy items
local buy_offer = grand_exchange:buy({id = 440}, {amount = 1000, price = 200})  -- Iron ore
if buy_offer then
    logger:info("Buy offer placed in slot " .. buy_offer:index())
end

-- Sell items (must be in inventory)
local sell_offer = grand_exchange:sell({id = 453}, {amount = 500, price = 300})  -- Coal
if sell_offer then
    logger:info("Sell offer placed in slot " .. sell_offer:index())
end

Monitoring All Offers

local function check_all_offers()
    local offers = grand_exchange:offers()
    
    for _, offer in ipairs(offers) do
        if offer:valid() then
            if offer:is_available() then
                logger:info("Slot " .. offer:index() .. ": [Empty]")
            elseif offer:is_complete() then
                logger:info("Slot " .. offer:index() .. ": " .. offer:name() .. " - COMPLETE")
            elseif offer:is_in_progress() then
                local current = offer:current_quantity()
                local total = offer:total_quantity()
                local percent = math.floor((current / total) * 100)
                logger:info("Slot " .. offer:index() .. ": " .. offer:name() .. " - " .. percent .. "%")
            end
        end
    end
end

check_all_offers()

Auto-Collect Completed Offers

local function auto_collect()
    if grand_exchange:has_completed_offers() then
        logger:info("Collecting completed offers...")
        
        -- Collect to bank (recommended for large quantities)
        if grand_exchange:collect_all_to_bank() then
            logger:info("All offers collected to bank")
        else
            logger:warn("Failed to collect offers")
        end
    end
end

-- Run periodically
while script_running() do
    auto_collect()
    sleep(5000)  -- Check every 5 seconds
end

Wait for Offer Completion

local function wait_for_offer_completion(offer_index, timeout_ms)
    local start = os.time()
    timeout_ms = timeout_ms or 300000  -- Default 5 minutes
    
    while (os.time() - start) * 1000 < timeout_ms do
        local offer = grand_exchange:offer(offer_index)
        
        if not offer or not offer:valid() then
            logger:warn("Offer became invalid")
            return false
        end
        
        if offer:is_complete() then
            logger:info("Offer completed!")
            return true
        end
        
        if offer:is_aborted() then
            logger:warn("Offer was cancelled")
            return false
        end
        
        sleep(1000)  -- Check every second
    end
    
    logger:warn("Offer timed out")
    return false
end

-- Place offer and wait
local offer = grand_exchange:buy({id = 440}, {amount = 1000, price = 200})
if offer then
    wait_for_offer_completion(offer:index(), 300000)
end

Bulk Selling with Filters

-- Sell all ores at 5% below guide price
grand_exchange:sell_all({
    {
        filter = {name_contains = "ore"},
        opts = {
            price_reduction_percent = 5.0
        }
    }
})

-- Sell multiple specific items
grand_exchange:sell_all({
    {
        filter = {name = "Iron ore"},
        opts = {amount = 1000}
    },
    {
        filter = {name = "Coal"},
        opts = {amount = 500}
    },
    {
        filter = {id = 447},  -- Mithril ore
        opts = {price = 500}
    }
})

Managing Full Slots

local function wait_for_slot(timeout_ms)
    timeout_ms = timeout_ms or 30000  -- Default 30 seconds
    
    local start = os.time()
    while (os.time() - start) * 1000 < timeout_ms do
        if grand_exchange:available_slot() then
            return true
        end
        sleep(1000)
    end
    
    return false
end

-- Check before placing offer
if not grand_exchange:available_slot() then
    logger:info("All slots full, waiting...")
    if wait_for_slot(30000) then
        logger:info("Slot available!")
    else
        logger:warn("Timeout waiting for slot")
    end
end

Abort Slow Offers

local function abort_slow_offers(min_progress_percent)
    min_progress_percent = min_progress_percent or 10
    
    local active = grand_exchange:active_offers()
    
    for _, offer in ipairs(active) do
        local current = offer:current_quantity()
        local total = offer:total_quantity()
        local percent = (current / total) * 100
        
        if percent < min_progress_percent then
            logger:info("Aborting slow offer: " .. offer:name() .. " (" .. percent .. "%)")
            grand_exchange:abort_offer(offer:index())
            sleep(600)
        end
    end
end

-- Abort offers with less than 10% progress
abort_slow_offers(10)

Complete Trading Loop

local function trade_loop()
    -- 1. Collect any completed offers
    if grand_exchange:has_completed_offers() then
        grand_exchange:collect_all_to_bank()
    end
    
    -- 2. Check for available slot
    if not grand_exchange:available_slot() then
        logger:info("All slots full, waiting...")
        return
    end
    
    -- 3. Open GE if needed
    if not grand_exchange:opened() then
        grand_exchange:open()
        wait:until(function() return grand_exchange:opened() end, 3000)
    end
    
    -- 4. Place buy offer
    local offer = grand_exchange:buy({id = 440}, {amount = 1000, price = 200})
    if offer then
        logger:info("Buy offer placed in slot " .. offer:index())
    end
end

while script_running() do
    trade_loop()
    sleep(10000)  -- Run every 10 seconds
end

GrandExchangeOffer Methods

Each GrandExchangeOffer object has methods to access offer properties:

  • Basic Info

    • index() → number
    • item_id() → number
    • name() → string
    • unit_price() → number
    • total_quantity() → number
    • current_quantity() → number
  • State Checks

    • valid() → boolean
    • is_available() → boolean
    • is_sell() → boolean
    • is_in_progress() → boolean
    • is_complete() → boolean
    • is_aborted() → boolean
    • state() → string

See GrandExchangeOffer type documentation for complete method list.


Related APIs