TinyFish
Search
Fast, structured web search
Fetch
Any URL to clean content
Agent
Multi-step web automation
Browser
Stealth Chromium sessions
All products share one API keyView docs →
Documentation
API reference and guides
Integrations
Connect with your stack
Blog
Product updates and insights
Cookbook
Open-source examples
Pricing
Overview
Enterprise-grade web data
Use Cases
What teams are building
Customers
See who builds with TinyFish
ContactLog InLog In
Products
SearchFast, structured web search
FetchAny URL to clean content
AgentMulti-step web automation
BrowserStealth Chromium sessions
Resources
DocumentationAPI reference and guides
IntegrationsConnect with your stack
BlogProduct updates and insights
CookbookOpen-source examples
PricingPlans, credits, and billing
Enterprise
OverviewEnterprise-grade web data
Use CasesWhat teams are building
CustomersSee who builds with TinyFish
ContactLog In
TinyFish

Web APIs built for agents.

Product
  • Enterprise
  • Use Cases
  • Customers
  • Pricing
  • Integrations
  • Docs
  • Trust
Resources
  • Cookbook
  • Blog
  • Current
  • Accelerator
Connect
  • X/Twitter
  • LinkedIn
  • Discord
  • GitHub
  • Contact Us
© 2026 TinyFish·Privacy·Cookies·Terms
Engineering

Build a Video Game Price Comparison Tool Across 10 Platforms

TinyFishie·TinyFish Observer·May 19, 2026·9 min read
Share
Cover: video-game-price-comparison

TinyFish parallel agents are cloud-based browser sessions that query multiple game storefronts simultaneously and return structured pricing data — game title, current price, discount percentage, and direct purchase URL — normalized across currencies and formats into a single ranked list.

Game prices vary wildly across storefronts and change constantly during sales. IsThereAnyDeal and CheapShark aggregate some of this, but neither covers every platform — itch.io is out, regional stores are out, newer storefronts take months to appear. When a game isn't in the database, you're back to checking each store manually.

This tutorial builds a 10-platform game price comparison tool using TinyFish agents. All stores are checked simultaneously: Steam, GOG, Epic Games, Xbox, PlayStation Store, Humble Bundle, Fanatical, Green Man Gaming, itch.io, and Amazon Gaming. You get a ranked price list in the time it takes to manually open two tabs.

Build a video game price comparison tool in 4 steps:

  1. Define your 10-platform list with store search URLs
  2. Write a goal prompt that extracts price, discount, and link consistently
  3. Run all 10 agents in parallel with Promise.allSettled
  4. Normalize currencies and rank by discounted price

Why Building Beats Using CheapShark or IsThereAnyDeal

CheapShark and IsThereAnyDeal are good starting points. They cover Steam, GOG, and most US/EU storefronts reliably — and their APIs are free. For straightforward PC game price lookup, they're often sufficient.

Browser agents make sense when:

  • Your target platform isn't indexed — itch.io, regional stores, console storefronts (Xbox, PlayStation), and subscription bundles (Humble Bundle) have partial or no CheapShark coverage
  • You need real-time data — API-backed aggregators cache prices, sometimes aggressively. During a Steam Summer Sale, cached prices lag the actual storefront by hours
  • You're building beyond price lookup — availability status, regional pricing differences, bundle detection, and DLC pricing require reading the actual store page rather than an API summary

The engineering tradeoff is straightforward:

ApproachBest forLimitation
CheapShark / IsThereAnyDeal APIQuick PC storefront lookup, free, no setupDoesn't cover all platforms; cached data
Browser agents (this tutorial)All 10 platforms, real-time, fully customizableCredit cost per query; rate-limited at Free tier

Choosing the right TinyFish API for this project: TinyFish offers two tools with different cost profiles. The Fetch API (api.fetch.tinyfish.ai) retrieves a page at a known URL — 1 credit = 15 pages. The Agent API (agent.tinyfish.ai) handles storefronts requiring a search step — 1 credit per step. For storefronts with stable product page URLs (GOG, itch.io), Fetch API can retrieve the price directly. For storefronts that require a search query (Steam, Epic), Agent API is required. Where you have direct product URLs, Fetch API is 15x cheaper than Agent API.

Video Game Price Comparison: Sending 10 Agents at Once

Prerequisites: Node.js 18+, TypeScript, and a TinyFish API key.

npm install @tiny-fish/sdk
npm install -D ts-node typescript
export TINYFISH_API_KEY=your_key_here
import { TinyFish } from "@tiny-fish/sdk";

const client = new TinyFish(); // reads TINYFISH_API_KEY from env

const PLATFORMS = [
  { name: "Steam",            url: "https://store.steampowered.com/search/?term=" },
  { name: "GOG",              url: "https://www.gog.com/en/games?query=" },
  { name: "Epic Games",       url: "https://store.epicgames.com/en-US/browse?q=" },
  { name: "Xbox",             url: "https://www.xbox.com/en-US/search?q=" },
  { name: "PlayStation",      url: "https://store.playstation.com/en-us/search/" },
  { name: "Humble Bundle",    url: "https://www.humblebundle.com/store/search?search=" },
  { name: "Fanatical",        url: "https://www.fanatical.com/en/search?search=" },
  { name: "Green Man Gaming", url: "https://www.greenmangaming.com/search/?query=" },
  { name: "itch.io",          url: "https://itch.io/search?q=" },
  // Add your 10th storefront here — any public game store search URL works
  // e.g. regional stores, indie platforms, or subscription bundle catalogs
];

const buildGoal = (gameTitle: string, platformName: string): string => `
  Search for the game "${gameTitle}" on this storefront.
  Return a JSON object with:
  - gameTitle: string (exact title as shown on this store)
  - price: string (current price exactly as displayed, including currency symbol)
  - originalPrice: string or null (if there is an active discount, the original/crossed-out price)
  - discountPercent: number or null (e.g. 40 for 40% off, null if not discounted)
  - currency: string (3-letter code: "USD", "EUR", "GBP", etc.)
  - platform: "${platformName}" (e.g. "Steam" or "GOG")
  - url: string (direct link to this game's store page)
  - available: boolean (true if the game is purchasable; false if unavailable, region-locked, or subscription-only)

  If the game is not found on this storefront, return null.
  Return only the most relevant match for "${gameTitle}".
`;

async function compareGamePrices(gameTitle: string) {
  const query = encodeURIComponent(gameTitle);

  const requests = PLATFORMS.map((platform) =>
    client.agent
      .run({ url: platform.url + query, goal: buildGoal(gameTitle, platform.name) })
      .then((response) => {
        // response.result is the parsed JavaScript object returned by the agent.
        // null = game not found. Check for goal failure vs legitimate null:
        const result = response.result as unknown;
        if (result && typeof result = "object" && !Array.isArray(result) &&
            (result as Record<string, unknown>).status = "failure") {
          return { platform: platform.name, result: null };
        }
        return { platform: platform.name, result: result ?? null };
      })
  );

  const settled = await Promise.allSettled(requests);
  return settled.map((r, i) => ({
    platform: PLATFORMS[i].name,
    // error: true = network/timeout failure, distinct from null (game not found)
    ...(r.status = "fulfilled" ? r.value : { result: null, error: true }),
  }));
}

Why `Promise.allSettled` not `Promise.all`: A single storefront timing out or returning an error shouldn't cancel the nine successful results. allSettled ensures every agent completes and reports independently.

Concurrency note: The Free plan supports 2 concurrent agent runs — 10 platforms run in approximately 5 batches. The Starter plan (10 concurrent) runs all 10 simultaneously. Each agent step consumes 1 credit. Querying all 10 platforms for one game title typically costs 30–60 credits depending on site complexity.
Ten TinyFish agents querying Steam, GOG, Epic, Xbox, PlayStation, and five other game storefronts simultaneously, returning normalized USD prices

Normalizing Prices Across 10 Currencies and Formats

Ten storefronts return ten different price formats: "$29.99", "€24,99", "£19.99", "AU$39.95", "Free", "N/A (subscription only)". Without normalization, sorting by price is impossible.

// normalize.ts

const CURRENCY_TO_USD: Record<string, number> = {
  USD: 1.0,
  EUR: 1.09,  // update these rates from an exchange rate API in production
  GBP: 1.27,
  AUD: 0.65,
  CAD: 0.73,
};

function parsePriceToUsd(raw: string, currency: string): number | null {
  if (!raw || raw.toLowerCase().includes("free")) return 0;
  if (raw.toLowerCase().includes("n/a") || raw.toLowerCase().includes("subscription")) return null;

  // Strip currency symbols and labels, normalize decimal separator
  // Remove all non-numeric chars except dot; handle comma as thousands separator
  const cleaned = raw.replace(/[^0-9.,]/g, "").replace(/,/g, "");
  const amount = parseFloat(cleaned);
  if (isNaN(amount)) return null;

  const rate = CURRENCY_TO_USD[currency] ?? 1.0;
  return Math.round(amount * rate * 100) / 100; // round to cents
}

function calcSavings(
  currentUsd: number | null,
  originalUsd: number | null
): { savingsUsd: number; discountPct: number } | null {
  if (currentUsd = null || originalUsd = null || originalUsd <= 0) return null;
  const savingsUsd = originalUsd - currentUsd;
  const discountPct = Math.round((savingsUsd / originalUsd) * 100);
  return savingsUsd > 0 ? { savingsUsd, discountPct } : null;
}

export function normalizeGameResult(raw: {
  price?: string;
  originalPrice?: string;
  currency?: string;
  discountPercent?: number | null;
  available?: boolean;
  [key: string]: unknown;
}) {
  const currency = raw.currency ?? "USD";
  const priceUsd = parsePriceToUsd(raw.price ?? "", currency);
  const originalUsd = raw.originalPrice
    ? parsePriceToUsd(raw.originalPrice, currency)
    : null;
  const savings = calcSavings(priceUsd, originalUsd);

  return {
    ...raw,
    priceUsd,
    originalUsd,
    effectiveDiscountPct: raw.discountPercent ?? savings?.discountPct ?? 0,
    savingsUsd: savings?.savingsUsd ?? 0,
  };
}

Three normalization decisions worth explaining:

  1. Currency conversion at query time. Build the lookup table once, apply it at normalization — don't call an exchange rate API per result. In production, fetch rates daily and cache them.
  2. `"Free"` returns 0. Sorting by price puts free games at the top, which is almost always the right behavior for a deals tool.
  3. `null` for subscription/unavailable. A game that's only on Game Pass shouldn't appear as $0 — it's not a direct purchase price. null price pushes it to the bottom of sorted output.

Putting it together:

import { normalizeGameResult } from "./normalize";

const rawResults = await compareGamePrices("Elden Ring");

const normalized = rawResults
  .filter((r) => r.result ! null)
  .map((r) => ({ ...r, result: normalizeGameResult(r.result as Record<string, unknown>) }))
  .sort((a, b) => {
    const aPrice = a.result.priceUsd ?? Infinity;
    const bPrice = b.result.priceUsd ?? Infinity;
    return aPrice - bPrice;
  });

normalized.forEach((r) => {
  const disc = r.result.effectiveDiscountPct > 0 ? ` (${r.result.effectiveDiscountPct}% off)` : "";
  console.log(`${r.platform}: $${r.result.priceUsd?.toFixed(2)}${disc} → ${r.result.url}`);
});

Run with npx ts-node index.ts.

Building a Deal Alert Bot

The comparison tool becomes a deal monitor with a scheduled run and a threshold check:

import { WebhookClient } from "discord.js";

const DEAL_THRESHOLD_USD = 15; // alert when any platform drops below this
const webhook = new WebhookClient({ url: process.env.DISCORD_WEBHOOK_URL! });

async function checkForDeals(gameTitle: string) {
  const results = await compareGamePrices(gameTitle);
  const normalized = results
    .filter((r) => r.result !== null)
    .map((r) => ({ ...r, result: normalizeGameResult(r.result as Record<string, unknown>) }));

  const deals = normalized.filter(
    (r) => (r.result.priceUsd ?? Infinity) < DEAL_THRESHOLD_USD
  );

  if (deals.length > 0) {
    const message = deals
      .map((d) => `**${d.platform}**: $${d.result.priceUsd?.toFixed(2)} ${d.result.effectiveDiscountPct > 0 ? `(-${d.result.effectiveDiscountPct}%)` : ""} — ${d.result.url}`)
      .join("\n");
    await webhook.send({ content: `🎮 Deal alert for **${gameTitle}**:\n${message}` });
  }
}

// Run daily via cron or GitHub Actions
await checkForDeals("Elden Ring");
await checkForDeals("Baldur's Gate 3");

Extension: Steam Summer Sale monitor. Run the comparison daily in mid-June and late November — the periods when Steam historically runs its major sales. When any game in your watchlist drops more than 50%, trigger the webhook immediately. Track prices over time to see which stores match Steam sale prices and which don't.

Extension: multi-game watchlist. Pass an array of game titles to compareGamePrices in parallel via Promise.all — 5 games × 10 platforms = 50 concurrent agent runs on a Pro plan.

For the same 10-platform parallel pattern applied to retail products, see how parallel agents work for medicine price comparison across pharmacy chains.

Game stores update prices hourly during sales. Checking 10 platforms manually for a single title takes long enough to miss a flash deal. Parallel agents collapse that to a single function call — all 10 checked simultaneously, prices normalized across currencies, ranked by actual cost. The same architecture that powers commercial deal aggregators, without the platform partnership requirements.

FAQ

Can this build a video game price comparison tool that checks all major storefronts? Yes — browser agents navigate public storefront pages and require no per-platform API registration. The example covers 10 platforms including ones not indexed by existing aggregators: itch.io, Xbox, PlayStation Store, and Amazon Gaming. Add any storefront with a public search page by appending an entry to the PLATFORMS array.

When should I use browser agents instead of CheapShark API? Use this decision framework:

If you need→ Use
All major platforms including itch.io, Xbox, PlayStation→ Browser agents (CheapShark doesn't index these)
Real-time pricing during an active sale→ Browser agents (aggregators cache)
PC storefronts only, fast prototype→ CheapShark API (free, no credits needed)
Availability, bundle status, or regional pricing→ Browser agents (read the actual store page)

In practice: start with CheapShark for the platforms it covers, add browser agents for the gaps.

Why do existing APIs like CheapShark miss some platforms? CheapShark and IsThereAnyDeal aggregate prices from storefronts that maintain API partnerships or accept data feeds. Platforms without those partnerships — itch.io, console storefronts, newer indie platforms — don't appear. Browser agents have no such dependency: they read the public storefront pages that any customer visits, regardless of whether the platform has an API program.

How does the price normalization handle "Free" or subscription-only titles? The parsePriceToUsd function returns 0 for "Free" (putting free games first in sorted output) and null for titles that are only available through a subscription service (Game Pass, PlayStation Plus). null prices sort to the bottom — a subscription title isn't a direct purchase price and shouldn't displace paid options.

What happens when a storefront doesn't carry the game?

null is the correct result — the game isn't listed on that platform. This is distinct from error: true (network failure or timeout). The comparison output shows null-result platforms as "not found" in the display layer, so users can see at a glance which stores carry the title.

How many platforms can run simultaneously? The Free plan (PAYG, 500 credits to start) supports 2 concurrent agent runs — 10 platforms run in 5 batches. The Starter plan ($15/mo, 10 concurrent) runs all 10 at once. Each agent step consumes 1 credit; a typical 10-platform query costs 30–60 credits total depending on site complexity and JavaScript rendering requirements.

Can I extend this to track prices over time? Yes. Save each run's output to a database keyed by game title, platform, and timestamp. Plotting priceUsd over time reveals sale patterns — which stores match Steam sale prices, how long post-launch discounts take to appear on console storefronts, and which platforms hold full price longest. Add a cron job to run daily checks for your watchlist.

Related Reading:

  • Build a Medicine Price Comparison Tool with Parallel Agents
  • Build a Vehicle Rental Price Comparison Tool with Parallel AI Agents
Get started

Start building.

No credit card. No setup. Run your first operation in under a minute.

Get 500 free creditsRead the docs
More Articles
80% of your Web Fetch returns Junk
Engineering

80% of your Web Fetch returns Junk

Matthew Sparr·May 11, 2026
Search and Fetch are now FREE for every agent, everywhere!
Company

Search and Fetch are now FREE for every agent, everywhere!

Keith Zhai·May 4, 2026
Production-Grade Web Fetching for AI Agents
Engineering

Production-Grade Web Fetching for AI Agents

Chenlu Ji·Apr 14, 2026