HomeGadgets.caHomeGadgets.ca🍁

HomeGadgets Price API

A read-only API for Canadian retailer prices on consumer electronics and appliances. Everything is in CAD, from Canadian retailers only. There are two access levels and they answer different questions:

Anonymous (no key)Live tier (issued key)
What it returnsThe catalog price we last observed: the two cheapest in-stock offers plus a count of how many Canadian retailers carry the item.A cross-retailer price fetched at the moment you ask, with the full per-retailer offer list.
SignupNone. No account, no email, no key.Keys are issued manually on approval.
Budget10 requests/day per IP, inside a shared 250 requests/day ceiling for all anonymous traffic.Per-key request, rate and row budgets.
LatencyImmediate (cached catalog).Up to ~90 s: submit a job, then poll.
Base URLhttps://www.homegadgets.cahttps://mcp.homegadgets.ca

There is no free-key tier and no email-for-a-key tier. Anonymous access is anonymous: the way to use this API without paying is simply to call it, within the budgets below. We do not want your email address in exchange.

New here? The introduction is The Price API: One Request, One Product - what it returns, what it costs you (nothing), how an AI assistant can call it over MCP, and what we ask in return. This page is the full reference.

Anonymous API: endpoints

No authentication header. No key parameter. Just call it. Both endpoints are read-only, return JSON, and serve catalog data, the same prices shown on our public product pages.

GET /public/v1/price/{mpn}

The best current Canadian catalog price for one product, addressed by a clean PATH with no question mark. This is the primary form: AI browsing connectors that refuse query-string URLs will fetch it. Just open it, or curl it:

curl "https://www.homegadgets.ca/public/v1/price/FRFG2033AV"

Live call, returns the JSON below: https://www.homegadgets.ca/public/v1/price/FRFG2033AV

Add a province as the next path segment to restrict to deliverable offers: https://www.homegadgets.ca/public/v1/price/FRFG2033AV/ON

For an MPN that contains a slash (e.g. Apple part numbers), use the slash-free product-id form instead:

https://www.homegadgets.ca/public/v1/product/{product_id}
(the id is returned by search). A space in an MPN is percent-encoded as %20.

GET /public/v1/price (query form, for existing callers)

The same lookup with query parameters. Kept unchanged; prefer the path form above for agents. Resolve by exact manufacturer part number or by our product id.

ParameterTypeRequiredMeaning
mpnstringone ofManufacturer part / model number. Exact, case-insensitive.
product_iduuidone ofHomeGadgets product id, as returned by search.
provincestringoptionalCanadian province, 2-letter code or name (e.g. ON, Ontario). Restricts to offers deliverable there before the cheapest is chosen.

Supply exactly one of mpn or product_id. Supplying both, or neither, is a 400.

curl "https://www.homegadgets.ca/public/v1/price?mpn=98UT9000PUA&province=ON"

Live call: https://www.homegadgets.ca/public/v1/price?mpn=98UT9000PUA&province=ON

{
  "product": {
    "name": "LG 98\" 4K UHD HDR LED webOS Smart TV - 2024",
    "brand": "LG",
    "mpn": "98UT9000PUA",
    "category": "tv",
    "product_id": "322caeb6-f3c4-4c7e-aab1-ad93b9c07b85",
    "pdp": "https://www.homegadgets.ca/tvs/lg-98ut9000pua-..."
  },
  "cheapest_offers": [
    { "price_cad": 1998.0, "retailer": "Retailer A", "in_stock": true,
      "buy_link": "https://mcp.homegadgets.ca/go/<offer_id>",
      "observed_at": "2026-08-08T18:56:48Z" },
    { "price_cad": 2098.0, "retailer": "Retailer B", "in_stock": true,
      "buy_link": "https://mcp.homegadgets.ca/go/<offer_id>",
      "observed_at": "2026-08-12T18:47:09Z" }
  ],
  "best_offer": { "...": "same object as cheapest_offers[0]" },
  "offers_shown": 2,
  "retailer_count": 9,
  "summary": "Cheapest of 9 Canadian retailers carrying this item.",
  "offer_count": 9,
  "low_price": 1998.0,
  "high_price": 2498.0,
  "rating": { "value": 4.6, "count": 812, "source_count": 3 },
  "as_of": "2026-08-12T18:47:09Z",
  "currency": "CAD",
  "disclaimer": "Prices are CAD from Canadian retailers; verify at the retailer before buying...",
  "full_offer_list": "Available with an API key. See https://www.homegadgets.ca/terms",
  "source": "https://www.homegadgets.ca"
}

Read the shape carefully. cheapest_offers is capped at two for anonymous callers, but retailer_count and offer_count are the real totals across every Canadian retailer we have for that product. That is deliberate: two offers answer “what does it cost and who has it”, and the count tells you honestly how deep the comparison behind it goes. The complete per-retailer list is the paid product.

best_offer is retained as an alias of cheapest_offers[0] so earlier integrations keep working. Prices are never invented, rounded or reordered - they are selected. Unpriced listings (call-for-pricing) are counted in retailer_count but never shown as an offer.

Free-text catalog search returning a ranked list of product cards. Use it to turn “lg 98 inch tv” into a product_id or mpn, then call /public/v1/price.

ParameterTypeRequiredMeaning
qstringyesFree text. Multi-word queries are matched as AND across brand, name and model number. "65 inch" is normalised to 65".
limitintegeroptionalResults to return. Default 20, hard maximum 20: a larger value is silently reduced, never an error.
curl "https://www.homegadgets.ca/public/v1/search?q=lg%2098%20inch%20tv&limit=5"

Live call - open it to see the ranked cards: https://www.homegadgets.ca/public/v1/search?q=lg%2098%20inch%20tv&limit=5

{
  "query": "lg 98 inch tv",
  "count": 5,
  "results": [
    { "product_id": "322caeb6-...", "mpn": "98UT9000PUA", "name": "LG 98\" 4K UHD ...",
      "brand": "LG", "category": "tv", "appliance_category": null,
      "image_url": "https://...", "slug": "lg-98ut9000pua-...",
      "best_price_cad": 1998.0, "retailer_count": 9, "currency": "CAD" }
  ],
  "currency": "CAD",
  "disclaimer": "...",
  "source": "https://www.homegadgets.ca"
}

Search returns catalog summaries only. It never returns per-retailer offer URLs - those stay single-product, so search cannot be turned into a bulk export.

Agent discovery

AI agents can import the API without reading this page. Both documents are public, unmetered and describe only the anonymous price endpoint - never our internal routes. These are real links: click or fetch them directly.

Building a ChatGPT integration? Use a Custom GPT Action, not the plugin manifest. OpenAI retired ChatGPT plugins in 2024, so ai-plugin.json is kept only for bots that still read it - it is not the integration path. To let ChatGPT call this API, create a Custom GPT and import the OpenAPI 3.1 document above as an Action (no authentication - the anonymous tier is keyless). The same OpenAPI document works unmodified as the Action schema.

Live worked examples

Every link below is a live call that returns real JSON (or, if your client prefers HTML, a readable page carrying the same data). Each is an absolute www.homegadgets.ca URL and each returns 200. An agent can follow any of them without reading a line of this prose.

Every link is a clean PATH with no question mark, so a browsing connector will fetch it. Each returns 200 with real JSON (or a readable HTML page if the client prefers HTML).

What it answersClickable call (no query string)
Best price for a Frigidaire fridge (FRFG2033AV)https://www.homegadgets.ca/public/v1/price/FRFG2033AV
Same, restricted to Ontario deliveryhttps://www.homegadgets.ca/public/v1/price/FRFG2033AV/ON
Best price for a GE built-in fridge (CDE06RP3ND1)https://www.homegadgets.ca/public/v1/price/CDE06RP3ND1
Best price for a Whirlpool washer (WTW5057LW)https://www.homegadgets.ca/public/v1/price/WTW5057LW
Search the catalog (free text, path form)https://www.homegadgets.ca/public/v1/search/french%20door%20refrigerator
The human-and-agent price page (indexable)https://www.homegadgets.ca/price/FRFG2033AV
OpenAPI 3.1 document (import as a Custom GPT Action)https://www.homegadgets.ca/public/v1/openapi.json

Anonymous limits

These are hard numbers, not guidance:

LimitValueScope
Requests per IP per day10Each caller, counted against an irreversible fingerprint of the request, never a stored IP address.
Requests per day, global250Shared by ALL anonymous callers together. It binds even when every individual caller is under 10/day.
Catalog rows per day200Per caller. Offers and search results are rows; the envelope around them is not.
Offers per product2The two cheapest. The true retailer count is always returned alongside.
Search results20Per request, hard maximum.
Reset00:00 UTCAll daily budgets reset together.

Every response carries X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset (seconds until reset), plus X-HG-Tier. When a budget is spent you get 429 with Retry-After and a documented JSON body - never an empty response and never a 5xx. Read the body, do not retry-loop: a spent daily budget will not free up before 00:00 UTC.

Successful catalog responses are cacheable (Cache-Control: public, max-age=3600, s-maxage=21600). Respecting that cache is the cheapest way to stay inside 10/day.

Error codes

Every error is JSON in one shape, so you can branch on error.code without parsing prose:

{
  "error": {
    "code": "anonymous_daily_quota_reached",
    "message": "Anonymous callers get 10 requests/day. Yours are spent; the count resets at 00:00 UTC. An API key removes this limit.",
    "tier": "anonymous",
    "resets_at": "00:00 UTC",
    "docs": "https://www.homegadgets.ca/terms"
  },
  "currency": "CAD",
  "source": "https://www.homegadgets.ca"
}
HTTPerror.codeMeaningWhat to do
400bad_requestMissing, ambiguous or empty identifier: e.g. both mpn and product_id, or neither; empty q.Fix the request. Retrying is pointless.
404not_foundNo in-stock Canadian product matches that identifier. Out-of-stock and archived products are not served.Try /public/v1/search to resolve the identifier.
429anonymous_daily_quota_reachedEither your 10/day or the shared 250/day anonymous ceiling is spent.Stop until 00:00 UTC. Tell your user the quota is spent.
429rate_limitedToo many requests in a short window.Back off for the Retry-After interval.
429row_budget_exhaustedThe daily budget of catalog ROWS is spent, not the request count.Stop until 00:00 UTC.
429quota_exhaustedA key’s own daily quota is spent (keyed tiers only).Stop until 00:00 UTC.
5xx-A fault on our side.Retry once with backoff; report it if it persists.

We collect no personal data through this API

Using the anonymous API requires no account, no email address, no name, no signup and no key, and we store no personal information about you in order to serve it. Specifically, and permanently:

What we do keep is aggregate operational telemetry: which route was called, how many rows it returned, how long it took, and which retailer buy links were served. That is how the service is capacity-planned and how affiliate links are reconciled. See our Privacy Policy.

Attribution and licence

Access is granted under these terms, alongside our Terms of Use. Using the API means accepting them.

Live tier (issued key)

Live (“Price Now”) fetches prices across Canadian retailers at the time you ask, and returns the full per-retailer offer list rather than the two cheapest. A live fetch can take up to ~90 seconds - too long for one blocking request - so it is submit-a-job-then-poll: you POST a job, get a job_id immediately, and poll until status is done.

EndpointAuthPurpose
POST /api/v1/ondemand/price-jobsX-API-KeySubmit one of mpn, canonical_id or query (exactly one, ≤200 chars).
GET /api/v1/ondemand/price-jobs/{job_id}X-API-KeyPoll for pending | claimed | done | error.
GET /api/v1/ondemand/healthnoneQueue readiness.
# 1) submit a price job (returns immediately)
POST /api/v1/ondemand/price-jobs
X-API-Key: <your-api-key>
Content-Type: application/json

{ "mpn": "ABC123" }

  -> 200  { "job_id": "b3f1...", "status": "pending" }

# 2) poll until status is "done" (live fetch ~60-90s)
GET /api/v1/ondemand/price-jobs/b3f1...
X-API-Key: <your-api-key>
{
  "job_id": "b3f1...",
  "status": "done",
  "meta": { "created_at": "...", "claimed_at": "...", "done_at": "...", "ceiling_s": 90 },
  "result": {
    "best": {
      "price_cad": 2798.0,
      "retailer": "retailer_x",
      "buy_link": "https://retailer-x.example/p/abc123"
    },
    "offers": [
      { "retailer": "retailer_x", "price_cad": 2798.0, "availability": "in_stock",
        "buy_link": "https://retailer-x.example/p/abc123" },
      { "retailer": "retailer_y", "price_cad": 2849.0, "availability": "in_stock",
        "buy_link": "https://retailer-y.example/p/abc123" },
      { "retailer": "retailer_z", "price_cad": 2899.0, "availability": "in_stock",
        "buy_link": "https://retailer-z.example/p/abc123" }
    ]
  }
}

Live-tier errors use the same { "error": { "code", "message" } } shape: unauthorized / invalid_api_key (401), bad_request (400), not_found (404), rate_limited (429), queue_unavailable (503). A job that exceeds the 90-second ceiling comes back as status: "error" with ceiling_exceeded - a poll always reaches closure rather than hanging.

Requesting a key

Keys are issued manually to approved partners. There is no self-serve signup and no free key. Tell us who you are and what you want to build, and we will follow up with a key, the full endpoint reference and interactive documentation. Request access via homegadgets.ca/contact.

Coverage notes

Canada only; all prices in CAD. Coverage varies by product - a given product may return a subset of Canadian retailers, and products with no in-stock Canadian offer are not served at all (they return 404). Headline prices are new, first-party retailer offers; marketplace, open-box and refurbished listings are never presented as the cheapest price. The API is read-only: it reports prices and buy links, it does not place orders.