Is it possible to call an external API through shopify functions?

Topic summary

Core Issue:
A developer is attempting to call an external API from within a Shopify Function to retrieve bundle product data but is not receiving any response. The code includes an async fetch() call to a third-party endpoint.

Key Limitation Identified:
Shopify Functions cannot make external API calls due to performance constraints. This is a fundamental architectural restriction of the platform.

Proposed Workaround:
Instead of fetching bundle configuration data from an external server at runtime, the suggestion is to store this information as metafields directly in Shopify. This would allow the Function to access the necessary bundle data without requiring external network requests.

Current Status:
The discussion remains open with the developer considering alternative approaches to implement their bundle discount logic within Shopify’s Function constraints.

Summarized with AI on November 7. AI used: claude-sonnet-4-5-20250929.

I am trying to access a third-party API (one we have created) from a Shopify function. But it does not seem to be returning data. Can anyone see what I could be doing wrong. Is this not possible with functions?

// -check
import { DiscountApplicationStrategy } from "../generated/api";

/**
 * @typedef {import("../generated/api").RunInput} RunInput
 * @typedef {import("../generated/api").FunctionRunResult} FunctionRunResult
 */

/**
 *  {FunctionRunResult}
 */
const EMPTY_DISCOUNT = {
  discountApplicationStrategy: DiscountApplicationStrategy.First,
  discounts: [],
};

function getNumericId(id){
  const parts = id.split('/')
  return parts[parts.length - 1];
}

async function getBundleData(name){

     const d = await fetch('https://our_bundle_url.com/getByName?name='+encodeURIComponent(name));
      const j = await d.json();

return j;

}

/**
 * @param {RunInput} input
 * @returns {FunctionRunResult}
 */
export function run(input) {
  const lines_length = input.cart.lines.length;
  
  let count_of_products = {};
  let line_items_of_products = {};
  let qty_price_breaks = {};

  let bundles_cost = {}
  let bundles_line_items = {};
  let bundles_total_cost = {};
  let bundle_products = {};
  let bundle_ratio = {};

  for(let i = 0; i < lines_length; i++){
    if(input.cart.lines[i].Bundle !== null && input.cart.lines[i].Bundle.value.length > 0){

      //do bundle discounting

      const data = getBundleData(input.cart.lines[i].Bundle.value);

      if(typeof data['bundle'] !== 'undefined'){

      const bundle_parts = [encodeURIComponent(input.cart.lines[i].Bundle.value).replace(/%/g, ''), data['bundle']['price'], data['bundle']['product_ids'].join(',')]
        

      if(typeof bundle_products[bundle_parts[0]] === 'undefined'){
        bundle_products[bundle_parts[0]] = {};

        const ids = bundle_parts[2].split(',');
        const id_length = ids.length

        for(let j = 0; j < id_length; j++){
          bundle_products[bundle_parts[0]][ids[j]] = 0;

          if(typeof bundle_ratio[bundle_parts[0]] === 'undefined'){
            bundle_ratio[bundle_parts[0]] = {};
          }

          if(typeof bundle_ratio[bundle_parts[0]][ids[j]] === 'undefined'){
            bundle_ratio[bundle_parts[0]][ids[j]] = 0;
          }
        }

        for(let j = 0; j < id_length; j++){

          bundle_ratio[bundle_parts[0]][ids[j]] += input.cart.lines[i].quantity;

        }

      }

      bundle_products[bundle_parts[0]][getNumericId(input.cart.lines[i].merchandise.product.id)] += input.cart.lines[i].quantity;

      if(typeof bundles_cost[bundle_parts[0]] === 'undefined'){
        bundles_cost[bundle_parts[0]] = 0;
      }
      if(typeof bundles_line_items[bundle_parts[0]] === 'undefined'){
        bundles_line_items[bundle_parts[0]] = [];
      }
      bundles_cost[bundle_parts[0]] += (1* input.cart.lines[i].cost.subtotalAmount.amount)
      bundles_total_cost[bundle_parts[0]] = bundle_parts[1] * input.cart.lines[i].quantity;
      bundles_line_items[bundle_parts[0]].push(input.cart.lines[i].id)
      }
    } else {
      //triers and other discounts
    }
  }

  let discounts = [];

  for(let i in bundle_products) {
    if (bundle_products.hasOwnProperty(i)) {
      let count_ok = true;
      let count = null;

      for (let j in bundle_products[i]) {
        if (bundle_products[i].hasOwnProperty(j)) {
          if (count === null) {
            count = bundle_products[i][j] / bundle_ratio[i][j];
          }
          if (count !== (bundle_products[i][j] / bundle_ratio[i][j])) {
            count_ok = false;
          }
        }
      }

      if (count_ok) {
        let discount = (100 - ((bundles_total_cost[i] / bundles_cost[i]) * 100));
        let cartLines = [];
        const len = bundles_line_items[i].length
        for (let k = 0; k < len; k++) {

          cartLines.push({
            // Use the variant ID to create a discount target
            cartLine: {
              id: bundles_line_items[i][k]
            }
          })
        }

        discounts.push({
          // Apply the discount to the collected targets
          targets: cartLines,
          // Define a percentage-based discount
          value: {
            percentage: {
              value: discount
            }
          }
        })
      }
    }
  }

  if(discounts.length === 0){
    return EMPTY_DISCOUNT;
  } else {

    return {
      discounts: discounts,
      discountApplicationStrategy: DiscountApplicationStrategy.All
    };
  }

and

In shopify.extension.toml

[extensions.capabilities]
  network_access = true

Hi FutureAndAHope - It’s not possible to make an external call inside a Function for performance reasons. What is the exact use case you are trying to achieve?

We are trying to retain our existing bundle packs. The API call is information about what a bundle pack consists of. We store this information on an external server.

Could you store this info as a metafield instead?