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 returns | The 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. |
| Signup | None. No account, no email, no key. | Keys are issued manually on approval. |
| Budget | 10 requests/day per IP, inside a shared 250 requests/day ceiling for all anonymous traffic. | Per-key request, rate and row budgets. |
| Latency | Immediate (cached catalog). | Up to ~90 s: submit a job, then poll. |
| Base URL | https://www.homegadgets.ca | https://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.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
mpn | string | one of | Manufacturer part / model number. Exact, case-insensitive. |
product_id | uuid | one of | HomeGadgets product id, as returned by search. |
province | string | optional | Canadian 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.
GET /public/v1/search
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.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
q | string | yes | Free text. Multi-word queries are matched as AND across brand, name and model number. "65 inch" is normalised to 65". |
limit | integer | optional | Results 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.
| Document | URL |
|---|---|
| OpenAPI 3.1 | https://www.homegadgets.ca/public/v1/openapi.json |
| Plugin manifest | https://www.homegadgets.ca/.well-known/ai-plugin.json |
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 answers | Clickable call (no query string) |
|---|---|
| Best price for a Frigidaire fridge (FRFG2033AV) | https://www.homegadgets.ca/public/v1/price/FRFG2033AV |
| Same, restricted to Ontario delivery | https://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:
| Limit | Value | Scope |
|---|---|---|
| Requests per IP per day | 10 | Each caller, counted against an irreversible fingerprint of the request, never a stored IP address. |
| Requests per day, global | 250 | Shared by ALL anonymous callers together. It binds even when every individual caller is under 10/day. |
| Catalog rows per day | 200 | Per caller. Offers and search results are rows; the envelope around them is not. |
| Offers per product | 2 | The two cheapest. The true retailer count is always returned alongside. |
| Search results | 20 | Per request, hard maximum. |
| Reset | 00:00 UTC | All 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"
}| HTTP | error.code | Meaning | What to do |
|---|---|---|---|
| 400 | bad_request | Missing, ambiguous or empty identifier: e.g. both mpn and product_id, or neither; empty q. | Fix the request. Retrying is pointless. |
| 404 | not_found | No in-stock Canadian product matches that identifier. Out-of-stock and archived products are not served. | Try /public/v1/search to resolve the identifier. |
| 429 | anonymous_daily_quota_reached | Either your 10/day or the shared 250/day anonymous ceiling is spent. | Stop until 00:00 UTC. Tell your user the quota is spent. |
| 429 | rate_limited | Too many requests in a short window. | Back off for the Retry-After interval. |
| 429 | row_budget_exhausted | The daily budget of catalog ROWS is spent, not the request count. | Stop until 00:00 UTC. |
| 429 | quota_exhausted | A 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:
- No raw IP address is ever stored or logged. To count requests against the 10/day limit, a caller is reduced to an irreversible salted hash. The counter cannot be turned back into a list of visitors, and it is held in memory, not in a table of people.
- No email or identity is collected anywhere: there is no signup route, no account, and no free-key-for-your-email path. That is a deliberate decision, not a missing feature: information we never collect is information we can never lose.
- No request bodies or raw query strings are retained. Usage records keep the route template and a fixed whitelist of catalog parameters (such as
mpnandq); anything else is dropped, and only the number of dropped keys is kept. So do not put anything personal in a query string: it will be discarded rather than honoured. - No cookies, no tracking pixels, no fingerprinting for advertising. Nothing from this API is sold or shared with data brokers.
- Keys, when issued, are stored as a hash, never the key itself.
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.
- Attribution is required when you display our data. Credit “HomeGadgets.ca” and link to the product page we return in
product.pdp, or tohttps://www.homegadgets.ca. An AI agent answering from this API should name HomeGadgets.ca as the source in its answer. - Quote the comparison honestly. Anonymous responses contain the two cheapest offers out of
retailer_countretailers. Say “cheapest of N Canadian retailers” - never an unqualified “cheapest”, and never imply the two offers are all that exist. Carry thedisclaimerand theas_oftimestamp; prices change and ours is an observation, not a quote. - Buy links must be passed through unmodified. The
buy_linkwe return carries source and affiliate parameters. Do not strip, rewrite, reorder or substitute them. If you cannot use the link as given, do not present the offer. - The licence is to answer a question, not to build a copy. You may query, display and cache what you retrieve for your own use. You may not systematically enumerate, bulk-extract, redistribute or resell the catalog, nor use it to build or train a competing price database. Bulk and derivative use is a separate licensed product - ask.
- Do not evade the budgets. Rotating IPs or clients to exceed 10/day is a breach of these terms even where it is technically possible.
- No warranty. Prices are collected from public retailer pages and may be stale, incomplete or wrong. They are informational only. Verify at the retailer before buying or advising anyone to buy. Product names and trademarks belong to their owners.
- The anonymous tier can change. Budgets, shape and availability may be adjusted; nothing here is a service-level commitment. Build for the documented
429.
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.
| Endpoint | Auth | Purpose |
|---|---|---|
POST /api/v1/ondemand/price-jobs | X-API-Key | Submit one of mpn, canonical_id or query (exactly one, ≤200 chars). |
GET /api/v1/ondemand/price-jobs/{job_id} | X-API-Key | Poll for pending | claimed | done | error. |
GET /api/v1/ondemand/health | none | Queue 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.