Felo API PlatformFelo API Platform

Research

Research a product niche before you stock it

Source the category context, capture competitor pages on a stated date, read what buyers complain about on X, then keep the evidence and the memo in one LiveDoc you can revisit before ordering inventory.

Build time30 minutesLevelIntermediateUpdated2026-10-30
Who it's forEcommerce operatorsProduct researchersDTC founders

Copy the prompt

Replace the bracketed fields, then paste it into Claude Code, Codex, or any AI with terminal access. It installs the Felo skill, asks before spending credits, and runs the workflow.

Research this product niche before we stock or launch: [product or category].

Market: [country or region]. Channel: [marketplace / DTC / retail]. Time window for buyer posts: last [30] days.
Comparable products or competitor URLs: [URLs]. Budget and margin constraints: [constraints]. Audience: [team].

Produce a Markdown memo with:
1. A one-paragraph verdict: is this worth researching further, and what would change your mind.
2. Demand signals: what the sources say about interest, seasonality, and substitutes.
3. Competing products: what each one claims, the price captured on its page, and where buyers say it falls short.
4. Price positioning: the price points actually found on the pages, each with its capture date.
5. Buyer complaints and unmet needs, with quotes, handles, and dates.
6. Risks and open questions to resolve before buying inventory.
7. Sources: every page and post used, with its URL and date.

Rules:
- Use only what the APIs return. Never invent market size, sales estimates, review counts, or competitor prices.
- Report prices exactly as captured, with the page URL and capture date. Never average or normalize them.
- Treat X posts as anecdote, not market data, and say when the sample is thin.
- Separate what a vendor claims from what a buyer reports.
- Before spending credits, show the extractions and searches you plan to make and wait for my approval.
- Do not contact suppliers, place orders, or message anyone on X.
- Write the result to ./product-research.md and summarize it in chat.

The copy also includes the install steps and safety rules.

What you get

Example output from a run. Not a live generation.

Example memo (excerpt)

Product research — example category

Verdict

Worth a closer look, but the public evidence is not enough for an inventory decision. The open questions below are the next step.

Category context

Buyers compare on counter space and cleanup more than on pressure. No public source supports a market-size estimate.

Competitor pages

  • Example A — product page (captured 2026-10-30)
  • Example B — product page (captured 2026-10-30)

What buyers report

  • @example_buyer: "Third replacement in a year; the seal fails." — 2026-10-19
  • @example_home: "Takes minutes to clean, which nobody mentions." — 2026-10-24

Price positioning

Prices were captured from the linked pages on 2026-10-30. They sit in two tiers; each page is quoted directly instead of being averaged.

Risks and open questions

  • Seal durability is the most repeated complaint.
  • No source covers return rates.
  • Check supplier lead times and landed cost before ordering.

Sources

  • https://example.com/product-a — 2026-10-30
  • https://x.com/example_buyer/status/... — 2026-10-19

The exact calls

For developers: the same workflow as copy-paste code, in the order it runs.

Show the exact API calls
  1. Ask Chat for the category picture

    Start with the sourced answer, not a blank page: Chat searches the public web, ranks the pages, and returns the resources it used. Ask for demand context, substitutes, and common complaints in one query, then keep the top resources as the evidence list. The helper below sends the key once and throws on a non-2xx response.

    product-research.ts
    const BASE = "https://openapi.felo.ai";
    const headers = {
      Authorization: `Bearer ${process.env.FELO_API_KEY}`,
      "Content-Type": "application/json",
    };
    
    async function felo<T>(path: string, body?: unknown): Promise<T> {
      const res = await fetch(`${BASE}${path}`, {
        method: body ? "POST" : "GET",
        headers,
        body: body ? JSON.stringify(body) : undefined,
      });
      if (!res.ok) throw new Error(`${res.status}: ${await res.text()}`);
      return res.json() as Promise<T>;
    }
    
    type ChatData = {
      data: {
        answer: string;
        resources: { link: string; title: string; snippet: string }[];
      };
    };
    
    const niche = "portable espresso maker"; // replace with the category you are judging
    
    const context = await felo<ChatData>("/v2/chat", {
      query: `Demand, price bands, and buyer complaints for ${niche} in the US`,
    });
    
    const sources = context.data.resources.slice(0, 5);

    Example output

    {
      "answer": "Buyers compare on counter space and cleanup …",
      "sources": [
        { "link": "https://example.com/category-guide", "title": "…", "snippet": "…" }
      ]
    }
  2. Capture the competitor pages

    Extract two or three competitor product pages as Markdown with readability enabled. The goal is the claim and the price on the page today, not a paraphrased spec sheet. Keep the URL beside the content and record the capture date in the memo — a price without a date is a rumour.

    product-research.ts
    const PRODUCT_PAGES = [
      { url: "https://example.com/product-a", title: "Example A — product page" },
      { url: "https://example.com/product-b", title: "Example B — product page" },
    ];
    
    type ExtractData = { data: { content: string } };
    
    const listings = await Promise.all(
      PRODUCT_PAGES.map((page) =>
        felo<ExtractData>("/v2/web/extract", {
          url: page.url,
          output_format: "markdown",
          with_readability: true,
        }).then((extracted) => ({ ...page, content: extracted.data.content })),
      ),
    );

    Example output

    [
      {
        "url": "https://example.com/product-a",
        "title": "Example A — product page",
        "content": "# Example A\n\nSpecs and price as displayed…"
      }
    ]
  3. Read what buyers report on X

    Vendor pages list features; buyers describe failures. Search the niche plus the friction words buyers use when something fails — shipping, quality, refund — inside the last 30 days, and keep the handle, date, and post link. These posts are anecdotes, not demand data, and the memo must say so.

    product-research.ts
    type XData = {
      data: {
        tweets: {
          content: string;
          author: { username: string };
          created_at: string;
          metrics: { retweet_count: number; favorite_count: number; reply_count: number };
          url: string;
        }[];
      };
    };
    
    const since = Math.floor((Date.now() - 30 * 24 * 60 * 60 * 1000) / 1000);
    
    const search = await felo<XData>("/v2/x/tweet/search", {
      query: `"${niche}" (shipping OR quality OR refund)`,
      limit: 50,
      since_time: since,
    });
    
    const complaints = search.data.tweets;

    Example output

    { "data": { "tweets": [
      {
        "content": "Third replacement in a year; the seal fails.",
        "author": { "username": "example_buyer" },
        "created_at": "2026-10-19T15:02:00Z",
        "metrics": { "retweet_count": 1, "favorite_count": 12, "reply_count": 3 },
        "url": "https://x.com/example_buyer/status/..."
      }
    ] } }
  4. Compose the memo

    This step is plain code: assemble the category answer, the capture dates, the buyer quotes, and the source list into one Markdown string. Keep the open questions section honest — the memo is useful because it separates what the pages prove from what you still need to check with a supplier.

    product-research.ts
    const capturedOn = new Date().toISOString().slice(0, 10);
    
    const memo = [
      `# Product research — ${niche}`,
      "",
      "## Category context",
      context.data.answer,
      "",
      "## Competitor pages",
      ...listings.map(
        (page) => `- [${page.title}](${page.url}) — captured ${capturedOn}`,
      ),
      "",
      "## What buyers report",
      ...complaints.slice(0, 10).map(
        (post) =>
          `- @${post.author.username} (${post.created_at.slice(0, 10)}): ${post.content}`,
      ),
      "",
      "## Sources",
      ...sources.map((source) => `- [${source.title}](${source.link})`),
    ].join("\n");
    
    console.log(memo);
  5. File the evidence in a LiveDoc

    Run this once per niche: create the LiveDoc and keep its short_id in your config. Add the competitor pages and the Chat sources as URL resources so the archive deduplicates them, then add the memo as a document resource. Before the buying decision you can retrieve the captures and check which prices moved since the memo was written.

    product-research.ts
    type LiveDocData = { data: { short_id: string } };
    
    const research = await felo<LiveDocData>("/v2/livedocs", {
      name: `Product research: ${niche}`,
      description: "Competitor pages, buyer posts, and the memo",
    });
    
    await felo(`/v2/livedocs/${research.data.short_id}/resources/urls`, {
      urls: [
        ...listings.map((page) => ({ url: page.url, title: page.title })),
        ...sources.map((source) => ({ url: source.link, title: source.title })),
      ].slice(0, 10),
    });
    
    await felo(`/v2/livedocs/${research.data.short_id}/resources/doc`, {
      title: `Product memo — ${capturedOn}`,
      content: memo,
    });

    Example output

    { "short_id": "PvyKouzJirXjFdst4uKRK3" }

What's included

  • A sourced category picture from Chat, with the pages behind it
  • Competitor product pages captured as Markdown on a stated date
  • Buyer complaints from X with handles, dates, and links
  • A Markdown memo assembled from those inputs
  • One LiveDoc holding the evidence and the memo for the next decision

What it doesn't do

  • Public sources only; paid market reports, supplier quotes, and internal sales data are out of scope
  • It does not estimate market size, sales volume, or margin
  • Prices are reported as captured, never normalized or averaged
  • X posts are anecdotes, not demand data
  • It does not contact suppliers, place orders, or change listings

Questions

Where does the market context come from?

Chat API returns a sourced answer with the public pages it used. Competitor pages come from the URLs you supply and are extracted with Web Fetch; buyer posts come from X Search. No private sales data is involved.

Can it tell me whether a product will sell?

No. It organizes public evidence — category context, captured prices, and buyer complaints — and writes down what is unknown. Demand validation still needs your own data, such as ads, preorders, or supplier conversations.

What if a marketplace blocks extraction?

Web Fetch fetches public pages one URL at a time and some sites block it. Use a product or supplier page you can access and let the memo label the missing capture instead of guessing a price.

How do you keep competitor prices from going stale?

Every price in the memo is tied to the page URL and the capture date. Re-run the extraction close to a buying decision; the archive keeps earlier captures so you can see that a price moved.

Does it contact suppliers or sellers?

No. The workflow is read-only: it extracts pages, searches public posts, and writes a local memo. It never sends messages, places orders, or edits listings.

How much does a research run cost?

Each Chat call, page extraction, and X search spends usage-based credits; current rates are on the pricing page. The prompt asks for approval before any of those calls run.

Related workflows

Make it yours

The same recipe, adapted to the way you actually ship.

Add review-video transcripts

When the category has review videos, fetch the transcript with YouTube Subtitling and add two or three timestamped quotes to the buyer section. A video review is still one person's experience, so keep the same anecdote label as the X posts.

Compare two candidates side by side

Run the same steps once per candidate and put the memos side by side: claimed features, captured price, repeated complaint, and open question. The comparison stays qualitative until you have your own sales or return data, which is the honest way to rank them.

Refresh before the buying decision

Re-run the page extraction a day before you order. Keep the same LiveDoc and add a new memo document, so the archive shows what changed between the desk research and the purchase — including a price that moved out of range.

The reference pages behind this recipe, and where to go next.