Skip to main content
Beta Feature - The AI Products Extraction API was released in January 2026 and is currently in beta.

Overview

Automatically extract product information from any company’s website including pricing, features, target audience, and more - all powered by AI.

Use Case

The Challenge: Manually researching competitor products, pricing models, and features is time-consuming and inconsistent. The Solution: Brand.dev’s AI Products API analyzes websites and returns structured product data automatically. The Result: Build competitive analysis tools, market research dashboards, and lead enrichment systems with up-to-date product information.

Quick Start

Basic Product Extraction

import BrandDev from "brand.dev";

const client = new BrandDev({
  apiKey: process.env.BRAND_DEV_API_KEY,
});

async function extractProducts(domain: string) {
  try {
    const response = await client.brand.ai.products({
      domain: domain,
      maxProducts: 5, // Extract up to 5 products (1-12)
    });

    return response.products;
  } catch (error) {
    console.error("Product extraction failed:", error);
    return [];
  }
}

// Extract products
const products = await extractProducts("stripe.com");
console.log(products);

Response Structure

{
  "products": [
    {
      "name": "<string>",
      "description": "<string>",
      "features": ["<string>"],
      "target_audience": ["<string>"],
      "tags": ["<string>"],
      "price": {
        "amount": 123,
        "currency": "<string>",
        "billing_frequency": "monthly",
        "pricing_model": "per_seat"
      },
      "url": "<string>",
      "category": "<string>",
      "image_url": "<string>"
    }
  ]
}

Use Cases

Competitive Analysis Dashboard

async function buildCompetitorAnalysis(competitors: string[]) {
  const analysis = await Promise.all(
    competitors.map(async (domain) => {
      const { brand } = await client.brand.retrieve({ domain });
      const response = await client.brand.ai.products({
        domain,
        maxProducts: 10,
      });

      return {
        company: brand.title,
        logo: brand.logos?.[0]?.url,
        domain: domain,
        products: response.products,
        productCount: response.products.length,
        pricingModels: [
          ...new Set(response.products.map((p) => p.price?.pricing_model)),
        ],
        averagePrice: calculateAveragePrice(response.products),
        categories: [...new Set(response.products.map((p) => p.category))],
      };
    }),
  );

  return analysis;
}

function calculateAveragePrice(products: any[]) {
  const prices = products
    .filter((p) => p.price?.amount && p.price.billing_frequency === "monthly")
    .map((p) => p.price.amount);

  if (prices.length === 0) return null;

  return prices.reduce((a, b) => a + b, 0) / prices.length;
}

// Usage
const competitors = ["stripe.com", "square.com", "adyen.com"];
const analysis = await buildCompetitorAnalysis(competitors);

Pricing Comparison Tool

interface PricingComparison {
  company: string;
  logo: string;
  products: Array<{
    name: string;
    price: number;
    frequency: string;
    model: string;
  }>;
}

async function comparePricing(domains: string[]): Promise<PricingComparison[]> {
  const comparisons = await Promise.all(
    domains.map(async (domain) => {
      const { brand } = await client.brand.retrieve({ domain });
      const response = await client.brand.ai.products({
        domain,
        maxProducts: 5,
      });

      return {
        company: brand.title,
        logo: brand.logos?.[0]?.url,
        products: response.products
          .filter((p) => p.price)
          .map((p) => ({
            name: p.name,
            price: p.price.amount,
            frequency: p.price.billing_frequency,
            model: p.price.pricing_model,
          })),
      };
    }),
  );

  return comparisons;
}

// Display in UI
const pricing = await comparePricing(["figma.com", "sketch.com", "adobe.com"]);

Market Research

Extract products from an entire industry:
async function researchIndustry(
  companies: Array<{ domain: string; name: string }>,
) {
  const allProducts = [];

  for (const company of companies) {
    try {
      const response = await client.brand.ai.products({
        domain: company.domain,
        maxProducts: 10,
      });

      allProducts.push(
        ...response.products.map((p) => ({
          ...p,
          company: company.name,
          domain: company.domain,
        })),
      );
    } catch (error) {
      console.error(`Failed to extract products for ${company.domain}:`, error);
    }

    // Rate limiting: wait between requests
    await new Promise((resolve) => setTimeout(resolve, 1000));
  }

  // Analyze the market
  return {
    totalProducts: allProducts.length,
    categories: groupBy(allProducts, "category"),
    pricingModels: groupBy(allProducts, "price.pricing_model"),
    averageFeaturesCount: average(
      allProducts.map((p) => p.features?.length || 0),
    ),
    commonFeatures: findCommonFeatures(allProducts),
  };
}

function groupBy(items: any[], key: string) {
  return items.reduce((acc, item) => {
    const value = key.split(".").reduce((obj, k) => obj?.[k], item);
    acc[value] = (acc[value] || 0) + 1;
    return acc;
  }, {});
}

Lead Enrichment

Enrich leads with product information:
async function enrichLeadWithProducts(email: string) {
  const domain = email.split("@")[1];

  try {
    // Get brand data
    const { brand } = await client.brand.retrieve({ domain });

    // Get products
    const response = await client.brand.ai.products({ domain, maxProducts: 5 });

    // Enrich lead record
    return {
      company: brand.title,
      logo: brand.logos?.[0]?.url,
      industry: brand.industries?.eic?.[0]?.industry,

      // Product data
      products: response.products.map((p) => p.name),
      hasFreemium: response.products.some(
        (p) => p.price?.pricing_model === "freemium",
      ),
      hasTrial: response.products.some((p) =>
        p.features?.some((f) => f.toLowerCase().includes("trial")),
      ),
      pricingModels: [
        ...new Set(response.products.map((p) => p.price?.pricing_model)),
      ],
      targetAudience: [
        ...new Set(response.products.flatMap((p) => p.target_audience || [])),
      ],
    };
  } catch (error) {
    console.error("Enrichment failed:", error);
    return null;
  }
}

Understanding Pricing Models

The API returns structured pricing information with these models:

Pricing Model Types

ModelDescriptionExample
per_seatPrice per user/seat$10/user/month
flatFixed price$99
tieredVolume-based tiers$10 for 0-10, $8 for 11-50
usage_basedPay per use$0.029 per transaction
freemiumFree tier + paidFree + $29/month Pro
customContact salesEnterprise pricing

Billing Frequency

  • monthly - Billed every month
  • yearly - Billed annually
  • one_time - One-time payment
  • per_transaction - Per usage
  • usage_based - Based on consumption

Example: Filtering by Pricing Model

const products = await client.brand.ai.products({
  domain: "figma.com",
  maxProducts: 10,
});

// Find freemium products
const freemiumProducts = products.products.filter(
  (p) => p.price?.pricing_model === "freemium",
);

// Find subscription products
const subscriptionProducts = products.products.filter((p) =>
  ["monthly", "yearly"].includes(p.price?.billing_frequency),
);

// Find usage-based products
const usageBasedProducts = products.products.filter(
  (p) => p.price?.pricing_model === "usage_based",
);

Building a Product Database

Store extracted products for analysis:
interface ProductRecord {
  id: string;
  company_domain: string;
  company_name: string;
  product_name: string;
  price: number | null;
  pricing_model: string | null;
  features: string[];
  extracted_at: Date;
}

async function buildProductDatabase(domains: string[]) {
  const products: ProductRecord[] = [];

  for (const domain of domains) {
    try {
      const { brand } = await client.brand.retrieve({ domain });
      const response = await client.brand.ai.products({
        domain,
        maxProducts: 12,
      });

      for (const product of response.products) {
        products.push({
          id: `${domain}-${product.name.toLowerCase().replace(/\s+/g, "-")}`,
          company_domain: domain,
          company_name: brand.title,
          product_name: product.name,
          price: product.price?.amount || null,
          pricing_model: product.price?.pricing_model || null,
          features: product.features || [],
          extracted_at: new Date(),
        });
      }

      // Store in database
      await db.products.insertMany(products);
    } catch (error) {
      console.error(`Failed for ${domain}:`, error);
    }
  }

  return products;
}

Best Practices

1. Set Appropriate maxProducts

Balance between coverage and cost:
// For quick overview
const response = await client.brand.ai.products({ domain, maxProducts: 3 });

// For comprehensive analysis
const response = await client.brand.ai.products({ domain, maxProducts: 12 });

2. Handle Missing Data

Not all fields are available for all products:
const product = products[0];

const price = product.price?.amount
  ? `$${product.price.amount}${formatFrequency(product.price.billing_frequency)}`
  : "Contact Sales";

const features =
  product.features?.length > 0
    ? product.features
    : ["Information not available"];

3. Cache Results

Product data doesn’t change frequently:
const CACHE_TTL = 7 * 24 * 60 * 60 * 1000; // 7 days

async function getCachedProducts(domain: string) {
  const cached = await redis.get(`products:${domain}`);

  if (cached) {
    return JSON.parse(cached);
  }

  const response = await client.brand.ai.products({ domain, maxProducts: 10 });
  await redis.setex(
    `products:${domain}`,
    CACHE_TTL / 1000,
    JSON.stringify(response.products),
  );

  return response.products;
}

4. Process Asynchronously

For large-scale analysis, process in background:
async function scheduleProductExtraction(domains: string[]) {
  for (const domain of domains) {
    await queue.add("extract-products", {
      domain,
      maxProducts: 10,
    });
  }
}

// Worker process
queue.process("extract-products", async (job) => {
  const { domain, maxProducts } = job.data;

  const response = await client.brand.ai.products({ domain, maxProducts });

  await db.products.upsert({
    domain,
    products: response.products,
    extracted_at: new Date(),
  });
});

Limitations

Beta Limitations: - Maximum 12 products per request - Works best with SaaS and B2B companies - Accuracy varies by website structure - Some pricing may not be detected (enterprise/custom pricing)

Use Case Examples

1. Competitor Monitoring

Track competitor product launches and pricing changes

2. Market Research

Analyze product offerings across an entire industry

3. Sales Enablement

Equip sales teams with competitive intelligence

4. Lead Scoring

Score leads based on their product offerings

5. Partnership Discovery

Find complementary products for partnerships
Have feedback on the AI Products API? This is a beta feature and we’d love to hear your thoughts. Contact us with feedback or feature requests.