Get started
Quickstart
Go from API key to competitor prices in about five minutes. Pick your language in any code panel and every panel follows.
Get your key#
Create your key in GetPrice: Settings → API & Integrations → Create API key. Only an admin can do this, and your organisation has one key. The full key is shown once, so copy it to your secret store before you close the dialog. Store it where your code can read it, never in the code itself.
The 8 characters after
gp_live_are your key's prefix. The prefix isn't secret, so quote it to support when you need to identify a key. After the key is created, Settings shows only the prefix and the last 4 characters.export GETPRICE_API_KEY="gp_live_…" # your keyexport GETPRICE_API_KEY="gp_live_…" # your key # Python 3.9+ with requests: # pip install requestsexport GETPRICE_API_KEY="gp_live_…" # your key # Node.js 22.18+ runs a .ts file directly: # node quickstart.ts # Node.js 20+: save the code as .mts, then # npx tsx quickstart.mtsexport GETPRICE_API_KEY="gp_live_…" # your key # PHP 8.2+ with the curl extension: # php quickstart.phpexport GETPRICE_API_KEY="gp_live_…" # your key # .NET 8: dotnet new console, paste the code # into Program.cs, then dotnet run# Once, in odoo-bin shell -d <your-database>. # Or: Settings > Technical > System Parameters. icp = env["ir.config_parameter"].sudo() icp.set_param("getprice.api_key", "gp_live_…") env.cr.commit()Your keygp_live_k3Xa91Qz_••••••••••••••••••••••••••••••••••••••Make your first request#
Call
GET /v1/meto check that your key works. It returns your key's name, your company, the key's scopes and your rate limit.A
401means the key is missing or wrong. See Errors.BASE=https://api.getprice.ai/v1 curl "$BASE/me" \ -H "Authorization: Bearer $GETPRICE_API_KEY"import os import requests BASE = "https://api.getprice.ai/v1" KEY = os.environ["GETPRICE_API_KEY"] resp = requests.get( f"{BASE}/me", headers={"Authorization": f"Bearer {KEY}"}, timeout=30, ) resp.raise_for_status() me = resp.json() name = me["company"]["name"] per_minute = me["limits"]["per_minute"] print(f"Connected: {name}, {per_minute}/min")interface Me { company: { name: string }; limits: { per_minute: number }; } const BASE = "https://api.getprice.ai/v1"; const KEY = process.env.GETPRICE_API_KEY; const url = `${BASE}/me`; const res = await fetch(url, { headers: { Authorization: `Bearer ${KEY}` }, }); if (!res.ok) { throw new Error(`GetPrice returned ${res.status}`); } const me = (await res.json()) as Me; const { name } = me.company; const perMinute = me.limits.per_minute; console.log(`Connected: ${name}, ${perMinute}/min`);<?php $base = "https://api.getprice.ai/v1"; $key = getenv("GETPRICE_API_KEY"); $ch = curl_init("$base/me"); curl_setopt_array($ch, [ CURLOPT_HTTPHEADER => [ "Authorization: Bearer $key", ], CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 30, ]); $body = curl_exec($ch); $status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE); if ($body === false || $status >= 400) { throw new RuntimeException( "GetPrice returned $status: $body" ); } $data = json_decode( $body, true, flags: JSON_THROW_ON_ERROR ); printf( "Connected: %s, %d/min\n", $data["company"]["name"], $data["limits"]["per_minute"], );using System.Net.Http.Headers; using System.Net.Http.Json; using System.Text.Json; using Naming = System.Text.Json.JsonNamingPolicy; var key = Environment.GetEnvironmentVariable( "GETPRICE_API_KEY"); var api = new Uri("https://api.getprice.ai/v1/"); var http = new HttpClient { BaseAddress = api }; http.Timeout = TimeSpan.FromSeconds(30); http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", key); var json = new JsonSerializerOptions { PropertyNamingPolicy = Naming.SnakeCaseLower, }; var res = await http.GetAsync("me"); res.EnsureSuccessStatusCode(); var me = await res.Content .ReadFromJsonAsync<Me>(json); var name = me!.Company.Name; var perMinute = me.Limits.PerMinute; Console.WriteLine( $"Connected: {name}, {perMinute}/min"); record Me(Company Company, Limits Limits); record Company(string Name); record Limits(int PerMinute);# In odoo-bin shell import requests BASE = "https://api.getprice.ai/v1" icp = env["ir.config_parameter"].sudo() KEY = icp.get_param("getprice.api_key") resp = requests.get( f"{BASE}/me", headers={"Authorization": f"Bearer {KEY}"}, timeout=30, ) resp.raise_for_status() me = resp.json() name = me["company"]["name"] per_minute = me["limits"]["per_minute"] print(f"Connected: {name}, {per_minute}/min")Response{ "key": { "name": "Odoo production", "prefix": "k3Xa91Qz", "environment": "live" }, "company": { "name": "Example Wholesale (Pty) Ltd" }, "scopes": [ "catalog:read", "matches:read", "skus:write" ], "limits": { "per_minute": 120, "window_seconds": 60 } }Look up a SKU#
Ask for the comparison set behind one of your SKUs. Repeat
skuto look up to 100 SKUs in one request.skustells you what happened to each SKU you sent.foundmeans the set is indata. See Core concepts for the other statuses.BASE=https://api.getprice.ai/v1 curl -G "$BASE/comparison-sets" \ -H "Authorization: Bearer $GETPRICE_API_KEY" \ --data-urlencode "sku=10072L"import json import os import requests BASE = "https://api.getprice.ai/v1" KEY = os.environ["GETPRICE_API_KEY"] resp = requests.get( f"{BASE}/comparison-sets", params=[("sku", "10072L")], headers={"Authorization": f"Bearer {KEY}"}, timeout=30, ) resp.raise_for_status() print(json.dumps(resp.json(), indent=2))const BASE = "https://api.getprice.ai/v1"; const KEY = process.env.GETPRICE_API_KEY; const params = new URLSearchParams({ sku: "10072L" }); const url = `${BASE}/comparison-sets?${params}`; const res = await fetch(url, { headers: { Authorization: `Bearer ${KEY}` }, }); if (!res.ok) { throw new Error(`GetPrice returned ${res.status}`); } const page = await res.json(); console.log(JSON.stringify(page, null, 2));<?php $base = "https://api.getprice.ai/v1"; $key = getenv("GETPRICE_API_KEY"); $sku = rawurlencode("10072L"); $ch = curl_init("$base/comparison-sets?sku=$sku"); curl_setopt_array($ch, [ CURLOPT_HTTPHEADER => [ "Authorization: Bearer $key", ], CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 30, ]); $body = curl_exec($ch); $status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE); if ($body === false || $status >= 400) { throw new RuntimeException( "GetPrice returned $status: $body" ); } $data = json_decode($body, flags: JSON_THROW_ON_ERROR); echo json_encode( $data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES, ), "\n";using System.Net.Http.Headers; using System.Text.Json; var key = Environment.GetEnvironmentVariable( "GETPRICE_API_KEY"); var api = new Uri("https://api.getprice.ai/v1/"); var http = new HttpClient { BaseAddress = api }; http.Timeout = TimeSpan.FromSeconds(30); http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", key); var res = await http.GetAsync( "comparison-sets?sku=10072L"); res.EnsureSuccessStatusCode(); var body = await res.Content.ReadAsStringAsync(); using var doc = JsonDocument.Parse(body); var pretty = new JsonSerializerOptions { WriteIndented = true, }; Console.WriteLine( JsonSerializer.Serialize(doc.RootElement, pretty));# In odoo-bin shell import json import requests BASE = "https://api.getprice.ai/v1" icp = env["ir.config_parameter"].sudo() KEY = icp.get_param("getprice.api_key") resp = requests.get( f"{BASE}/comparison-sets", params=[("sku", "10072L")], headers={"Authorization": f"Bearer {KEY}"}, timeout=30, ) resp.raise_for_status() print(json.dumps(resp.json(), indent=2))Response{ "data": [ { "id": "set_x7Kp2Lm9", "name": "Baked Beans in Tomato Sauce 410g", "sku": "10072L", "own_product": { "id": "prd_8fK2mQa1", "name": "Baked Beans in Tomato Sauce 410g", "url": "https://shop.example.com/product/10072L", "price": { "amount": "18.99", "was": null, "currency": "ZAR" }, "last_collected_at": "2026-11-02T04:12:09Z", "stale": false }, "competitors": [ { "competitor": { "id": "cmp_3LqV8nP2", "name": "Harbour Market", "domain": "harbourmarket.example.com" }, "product": { "id": "prd_Q2vN9cD4", "name": "Baked Beans 410g", "url": "https://harbourmarket.example.com/baked-beans-in-tomato-sauce-410g" }, "price": { "amount": "17.99", "was": "19.99", "currency": "ZAR" }, "last_collected_at": "2026-11-02T03:05:11Z", "availability": "unknown", "status": "active", "stale": false, "match": { "review": "same", "reviewed_by": "customer", "confidence": null } }, { "competitor": { "id": "cmp_7RwK4tB9", "name": "Cape Fresh", "domain": "capefresh.example.com" }, "product": { "id": "prd_Hm3Wc8Kp", "name": "Baked Beans In Tomato Sauce 410g", "url": "https://capefresh.example.com/baked-beans-in-tomato-sauce-410g" }, "price": { "amount": "18.99", "was": null, "currency": "ZAR" }, "last_collected_at": "2026-11-02T03:11:40Z", "availability": "unknown", "status": "active", "stale": false, "match": { "review": "same", "reviewed_by": "getprice", "confidence": 0.94 } }, { "competitor": { "id": "cmp_9TzM2cX5", "name": "Northwind Wholesale", "domain": "northwindwholesale.example.com" }, "product": { "id": "prd_Zt6Pa2Rn", "name": "Baked Beans In Tomato Sauce 410g", "url": "https://northwindwholesale.example.com/baked-beans-in-tomato-sauce-410g" }, "price": { "amount": "17.99", "was": null, "currency": "ZAR" }, "last_collected_at": "2026-11-02T03:20:05Z", "availability": "unknown", "status": "active", "stale": false, "match": { "review": "unreviewed", "reviewed_by": null, "confidence": 0.81 } }, { "competitor": { "id": "cmp_2HdR6wJ3", "name": "Metro Bulk", "domain": "metrobulk.example.com" }, "product": { "id": "prd_Yc1Ke7Fs", "name": "Baked Beans in Tomato Sauce 24 x 410g", "url": "https://metrobulk.example.com/baked-beans-24x410g" }, "price": { "amount": "429.00", "was": null, "currency": "ZAR" }, "last_collected_at": "2026-10-24T04:31:58Z", "availability": "unknown", "status": "active", "stale": true, "match": { "review": "with_differences", "reviewed_by": "customer", "confidence": null } } ], "integrity": "ok", "updated_at": "2026-11-02T04:14:00Z" } ], "next_cursor": null, "skus": [ { "sku": "10072L", "status": "found", "set_id": "set_x7Kp2Lm9", "product_id": "prd_8fK2mQa1" } ] }Read the competitor prices#
Each entry in
competitorsis one retailer's listing of the same item. Readpricefor what they charge andlast_collected_atfor when GetPrice last read it.Harbour Market is on promotion: its
price.wasis R19.99, so shoppers save R2.00. Two of these need a second look. Metro Bulk's listing is a 24-pack kept on purpose (review: "with_differences"), and its price is stale. Northwind Wholesale's match isn't confirmed yet (review: "unreviewed").Next, read Core concepts, or go straight to the Nightly ERP sync guide.
BASE=https://api.getprice.ai/v1 curl -sG "$BASE/comparison-sets" \ -H "Authorization: Bearer $GETPRICE_API_KEY" \ --data-urlencode "sku=10072L" | jq -r '.data[].competitors[] | select(.price != null) | "\(.competitor.name): R\(.price.amount)" + " at \(.last_collected_at)" + (if .stale then " (stale)" else "" end)'import os import requests BASE = "https://api.getprice.ai/v1" KEY = os.environ["GETPRICE_API_KEY"] resp = requests.get( f"{BASE}/comparison-sets", params=[("sku", "10072L")], headers={"Authorization": f"Bearer {KEY}"}, timeout=30, ) resp.raise_for_status() for cset in resp.json()["data"]: for e in cset["competitors"]: if e["price"] is None: continue name = e["competitor"]["name"] amount = e["price"]["amount"] when = e["last_collected_at"] stale = " (stale)" if e["stale"] else "" print(f"{name}: R{amount} at {when}{stale}")interface Entry { competitor: { name: string }; price: { amount: string } | null; last_collected_at: string | null; stale: boolean; } interface Page { data: { competitors: Entry[] }[]; } const BASE = "https://api.getprice.ai/v1"; const KEY = process.env.GETPRICE_API_KEY; const params = new URLSearchParams({ sku: "10072L" }); const url = `${BASE}/comparison-sets?${params}`; const res = await fetch(url, { headers: { Authorization: `Bearer ${KEY}` }, }); if (!res.ok) { throw new Error(`GetPrice returned ${res.status}`); } const page = (await res.json()) as Page; for (const set of page.data) { for (const e of set.competitors) { if (!e.price) continue; const stale = e.stale ? " (stale)" : ""; console.log( `${e.competitor.name}: R${e.price.amount}` + ` at ${e.last_collected_at}${stale}`, ); } }<?php $base = "https://api.getprice.ai/v1"; $key = getenv("GETPRICE_API_KEY"); $sku = rawurlencode("10072L"); $ch = curl_init("$base/comparison-sets?sku=$sku"); curl_setopt_array($ch, [ CURLOPT_HTTPHEADER => [ "Authorization: Bearer $key", ], CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 30, ]); $body = curl_exec($ch); $status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE); if ($body === false || $status >= 400) { throw new RuntimeException( "GetPrice returned $status: $body" ); } $data = json_decode( $body, true, flags: JSON_THROW_ON_ERROR ); foreach ($data["data"] as $set) { foreach ($set["competitors"] as $e) { if ($e["price"] === null) { continue; } printf( "%s: R%s at %s%s\n", $e["competitor"]["name"], $e["price"]["amount"], $e["last_collected_at"], $e["stale"] ? " (stale)" : "", ); } }using System.Net.Http.Headers; using System.Net.Http.Json; using System.Text.Json; using Naming = System.Text.Json.JsonNamingPolicy; var key = Environment.GetEnvironmentVariable( "GETPRICE_API_KEY"); var api = new Uri("https://api.getprice.ai/v1/"); var http = new HttpClient { BaseAddress = api }; http.Timeout = TimeSpan.FromSeconds(30); http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", key); var json = new JsonSerializerOptions { PropertyNamingPolicy = Naming.SnakeCaseLower, }; var res = await http.GetAsync( "comparison-sets?sku=10072L"); res.EnsureSuccessStatusCode(); var page = await res.Content .ReadFromJsonAsync<Page>(json); foreach (var set in page!.Data) { foreach (var e in set.Competitors) { if (e.Price is null) continue; var stale = e.Stale ? " (stale)" : ""; Console.WriteLine( $"{e.Competitor.Name}: R{e.Price.Amount}" + $" at {e.LastCollectedAt}{stale}"); } } record Page(List<CompSet> Data); record CompSet(List<Entry> Competitors); record Entry( Competitor Competitor, Price? Price, string? LastCollectedAt, bool Stale); record Competitor(string Name); record Price(string Amount);# In odoo-bin shell import requests BASE = "https://api.getprice.ai/v1" icp = env["ir.config_parameter"].sudo() KEY = icp.get_param("getprice.api_key") resp = requests.get( f"{BASE}/comparison-sets", params=[("sku", "10072L")], headers={"Authorization": f"Bearer {KEY}"}, timeout=30, ) resp.raise_for_status() for cset in resp.json()["data"]: for e in cset["competitors"]: if e["price"] is None: continue name = e["competitor"]["name"] amount = e["price"]["amount"] when = e["last_collected_at"] stale = " (stale)" if e["stale"] else "" print(f"{name}: R{amount} at {when}{stale}")OutputHarbour Market: R17.99 at 2026-11-02T03:05:11Z Cape Fresh: R18.99 at 2026-11-02T03:11:40Z Northwind Wholesale: R17.99 at 2026-11-02T03:20:05Z Metro Bulk: R429.00 at 2026-10-24T04:31:58Z (stale)