> ## Documentation Index
> Fetch the complete documentation index at: https://docs.tinyfish.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Compare Prices Across Websites With an AI Agent

> Check one product or quote on several sites at once with TinyAgent and get every price back as JSON in the same shape

> [!NOTE]
>
> **Capability:** parallel runs + `output_schema`

Use this to find where something is cheapest. TinyAgent runs one browser per site, all at the same time, and every site returns its price in the same JSON shape — so you can sort the results straight away. It works for listed prices and for quotes you only get after filling in a form.

![TinyAgent opens Best Buy's search results for the Logitech MX Master 3S and returns the lowest-priced listing as JSON](https://mintcdn.com/tinyfish/4TzYEN-cACiNG0l8/images/agent-tutorial/compare-prices/run.gif?s=70b8753fe96c92d95c664060800314b5)

TinyAgent opens Best Buy's search results for the Logitech MX Master 3S and returns the lowest-priced listing as JSON.

## Steps

1. **Start each run on the site's search results.** Put the search in the URL — e.g. `bestbuy.com/site/searchpage.jsp?st=logitech+mx+master+3s` — so the agent lands on the listings, not the homepage. Fewer steps means a faster run that's less likely to time out.
2. **Use one schema for every site.** Same field names everywhere, with `nullable: true` on anything a site might not show.
3. **Run them all at once.** One run per site, started together, each with a time limit.
4. **Sort what came back.** Skip sites that failed or found nothing, then sort by price.

> [tab] Python
>
> ```py theme={null}
> import asyncio
> from tinyfish import AsyncTinyFish, AgentConfig, BrowserProfile, ProxyConfig, ProxyCountryCode
>
> SITES = [
>     "https://www.bestbuy.com/site/searchpage.jsp?st=logitech+mx+master+3s",
>     "https://www.newegg.com/p/pl?d=logitech+mx+master+3s",
> ]
>
> GOAL = (
>     "Look at the first 5 listings for the Logitech MX Master 3S mouse "
>     "(the mouse itself, not accessories, bundles, or refurbished units). "
>     "Return the one with the lowest price. Set site to the store's name. "
>     "Use null for any field the page doesn't show."
> )
>
> SCHEMA = {
>     "type": "object",
>     "properties": {
>         "site": {"type": "string"},
>         "product_name": {"type": "string", "nullable": True},
>         "price": {"type": "number", "nullable": True},
>         "currency": {"type": "string", "nullable": True},
>         "in_stock": {"type": "boolean", "nullable": True},
>         "product_url": {"type": "string", "nullable": True},
>     },
>     "required": ["site", "product_name", "price", "currency", "in_stock", "product_url"],
> }
>
>
> async def main():
>     client = AsyncTinyFish()
>
>     runs = await asyncio.gather(
>         *(
>             client.agent.run(
>                 url=url,
>                 goal=GOAL,
>                 output_schema=SCHEMA,
>                 browser_profile=BrowserProfile.STEALTH,
>                 proxy_config=ProxyConfig(enabled=True, country_code=ProxyCountryCode.US),
>                 agent_config=AgentConfig(max_duration_seconds=180),
>             )
>             for url in SITES
>         ),
>         return_exceptions=True,
>     )
>
>     listings = [
>         run.result
>         for run in runs
>         if not isinstance(run, Exception) and run.result and run.result.get("price") is not None
>     ]
>     for item in sorted(listings, key=lambda item: item["price"]):
>         print(f"{item['site']}: {item['price']} {item['currency']}  {item['product_url']}")
>
>
> asyncio.run(main())
> ```

> [tab] TypeScript
>
> ```ts theme={null}
> import { TinyFish } from "@tiny-fish/sdk";
>
> type Listing = {
>   site: string;
>   product_name: string | null;
>   price: number | null;
>   currency: string | null;
>   in_stock: boolean | null;
>   product_url: string | null;
> };
>
> const client = new TinyFish();
>
> const SITES = [
>   "https://www.bestbuy.com/site/searchpage.jsp?st=logitech+mx+master+3s",
>   "https://www.newegg.com/p/pl?d=logitech+mx+master+3s",
> ];
>
> const GOAL =
>   "Look at the first 5 listings for the Logitech MX Master 3S mouse " +
>   "(the mouse itself, not accessories, bundles, or refurbished units). " +
>   "Return the one with the lowest price. Set site to the store's name. " +
>   "Use null for any field the page doesn't show.";
>
> const SCHEMA = {
>   type: "object",
>   properties: {
>     site: { type: "string" },
>     product_name: { type: "string", nullable: true },
>     price: { type: "number", nullable: true },
>     currency: { type: "string", nullable: true },
>     in_stock: { type: "boolean", nullable: true },
>     product_url: { type: "string", nullable: true },
>   },
>   required: ["site", "product_name", "price", "currency", "in_stock", "product_url"],
> };
>
> const runs = await Promise.allSettled(
>   SITES.map((url) =>
>     client.agent.run({
>       url,
>       goal: GOAL,
>       output_schema: SCHEMA,
>       browser_profile: "stealth",
>       proxy_config: { enabled: true, country_code: "US" },
>       agent_config: { max_duration_seconds: 180 },
>     }),
>   ),
> );
>
> const listings = runs
>   .flatMap((r) => (r.status === "fulfilled" && r.value.result ? [r.value.result as Listing] : []))
>   .filter((item): item is Listing & { price: number } => item.price !== null)
>   .sort((a, b) => a.price - b.price);
>
> console.table(listings);
> ```

**Result**

```json theme={null}
[
  { "site": "Best Buy", "product_name": "Logitech - MX Master 3S Bluetooth Edition Performance Wireless Optical Mouse ...", "price": 79.99, "currency": "USD", "in_stock": true, "product_url": "https://www.bestbuy.com/product/..." },
  { "site": "Newegg", "product_name": "Logitech MX Master 3S - Wireless Performance Mouse ... - Pale Grey", "price": 87.5, "currency": "USD", "in_stock": true, "product_url": "https://www.newegg.com/..." }
]
```

Each run is independent and billed separately — one site failing doesn't stop the others. To check many products or many sites, see [Run TinyAgent Across Multiple URLs](https://docs.tinyfish.ai/agent/how-to/multiple-urls.md).

## Get a Quote From a Form

Some prices only appear after you fill in a form — shipping, insurance, a moving quote. Start the run on the quote page, put every input in the goal, and say where to stop:

```txt theme={null}
url:  https://postcalc.usps.com/
goal: Get a shipping quote for one parcel:
        - From ZIP: 94107
        - To ZIP: 10001
        - Weight: 2 lb, size 12 x 8 x 4 inches
      Fill in the quote form and return each service shown with its price and delivery estimate.
      Stop at the quote results. Don't create an account, enter payment details, or buy anything.
```

The run returns every service on the results page — Priority Mail Express, Priority Mail, Ground Advantage and so on — with price and delivery date. Add an `output_schema` with a `services` array if you need the same fields every time.

> [!NOTE]
>
> **Tip — from ChatGPT or Claude:** name the product and every site in one message, e.g. "Compare the price of the Logitech MX Master 3S on Best Buy and Newegg." Connect once with [Run TinyAgent From ChatGPT or Claude](https://docs.tinyfish.ai/agent/how-to/run-with-chatgpt-claude.md).

## Common Mistakes

> [accordion] One run asked to visit every site
>
> A single goal like "check Best Buy, then Newegg, then Amazon" makes one long run that can time out before it finishes. Give each site its own run, as above, and cap each with `max_duration_seconds` so one slow site can't hold up the rest.

> [accordion] A marketplace seller's price instead of the store's
>
> "The first listing" is often a sponsored result or a third-party seller with a marked-up price. Ask for the lowest price among the first few listings, and say what doesn't count — accessories, bundles, refurbished units.

> [accordion] output\_schema rejected with "type arrays are not supported"
>
> Writing `"type": ["number", "null"]` returns `400`. Use `"type": "number", "nullable": true` instead — full rules in the [Structured Output Reference](https://docs.tinyfish.ai/agent/api/agent/structured-output.md).

> [accordion] Prices that aren't really comparable
>
> The same product can show a different price, currency, or tax by country. Pin the location with `proxy_config.country_code`, keep a `currency` field in the schema, and say which price you want — e.g. "price before tax and shipping".

> [accordion] A retailer blocks the run
>
> Large retailers often block standard browsers. Use `browser_profile: "stealth"` with a proxy, as in the sample — see the [Anti-Bot Guide](https://docs.tinyfish.ai/anti-bot-guide.md).

## Related

> [card] [Run TinyAgent Across Multiple URLs](https://docs.tinyfish.ai/agent/how-to/multiple-urls.md)
>
> Run the same goal on many pages at once

> [card] [Get a TinyAgent Run as Structured JSON](https://docs.tinyfish.ai/agent/how-to/structured-output.md)
>
> Pin down an exact result shape

> [card] [Structured Output Reference](https://docs.tinyfish.ai/agent/api/agent/structured-output.md)
>
> Supported types, `nullable`, and limits

> [card] [Anti-Bot Guide](https://docs.tinyfish.ai/anti-bot-guide.md)
>
> Stealth browser and proxy settings


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.