Skip to main content
GET
/
v2
/
products

List Products

Returns a paginated list of products, ordered by creation date (most recent first). Supports filtering by status, eligibility, and more.

Authentication

Authorization: Bearer glm_test_YOUR_API_KEY

Query Parameters

ParameterTypeDescription
limitintegerNumber of results per page (default: 10, max: 100)
starting_afterintegerCursor for pagination (product ID)
statusenumFilter by status: active, inactive, archived
hsa_fsa_eligiblebooleanFilter by HSA/FSA eligibility (true/false)
merchant_product_idstringFilter by your product ID
created_aftertimestampFilter products created after this date
created_beforetimestampFilter products created before this date

Request

GET /v2/products?limit=10&status=active

Response

{
  "data": [
    {
      "id": 12345,
      "name": "Digital Blood Pressure Monitor",
      "tagline": "FDA-approved automatic BP monitor",
      "price": {
        "cents": 4995,
        "dollars": 49.95,
        "currency": "USD",
        "formatted": "$49.95"
      },
      "merchant_product_id": "BP-MONITOR-001",
      "status": "active",
      "eligibility": {
        "hsa_fsa_eligible": true,
        "eligibility_type": "auto_substantiation"
      },
      "images": [
        {
          "id": 567,
          "url": "https://cdn.example.com/bp-monitor.jpg",
          "is_primary": true
        }
      ],
      "created_at": "2025-10-18T14:30:00Z"
    },
    {
      "id": 12346,
      "name": "Digital Thermometer",
      "price": {
        "cents": 2995,
        "dollars": 29.95,
        "currency": "USD",
        "formatted": "$29.95"
      },
      "merchant_product_id": "THERM-001",
      "status": "active",
      "eligibility": {
        "hsa_fsa_eligible": true,
        "eligibility_type": "auto_substantiation"
      },
      "created_at": "2025-10-17T10:00:00Z"
    }
  ],
  "has_more": true,
  "next_cursor": 12346
}

Response Fields

FieldTypeDescription
dataarrayArray of product objects
has_morebooleanWhether more results are available
next_cursorintegerCursor for next page (use as starting_after)

Examples

List All Products

curl https://api.withgale.com/v2/products \
  -H "Authorization: Bearer glm_test_YOUR_API_KEY"

Filter by Status

curl "https://api.withgale.com/v2/products?status=active&limit=50" \
  -H "Authorization: Bearer glm_test_YOUR_API_KEY"

Filter by HSA/FSA Eligibility

curl "https://api.withgale.com/v2/products?hsa_fsa_eligible=true" \
  -H "Authorization: Bearer glm_test_YOUR_API_KEY"

Pagination

const getAllProducts = async () => {
  let allProducts = [];
  let hasMore = true;
  let cursor = null;

  while (hasMore) {
    const params = new URLSearchParams({
      limit: '100',
      ...(cursor && { starting_after: cursor })
    });

    const response = await fetch(
      `https://api.withgale.com/v2/products?${params}`,
      {
        headers: {
          'Authorization': `Bearer ${process.env.GALE_API_KEY}`
        }
      }
    );

    const data = await response.json();
    allProducts = allProducts.concat(data.data);
    hasMore = data.has_more;
    cursor = data.next_cursor;
  }

  return allProducts;
};

Filter by Date Range

// Get products created in the last 30 days
const getRecentProducts = async () => {
  const thirtyDaysAgo = new Date();
  thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30);

  const params = new URLSearchParams({
    created_after: thirtyDaysAgo.toISOString(),
    status: 'active',
    limit: '100'
  });

  const response = await fetch(
    `https://api.withgale.com/v2/products?${params}`,
    {
      headers: {
        'Authorization': `Bearer ${process.env.GALE_API_KEY}`
      }
    }
  );

  return await response.json();
};

Use Cases

Product Catalog Display

// Fetch all active HSA/FSA eligible products
const getHSAProducts = async () => {
  const params = new URLSearchParams({
    status: 'active',
    hsa_fsa_eligible: 'true',
    limit: '100'
  });

  const response = await fetch(
    `https://api.withgale.com/v2/products?${params}`,
    {
      headers: {
        'Authorization': `Bearer ${process.env.GALE_API_KEY}`
      }
    }
  );

  const { data } = await response.json();

  return data.map(product => ({
    id: product.id,
    name: product.name,
    price: product.price.formatted,
    image: product.images[0]?.url,
    eligibilityBadge: 'HSA/FSA Eligible'
  }));
};

Sync to E-commerce Platform

// Sync all Gale products to your platform
const syncAllProducts = async () => {
  let cursor = null;
  let syncedCount = 0;

  do {
    const params = new URLSearchParams({
      status: 'active',
      limit: '100',
      ...(cursor && { starting_after: cursor })
    });

    const response = await fetch(
      `https://api.withgale.com/v2/products?${params}`,
      {
        headers: {
          'Authorization': `Bearer ${process.env.GALE_API_KEY}`
        }
      }
    );

    const { data, has_more, next_cursor } = await response.json();

    for (const product of data) {
      await syncToLocalDB(product);
      syncedCount++;
    }

    cursor = next_cursor;
  } while (has_more);

  return { synced: syncedCount };
};

Product Analytics

// Calculate total catalog value
const calculateCatalogValue = async () => {
  const products = await getAllProducts();

  const stats = {
    totalProducts: products.length,
    activeProducts: 0,
    hsaEligibleProducts: 0,
    totalValue: 0,
    hsaEligibleValue: 0,
    averagePrice: 0
  };

  for (const product of products) {
    if (product.status === 'active') {
      stats.activeProducts++;
      stats.totalValue += product.price.cents;
    }

    if (product.eligibility.hsa_fsa_eligible) {
      stats.hsaEligibleProducts++;
      stats.hsaEligibleValue += product.price.cents;
    }
  }

  stats.averagePrice = stats.totalValue / stats.totalProducts / 100;
  stats.totalValue = stats.totalValue / 100;
  stats.hsaEligibleValue = stats.hsaEligibleValue / 100;

  return stats;
};

Export Products to CSV

// Export all products to CSV
const exportProductsToCSV = async () => {
  const products = await getAllProducts();

  const csv = [
    'ID,Name,Price,HSA Eligible,Status,Created',
    ...products.map(p =>
      [
        p.id,
        p.name,
        p.price.dollars,
        p.eligibility.hsa_fsa_eligible,
        p.status,
        p.created_at
      ].join(',')
    )
  ].join('\n');

  return csv;
};

Filter Eligible Products for Marketing

// Get eligible products for HSA marketing campaign
const getMarketingList = async () => {
  const params = new URLSearchParams({
    status: 'active',
    hsa_fsa_eligible: 'true',
    limit: '100'
  });

  const response = await fetch(
    `https://api.withgale.com/v2/products?${params}`,
    {
      headers: {
        'Authorization': `Bearer ${process.env.GALE_API_KEY}`
      }
    }
  );

  const { data } = await response.json();

  return data.map(product => ({
    name: product.name,
    price: product.price.formatted,
    eligibilityType: product.eligibility.eligibility_type,
    productUrl: `https://yoursite.com/products/${product.id}`,
    imageUrl: product.images[0]?.url
  }));
};

Inventory Report

// Generate low-stock report (using metadata)
const getLowStockProducts = async () => {
  const products = await getAllProducts();

  const lowStock = products.filter(product => {
    const inventory = parseInt(product.metadata?.inventory || 0);
    return inventory < 10 && product.status === 'active';
  });

  return lowStock.map(product => ({
    id: product.id,
    name: product.name,
    stock: product.metadata?.inventory || 0,
    price: product.price.formatted
  }));
};

Pagination Best Practices

Use cursor-based pagination for large datasets:
// Good: Use cursor pagination
let cursor = null;
do {
  const params = new URLSearchParams({
    limit: '100',
    ...(cursor && { starting_after: cursor })
  });

  const response = await fetch(`/v2/products?${params}`, {
    headers: { 'Authorization': `Bearer ${apiKey}` }
  });

  const data = await response.json();
  await processProducts(data.data);
  cursor = data.next_cursor;
} while (data.has_more);

Errors

Status CodeError CodeDescription
400invalid_requestInvalid query parameters
401unauthorizedInvalid or missing API key
429rate_limit_exceededToo many requests
Example error:
{
  "error": {
    "code": "invalid_request",
    "message": "Invalid status value",
    "param": "status"
  }
}

Best Practices

Use Pagination

Always paginate large result sets using cursors

Filter Efficiently

Use filters to reduce data transfer

Cache Results

Cache product lists to minimize API calls

Handle Empty Results

Check if data array is empty before processing