[{"data":1,"prerenderedAt":1936},["ShallowReactive",2],{"content-\u002Fguides\u002Fmonitor-a-price-step-api":3,"content-doc-\u002Fguides\u002Fmonitor-a-price-step-api-inline":1935},{"id":4,"title":5,"body":6,"date":1919,"description":1920,"extension":1921,"meta":1922,"navigation":130,"order":1919,"path":1931,"seo":1932,"stem":1933,"__hash__":1934},"guides\u002Fguides\u002Fmonitor-a-price-step-api.md","Monitor a product price for changes using the Step API",{"type":7,"value":8,"toc":1909},"minimark",[9,13,18,21,63,67,69,82,93,427,444,455,458,529,533,535,538,733,737,739,742,929,933,935,938,1092,1099,1103,1105,1108,1124,1131,1135,1137,1867,1873,1877,1879,1905],[10,11,12],"p",{},"The Step API lets you drive a cloud browser session from your own code, one action at a time. This guide scrapes a single product price, stores it, and sends an alert whenever the price changes on the next run. The example tracks a pair of trainers on secretsales.com, but the pattern works on any product page.",[14,15,17],"h2",{"id":16},"before-you-begin","Before you begin",[19,20],"hr",{},[22,23,24,33,44,57],"ul",{},[25,26,27,28,32],"li",{},"Node.js 18 or later, so the built-in ",[29,30,31],"code",{},"fetch"," is available.",[25,34,35,36,39,40,43],{},"The ",[29,37,38],{},"axiom-api"," library installed: ",[29,41,42],{},"npm install axiom-api",".",[25,45,46,47,52,53,56],{},"An axiom.ai API key from the ",[48,49,51],"a",{"href":50},"\u002Fdocs\u002Fcode\u002Ftoken-generation","Code Dashboard",", set as the ",[29,54,55],{},"AXIOM_API_KEY"," environment variable.",[25,58,59,60,43],{},"A webhook URL to receive alerts (Slack, Discord, or any endpoint that accepts JSON), set as ",[29,61,62],{},"ALERT_WEBHOOK_URL",[14,64,66],{"id":65},"scrape-the-price","Scrape the price",[19,68],{},[10,70,71,77,78,81],{},[48,72,74],{"href":73},"\u002Fdocs\u002Fdeveloper-hub\u002Fapi\u002Fstep-functions\u002Fscrape",[29,75,76],{},"axiom.scrape()"," extracts records from the current page using a CSS selector you choose at runtime. On the product page, the price sits in an element whose class contains ",[29,79,80],{},"productFullDetail-productPriceFinal",", so match it with a partial-class selector.",[10,83,84,85,88,89,92],{},"The class name carries a generated suffix (something like ",[29,86,87],{},"productFullDetail-productPriceFinal-a1b2c",") that changes when the site rebuilds. Matching the stable substring with ",[29,90,91],{},"[class*='...']"," keeps the selector working across those rebuilds.",[94,95,100],"pre",{"className":96,"code":97,"language":98,"meta":99,"style":99},"language-javascript shiki shiki-themes github-light-default github-dark-default","import { AxiomApi } from 'axiom-api';\n\nconst axiom = new AxiomApi(process.env.AXIOM_API_KEY);\n\nconst PRODUCT_URL =\n  \"https:\u002F\u002Fwww.secretsales.com\u002F66165d1f6b502-nike-nike-air-trainer-1-essential-mens-white-trainers-white\u002F\";\n\nasync function getPrice() {\n  await axiom.browserOpen();\n  try {\n    await axiom.goto(PRODUCT_URL);\n\n    const rows = await axiom.scrape(\n      null,                                                            \u002F\u002F scrape the current page\n      { hierarchy: \"[class*='productFullDetail-productPriceFinal']\" }, \u002F\u002F partial class match\n      null,                                                            \u002F\u002F no pagination\n      1,                                                               \u002F\u002F one record\n      {}                                                               \u002F\u002F default settings\n    );\n\n    \u002F\u002F scrape() returns a 2D array (rows x fields), so the value is at [0][0]\n    return rows?.[0]?.[0]?.trim() ?? null;                             \u002F\u002F e.g. \"Â£54.99\"\n  } finally {\n    await axiom.browserClose();                                       \u002F\u002F always free the session\n  }\n}\n","javascript","",[29,101,102,125,132,160,165,176,184,189,204,219,228,247,252,274,287,302,312,324,333,339,344,350,388,399,415,421],{"__ignoreMap":99},[103,104,107,111,115,118,122],"span",{"class":105,"line":106},"line",1,[103,108,110],{"class":109},"sjeE4","import",[103,112,114],{"class":113},"s4rv2"," { AxiomApi } ",[103,116,117],{"class":109},"from",[103,119,121],{"class":120},"sSVrQ"," 'axiom-api'",[103,123,124],{"class":113},";\n",[103,126,128],{"class":105,"line":127},2,[103,129,131],{"emptyLinePlaceholder":130},true,"\n",[103,133,135,138,142,145,148,152,155,157],{"class":105,"line":134},3,[103,136,137],{"class":109},"const",[103,139,141],{"class":140},"sHrmB"," axiom",[103,143,144],{"class":109}," =",[103,146,147],{"class":109}," new",[103,149,151],{"class":150},"sbjLL"," AxiomApi",[103,153,154],{"class":113},"(process.env.",[103,156,55],{"class":140},[103,158,159],{"class":113},");\n",[103,161,163],{"class":105,"line":162},4,[103,164,131],{"emptyLinePlaceholder":130},[103,166,168,170,173],{"class":105,"line":167},5,[103,169,137],{"class":109},[103,171,172],{"class":140}," PRODUCT_URL",[103,174,175],{"class":109}," =\n",[103,177,179,182],{"class":105,"line":178},6,[103,180,181],{"class":120},"  \"https:\u002F\u002Fwww.secretsales.com\u002F66165d1f6b502-nike-nike-air-trainer-1-essential-mens-white-trainers-white\u002F\"",[103,183,124],{"class":113},[103,185,187],{"class":105,"line":186},7,[103,188,131],{"emptyLinePlaceholder":130},[103,190,192,195,198,201],{"class":105,"line":191},8,[103,193,194],{"class":109},"async",[103,196,197],{"class":109}," function",[103,199,200],{"class":150}," getPrice",[103,202,203],{"class":113},"() {\n",[103,205,207,210,213,216],{"class":105,"line":206},9,[103,208,209],{"class":109},"  await",[103,211,212],{"class":113}," axiom.",[103,214,215],{"class":150},"browserOpen",[103,217,218],{"class":113},"();\n",[103,220,222,225],{"class":105,"line":221},10,[103,223,224],{"class":109},"  try",[103,226,227],{"class":113}," {\n",[103,229,231,234,236,239,242,245],{"class":105,"line":230},11,[103,232,233],{"class":109},"    await",[103,235,212],{"class":113},[103,237,238],{"class":150},"goto",[103,240,241],{"class":113},"(",[103,243,244],{"class":140},"PRODUCT_URL",[103,246,159],{"class":113},[103,248,250],{"class":105,"line":249},12,[103,251,131],{"emptyLinePlaceholder":130},[103,253,255,258,261,263,266,268,271],{"class":105,"line":254},13,[103,256,257],{"class":109},"    const",[103,259,260],{"class":140}," rows",[103,262,144],{"class":109},[103,264,265],{"class":109}," await",[103,267,212],{"class":113},[103,269,270],{"class":150},"scrape",[103,272,273],{"class":113},"(\n",[103,275,277,280,283],{"class":105,"line":276},14,[103,278,279],{"class":140},"      null",[103,281,282],{"class":113},",                                                            ",[103,284,286],{"class":285},"sU953","\u002F\u002F scrape the current page\n",[103,288,290,293,296,299],{"class":105,"line":289},15,[103,291,292],{"class":113},"      { hierarchy: ",[103,294,295],{"class":120},"\"[class*='productFullDetail-productPriceFinal']\"",[103,297,298],{"class":113}," }, ",[103,300,301],{"class":285},"\u002F\u002F partial class match\n",[103,303,305,307,309],{"class":105,"line":304},16,[103,306,279],{"class":140},[103,308,282],{"class":113},[103,310,311],{"class":285},"\u002F\u002F no pagination\n",[103,313,315,318,321],{"class":105,"line":314},17,[103,316,317],{"class":140},"      1",[103,319,320],{"class":113},",                                                               ",[103,322,323],{"class":285},"\u002F\u002F one record\n",[103,325,327,330],{"class":105,"line":326},18,[103,328,329],{"class":113},"      {}                                                               ",[103,331,332],{"class":285},"\u002F\u002F default settings\n",[103,334,336],{"class":105,"line":335},19,[103,337,338],{"class":113},"    );\n",[103,340,342],{"class":105,"line":341},20,[103,343,131],{"emptyLinePlaceholder":130},[103,345,347],{"class":105,"line":346},21,[103,348,349],{"class":285},"    \u002F\u002F scrape() returns a 2D array (rows x fields), so the value is at [0][0]\n",[103,351,353,356,359,362,365,367,370,373,376,379,382,385],{"class":105,"line":352},22,[103,354,355],{"class":109},"    return",[103,357,358],{"class":113}," rows?.[",[103,360,361],{"class":140},"0",[103,363,364],{"class":113},"]?.[",[103,366,361],{"class":140},[103,368,369],{"class":113},"]?.",[103,371,372],{"class":150},"trim",[103,374,375],{"class":113},"() ",[103,377,378],{"class":109},"??",[103,380,381],{"class":140}," null",[103,383,384],{"class":113},";                             ",[103,386,387],{"class":285},"\u002F\u002F e.g. \"Â£54.99\"\n",[103,389,391,394,397],{"class":105,"line":390},23,[103,392,393],{"class":113},"  } ",[103,395,396],{"class":109},"finally",[103,398,227],{"class":113},[103,400,402,404,406,409,412],{"class":105,"line":401},24,[103,403,233],{"class":109},[103,405,212],{"class":113},[103,407,408],{"class":150},"browserClose",[103,410,411],{"class":113},"();                                       ",[103,413,414],{"class":285},"\u002F\u002F always free the session\n",[103,416,418],{"class":105,"line":417},25,[103,419,420],{"class":113},"  }\n",[103,422,424],{"class":105,"line":423},26,[103,425,426],{"class":113},"}\n",[428,429,430],"blockquote",{},[10,431,432,436,437,443],{},[433,434,435],"strong",{},"Note:"," If the price is exposed in the page's structured data rather than a class, ",[48,438,440],{"href":439},"\u002Fdocs\u002Fdeveloper-hub\u002Fapi\u002Fstep-functions\u002Fscrape-metadata",[29,441,442],{},"axiom.scrapeMetadata()"," is a lighter way to read a single field.",[428,445,446],{},[10,447,448,451,452,454],{},[433,449,450],{},"Warning:"," An open session consumes runtime quota. Always close it in a ",[29,453,396],{}," block so a mid-script error can't leave it running.",[10,456,457],{},"Convert the scraped string to a number so you can compare it later.",[94,459,461],{"className":96,"code":460,"language":98,"meta":99,"style":99},"function toNumber(priceText) {\n  return Number(priceText.replace(\u002F[^0-9.]\u002Fg, \"\")); \u002F\u002F \"Â£54.99\" -> 54.99\n}\n",[29,462,463,480,525],{"__ignoreMap":99},[103,464,465,468,471,473,477],{"class":105,"line":106},[103,466,467],{"class":109},"function",[103,469,470],{"class":150}," toNumber",[103,472,241],{"class":113},[103,474,476],{"class":475},"sTDnQ","priceText",[103,478,479],{"class":113},") {\n",[103,481,482,485,488,491,494,496,499,502,505,508,510,513,516,519,522],{"class":105,"line":127},[103,483,484],{"class":109},"  return",[103,486,487],{"class":150}," Number",[103,489,490],{"class":113},"(priceText.",[103,492,493],{"class":150},"replace",[103,495,241],{"class":113},[103,497,498],{"class":120},"\u002F",[103,500,501],{"class":140},"[",[103,503,504],{"class":109},"^",[103,506,507],{"class":140},"0-9.]",[103,509,498],{"class":120},[103,511,512],{"class":109},"g",[103,514,515],{"class":113},", ",[103,517,518],{"class":120},"\"\"",[103,520,521],{"class":113},")); ",[103,523,524],{"class":285},"\u002F\u002F \"Â£54.99\" -> 54.99\n",[103,526,527],{"class":105,"line":134},[103,528,426],{"class":113},[14,530,532],{"id":531},"store-a-baseline-to-compare-against","Store a baseline to compare against",[19,534],{},[10,536,537],{},"To know when a price changes, keep the last value between runs. A small JSON file is enough to start. Swap it for a database or key-value store in production.",[94,539,541],{"className":96,"code":540,"language":98,"meta":99,"style":99},"import { readFileSync, writeFileSync, existsSync } from 'node:fs';\n\nconst STORE = \".\u002Fprice.json\";\n\nfunction loadLastPrice() {\n  if (!existsSync(STORE)) return null;\n  return JSON.parse(readFileSync(STORE, \"utf8\")).price;\n}\n\nfunction savePrice(price) {\n  const record = { price, checkedAt: new Date().toISOString() };\n  writeFileSync(STORE, JSON.stringify(record, null, 2));\n}\n",[29,542,543,557,561,575,579,588,617,646,650,654,668,696,729],{"__ignoreMap":99},[103,544,545,547,550,552,555],{"class":105,"line":106},[103,546,110],{"class":109},[103,548,549],{"class":113}," { readFileSync, writeFileSync, existsSync } ",[103,551,117],{"class":109},[103,553,554],{"class":120}," 'node:fs'",[103,556,124],{"class":113},[103,558,559],{"class":105,"line":127},[103,560,131],{"emptyLinePlaceholder":130},[103,562,563,565,568,570,573],{"class":105,"line":134},[103,564,137],{"class":109},[103,566,567],{"class":140}," STORE",[103,569,144],{"class":109},[103,571,572],{"class":120}," \".\u002Fprice.json\"",[103,574,124],{"class":113},[103,576,577],{"class":105,"line":162},[103,578,131],{"emptyLinePlaceholder":130},[103,580,581,583,586],{"class":105,"line":167},[103,582,467],{"class":109},[103,584,585],{"class":150}," loadLastPrice",[103,587,203],{"class":113},[103,589,590,593,596,599,602,604,607,610,613,615],{"class":105,"line":178},[103,591,592],{"class":109},"  if",[103,594,595],{"class":113}," (",[103,597,598],{"class":109},"!",[103,600,601],{"class":150},"existsSync",[103,603,241],{"class":113},[103,605,606],{"class":140},"STORE",[103,608,609],{"class":113},")) ",[103,611,612],{"class":109},"return",[103,614,381],{"class":140},[103,616,124],{"class":113},[103,618,619,621,624,626,629,631,634,636,638,640,643],{"class":105,"line":186},[103,620,484],{"class":109},[103,622,623],{"class":140}," JSON",[103,625,43],{"class":113},[103,627,628],{"class":150},"parse",[103,630,241],{"class":113},[103,632,633],{"class":150},"readFileSync",[103,635,241],{"class":113},[103,637,606],{"class":140},[103,639,515],{"class":113},[103,641,642],{"class":120},"\"utf8\"",[103,644,645],{"class":113},")).price;\n",[103,647,648],{"class":105,"line":191},[103,649,426],{"class":113},[103,651,652],{"class":105,"line":206},[103,653,131],{"emptyLinePlaceholder":130},[103,655,656,658,661,663,666],{"class":105,"line":221},[103,657,467],{"class":109},[103,659,660],{"class":150}," savePrice",[103,662,241],{"class":113},[103,664,665],{"class":475},"price",[103,667,479],{"class":113},[103,669,670,673,676,678,681,684,687,690,693],{"class":105,"line":230},[103,671,672],{"class":109},"  const",[103,674,675],{"class":140}," record",[103,677,144],{"class":109},[103,679,680],{"class":113}," { price, checkedAt: ",[103,682,683],{"class":109},"new",[103,685,686],{"class":150}," Date",[103,688,689],{"class":113},"().",[103,691,692],{"class":150},"toISOString",[103,694,695],{"class":113},"() };\n",[103,697,698,701,703,705,707,710,712,715,718,721,723,726],{"class":105,"line":249},[103,699,700],{"class":150},"  writeFileSync",[103,702,241],{"class":113},[103,704,606],{"class":140},[103,706,515],{"class":113},[103,708,709],{"class":140},"JSON",[103,711,43],{"class":113},[103,713,714],{"class":150},"stringify",[103,716,717],{"class":113},"(record, ",[103,719,720],{"class":140},"null",[103,722,515],{"class":113},[103,724,725],{"class":140},"2",[103,727,728],{"class":113},"));\n",[103,730,731],{"class":105,"line":254},[103,732,426],{"class":113},[14,734,736],{"id":735},"detect-a-change","Detect a change",[19,738],{},[10,740,741],{},"Compare the freshly scraped price against the stored baseline. On the first run there's nothing to compare against, so just set the baseline.",[94,743,745],{"className":96,"code":744,"language":98,"meta":99,"style":99},"async function checkPrice() {\n  const current = toNumber(await getPrice());\n  const previous = loadLastPrice();\n\n  if (previous === null) {\n    console.log(`First run. Baseline set to ${current}.`);\n  } else if (current !== previous) {\n    console.log(`Price changed: ${previous} -> ${current}`);\n    await sendAlert(previous, current);\n  } else {\n    console.log(`No change. Still ${current}.`);\n  }\n\n  savePrice(current);\n}\n",[29,746,747,758,779,792,796,810,831,850,874,884,892,909,913,917,925],{"__ignoreMap":99},[103,748,749,751,753,756],{"class":105,"line":106},[103,750,194],{"class":109},[103,752,197],{"class":109},[103,754,755],{"class":150}," checkPrice",[103,757,203],{"class":113},[103,759,760,762,765,767,769,771,774,776],{"class":105,"line":127},[103,761,672],{"class":109},[103,763,764],{"class":140}," current",[103,766,144],{"class":109},[103,768,470],{"class":150},[103,770,241],{"class":113},[103,772,773],{"class":109},"await",[103,775,200],{"class":150},[103,777,778],{"class":113},"());\n",[103,780,781,783,786,788,790],{"class":105,"line":134},[103,782,672],{"class":109},[103,784,785],{"class":140}," previous",[103,787,144],{"class":109},[103,789,585],{"class":150},[103,791,218],{"class":113},[103,793,794],{"class":105,"line":162},[103,795,131],{"emptyLinePlaceholder":130},[103,797,798,800,803,806,808],{"class":105,"line":167},[103,799,592],{"class":109},[103,801,802],{"class":113}," (previous ",[103,804,805],{"class":109},"===",[103,807,381],{"class":140},[103,809,479],{"class":113},[103,811,812,815,818,820,823,826,829],{"class":105,"line":178},[103,813,814],{"class":113},"    console.",[103,816,817],{"class":150},"log",[103,819,241],{"class":113},[103,821,822],{"class":120},"`First run. Baseline set to ${",[103,824,825],{"class":113},"current",[103,827,828],{"class":120},"}.`",[103,830,159],{"class":113},[103,832,833,835,838,841,844,847],{"class":105,"line":186},[103,834,393],{"class":113},[103,836,837],{"class":109},"else",[103,839,840],{"class":109}," if",[103,842,843],{"class":113}," (current ",[103,845,846],{"class":109},"!==",[103,848,849],{"class":113}," previous) {\n",[103,851,852,854,856,858,861,864,867,869,872],{"class":105,"line":191},[103,853,814],{"class":113},[103,855,817],{"class":150},[103,857,241],{"class":113},[103,859,860],{"class":120},"`Price changed: ${",[103,862,863],{"class":113},"previous",[103,865,866],{"class":120},"} -> ${",[103,868,825],{"class":113},[103,870,871],{"class":120},"}`",[103,873,159],{"class":113},[103,875,876,878,881],{"class":105,"line":206},[103,877,233],{"class":109},[103,879,880],{"class":150}," sendAlert",[103,882,883],{"class":113},"(previous, current);\n",[103,885,886,888,890],{"class":105,"line":221},[103,887,393],{"class":113},[103,889,837],{"class":109},[103,891,227],{"class":113},[103,893,894,896,898,900,903,905,907],{"class":105,"line":230},[103,895,814],{"class":113},[103,897,817],{"class":150},[103,899,241],{"class":113},[103,901,902],{"class":120},"`No change. Still ${",[103,904,825],{"class":113},[103,906,828],{"class":120},[103,908,159],{"class":113},[103,910,911],{"class":105,"line":249},[103,912,420],{"class":113},[103,914,915],{"class":105,"line":254},[103,916,131],{"emptyLinePlaceholder":130},[103,918,919,922],{"class":105,"line":276},[103,920,921],{"class":150},"  savePrice",[103,923,924],{"class":113},"(current);\n",[103,926,927],{"class":105,"line":289},[103,928,426],{"class":113},[14,930,932],{"id":931},"send-an-alert","Send an alert",[19,934],{},[10,936,937],{},"When the price moves, post a message to your webhook. This example uses a Slack-style incoming webhook, but any endpoint that accepts a JSON body works.",[94,939,941],{"className":96,"code":940,"language":98,"meta":99,"style":99},"async function sendAlert(previous, current) {\n  const direction = current \u003C previous ? \"dropped\" : \"rose\";\n\n  await fetch(process.env.ALERT_WEBHOOK_URL, {\n    method: \"POST\",\n    headers: { \"Content-Type\": \"application\u002Fjson\" },\n    body: JSON.stringify({\n      text: `Nike Air Trainer 1 price ${direction}: Â£${previous} -> Â£${current}`,\n    }),\n  });\n}\n",[29,942,943,961,993,997,1011,1022,1039,1053,1078,1083,1088],{"__ignoreMap":99},[103,944,945,947,949,951,953,955,957,959],{"class":105,"line":106},[103,946,194],{"class":109},[103,948,197],{"class":109},[103,950,880],{"class":150},[103,952,241],{"class":113},[103,954,863],{"class":475},[103,956,515],{"class":113},[103,958,825],{"class":475},[103,960,479],{"class":113},[103,962,963,965,968,970,973,976,979,982,985,988,991],{"class":105,"line":127},[103,964,672],{"class":109},[103,966,967],{"class":140}," direction",[103,969,144],{"class":109},[103,971,972],{"class":113}," current ",[103,974,975],{"class":109},"\u003C",[103,977,978],{"class":113}," previous ",[103,980,981],{"class":109},"?",[103,983,984],{"class":120}," \"dropped\"",[103,986,987],{"class":109}," :",[103,989,990],{"class":120}," \"rose\"",[103,992,124],{"class":113},[103,994,995],{"class":105,"line":134},[103,996,131],{"emptyLinePlaceholder":130},[103,998,999,1001,1004,1006,1008],{"class":105,"line":162},[103,1000,209],{"class":109},[103,1002,1003],{"class":150}," fetch",[103,1005,154],{"class":113},[103,1007,62],{"class":140},[103,1009,1010],{"class":113},", {\n",[103,1012,1013,1016,1019],{"class":105,"line":167},[103,1014,1015],{"class":113},"    method: ",[103,1017,1018],{"class":120},"\"POST\"",[103,1020,1021],{"class":113},",\n",[103,1023,1024,1027,1030,1033,1036],{"class":105,"line":178},[103,1025,1026],{"class":113},"    headers: { ",[103,1028,1029],{"class":120},"\"Content-Type\"",[103,1031,1032],{"class":113},": ",[103,1034,1035],{"class":120},"\"application\u002Fjson\"",[103,1037,1038],{"class":113}," },\n",[103,1040,1041,1044,1046,1048,1050],{"class":105,"line":186},[103,1042,1043],{"class":113},"    body: ",[103,1045,709],{"class":140},[103,1047,43],{"class":113},[103,1049,714],{"class":150},[103,1051,1052],{"class":113},"({\n",[103,1054,1055,1058,1061,1064,1067,1069,1072,1074,1076],{"class":105,"line":191},[103,1056,1057],{"class":113},"      text: ",[103,1059,1060],{"class":120},"`Nike Air Trainer 1 price ${",[103,1062,1063],{"class":113},"direction",[103,1065,1066],{"class":120},"}: Â£${",[103,1068,863],{"class":113},[103,1070,1071],{"class":120},"} -> Â£${",[103,1073,825],{"class":113},[103,1075,871],{"class":120},[103,1077,1021],{"class":113},[103,1079,1080],{"class":105,"line":206},[103,1081,1082],{"class":113},"    }),\n",[103,1084,1085],{"class":105,"line":221},[103,1086,1087],{"class":113},"  });\n",[103,1089,1090],{"class":105,"line":230},[103,1091,426],{"class":113},[428,1093,1094],{},[10,1095,1096,1098],{},[433,1097,435],{}," To alert by email instead, send the same payload to a transactional email API such as Postmark or SendGrid in place of the webhook call.",[14,1100,1102],{"id":1101},"run-it-on-a-schedule","Run it on a schedule",[19,1104],{},[10,1106,1107],{},"The script checks the price once per run. To watch for changes over time, run it on a schedule:",[22,1109,1110,1117],{},[25,1111,1112,1113,1116],{},"A cron job on a server: ",[29,1114,1115],{},"0 * * * * node check-price.js"," runs it hourly.",[25,1118,1119,1120,1123],{},"A scheduled CI job, such as GitHub Actions with a ",[29,1121,1122],{},"schedule"," trigger, which needs no server of your own.",[10,1125,1126,1127,1130],{},"Persist ",[29,1128,1129],{},"price.json",", or move the baseline into a database, so the value survives between scheduled runs.",[14,1132,1134],{"id":1133},"full-script","Full script",[19,1136],{},[94,1138,1140],{"className":96,"code":1139,"language":98,"meta":99,"style":99},"import { AxiomApi } from 'axiom-api';\nimport { readFileSync, writeFileSync, existsSync } from 'node:fs';\n\nconst axiom = new AxiomApi(process.env.AXIOM_API_KEY);\nconst STORE = \".\u002Fprice.json\";\n\nconst PRODUCT_URL =\n  \"https:\u002F\u002Fwww.secretsales.com\u002F66165d1f6b502-nike-nike-air-trainer-1-essential-mens-white-trainers-white\u002F\";\n\nasync function getPrice() {\n  await axiom.browserOpen();\n  try {\n    await axiom.goto(PRODUCT_URL);\n    const rows = await axiom.scrape(\n      null,\n      { hierarchy: \"[class*='productFullDetail-productPriceFinal']\" },\n      null,\n      1,\n      {}\n    );\n    return rows?.[0]?.[0]?.trim() ?? null;\n  } finally {\n    await axiom.browserClose();\n  }\n}\n\nfunction toNumber(priceText) {\n  return Number(priceText.replace(\u002F[^0-9.]\u002Fg, \"\"));\n}\n\nfunction loadLastPrice() {\n  if (!existsSync(STORE)) return null;\n  return JSON.parse(readFileSync(STORE, \"utf8\")).price;\n}\n\nfunction savePrice(price) {\n  writeFileSync(STORE, JSON.stringify({ price, checkedAt: new Date().toISOString() }, null, 2));\n}\n\nasync function sendAlert(previous, current) {\n  const direction = current \u003C previous ? \"dropped\" : \"rose\";\n  await fetch(process.env.ALERT_WEBHOOK_URL, {\n    method: \"POST\",\n    headers: { \"Content-Type\": \"application\u002Fjson\" },\n    body: JSON.stringify({\n      text: `Nike Air Trainer 1 price ${direction}: Â£${previous} -> Â£${current}`,\n    }),\n  });\n}\n\nasync function checkPrice() {\n  const current = toNumber(await getPrice());\n  const previous = loadLastPrice();\n\n  if (previous === null) {\n    console.log(`First run. Baseline set to ${current}.`);\n  } else if (current !== previous) {\n    console.log(`Price changed: ${previous} -> ${current}`);\n    await sendAlert(previous, current);\n  } else {\n    console.log(`No change. Still ${current}.`);\n  }\n\n  savePrice(current);\n}\n\ncheckPrice();\n",[29,1141,1142,1154,1166,1170,1188,1200,1204,1212,1218,1222,1232,1242,1248,1262,1278,1284,1292,1298,1304,1309,1313,1337,1345,1355,1359,1363,1367,1380,1411,1416,1421,1430,1453,1478,1483,1488,1501,1540,1545,1550,1569,1594,1607,1616,1629,1642,1663,1668,1673,1678,1683,1694,1713,1726,1731,1744,1761,1776,1797,1806,1815,1832,1837,1842,1849,1854,1859],{"__ignoreMap":99},[103,1143,1144,1146,1148,1150,1152],{"class":105,"line":106},[103,1145,110],{"class":109},[103,1147,114],{"class":113},[103,1149,117],{"class":109},[103,1151,121],{"class":120},[103,1153,124],{"class":113},[103,1155,1156,1158,1160,1162,1164],{"class":105,"line":127},[103,1157,110],{"class":109},[103,1159,549],{"class":113},[103,1161,117],{"class":109},[103,1163,554],{"class":120},[103,1165,124],{"class":113},[103,1167,1168],{"class":105,"line":134},[103,1169,131],{"emptyLinePlaceholder":130},[103,1171,1172,1174,1176,1178,1180,1182,1184,1186],{"class":105,"line":162},[103,1173,137],{"class":109},[103,1175,141],{"class":140},[103,1177,144],{"class":109},[103,1179,147],{"class":109},[103,1181,151],{"class":150},[103,1183,154],{"class":113},[103,1185,55],{"class":140},[103,1187,159],{"class":113},[103,1189,1190,1192,1194,1196,1198],{"class":105,"line":167},[103,1191,137],{"class":109},[103,1193,567],{"class":140},[103,1195,144],{"class":109},[103,1197,572],{"class":120},[103,1199,124],{"class":113},[103,1201,1202],{"class":105,"line":178},[103,1203,131],{"emptyLinePlaceholder":130},[103,1205,1206,1208,1210],{"class":105,"line":186},[103,1207,137],{"class":109},[103,1209,172],{"class":140},[103,1211,175],{"class":109},[103,1213,1214,1216],{"class":105,"line":191},[103,1215,181],{"class":120},[103,1217,124],{"class":113},[103,1219,1220],{"class":105,"line":206},[103,1221,131],{"emptyLinePlaceholder":130},[103,1223,1224,1226,1228,1230],{"class":105,"line":221},[103,1225,194],{"class":109},[103,1227,197],{"class":109},[103,1229,200],{"class":150},[103,1231,203],{"class":113},[103,1233,1234,1236,1238,1240],{"class":105,"line":230},[103,1235,209],{"class":109},[103,1237,212],{"class":113},[103,1239,215],{"class":150},[103,1241,218],{"class":113},[103,1243,1244,1246],{"class":105,"line":249},[103,1245,224],{"class":109},[103,1247,227],{"class":113},[103,1249,1250,1252,1254,1256,1258,1260],{"class":105,"line":254},[103,1251,233],{"class":109},[103,1253,212],{"class":113},[103,1255,238],{"class":150},[103,1257,241],{"class":113},[103,1259,244],{"class":140},[103,1261,159],{"class":113},[103,1263,1264,1266,1268,1270,1272,1274,1276],{"class":105,"line":276},[103,1265,257],{"class":109},[103,1267,260],{"class":140},[103,1269,144],{"class":109},[103,1271,265],{"class":109},[103,1273,212],{"class":113},[103,1275,270],{"class":150},[103,1277,273],{"class":113},[103,1279,1280,1282],{"class":105,"line":289},[103,1281,279],{"class":140},[103,1283,1021],{"class":113},[103,1285,1286,1288,1290],{"class":105,"line":304},[103,1287,292],{"class":113},[103,1289,295],{"class":120},[103,1291,1038],{"class":113},[103,1293,1294,1296],{"class":105,"line":314},[103,1295,279],{"class":140},[103,1297,1021],{"class":113},[103,1299,1300,1302],{"class":105,"line":326},[103,1301,317],{"class":140},[103,1303,1021],{"class":113},[103,1305,1306],{"class":105,"line":335},[103,1307,1308],{"class":113},"      {}\n",[103,1310,1311],{"class":105,"line":341},[103,1312,338],{"class":113},[103,1314,1315,1317,1319,1321,1323,1325,1327,1329,1331,1333,1335],{"class":105,"line":346},[103,1316,355],{"class":109},[103,1318,358],{"class":113},[103,1320,361],{"class":140},[103,1322,364],{"class":113},[103,1324,361],{"class":140},[103,1326,369],{"class":113},[103,1328,372],{"class":150},[103,1330,375],{"class":113},[103,1332,378],{"class":109},[103,1334,381],{"class":140},[103,1336,124],{"class":113},[103,1338,1339,1341,1343],{"class":105,"line":352},[103,1340,393],{"class":113},[103,1342,396],{"class":109},[103,1344,227],{"class":113},[103,1346,1347,1349,1351,1353],{"class":105,"line":390},[103,1348,233],{"class":109},[103,1350,212],{"class":113},[103,1352,408],{"class":150},[103,1354,218],{"class":113},[103,1356,1357],{"class":105,"line":401},[103,1358,420],{"class":113},[103,1360,1361],{"class":105,"line":417},[103,1362,426],{"class":113},[103,1364,1365],{"class":105,"line":423},[103,1366,131],{"emptyLinePlaceholder":130},[103,1368,1370,1372,1374,1376,1378],{"class":105,"line":1369},27,[103,1371,467],{"class":109},[103,1373,470],{"class":150},[103,1375,241],{"class":113},[103,1377,476],{"class":475},[103,1379,479],{"class":113},[103,1381,1383,1385,1387,1389,1391,1393,1395,1397,1399,1401,1403,1405,1407,1409],{"class":105,"line":1382},28,[103,1384,484],{"class":109},[103,1386,487],{"class":150},[103,1388,490],{"class":113},[103,1390,493],{"class":150},[103,1392,241],{"class":113},[103,1394,498],{"class":120},[103,1396,501],{"class":140},[103,1398,504],{"class":109},[103,1400,507],{"class":140},[103,1402,498],{"class":120},[103,1404,512],{"class":109},[103,1406,515],{"class":113},[103,1408,518],{"class":120},[103,1410,728],{"class":113},[103,1412,1414],{"class":105,"line":1413},29,[103,1415,426],{"class":113},[103,1417,1419],{"class":105,"line":1418},30,[103,1420,131],{"emptyLinePlaceholder":130},[103,1422,1424,1426,1428],{"class":105,"line":1423},31,[103,1425,467],{"class":109},[103,1427,585],{"class":150},[103,1429,203],{"class":113},[103,1431,1433,1435,1437,1439,1441,1443,1445,1447,1449,1451],{"class":105,"line":1432},32,[103,1434,592],{"class":109},[103,1436,595],{"class":113},[103,1438,598],{"class":109},[103,1440,601],{"class":150},[103,1442,241],{"class":113},[103,1444,606],{"class":140},[103,1446,609],{"class":113},[103,1448,612],{"class":109},[103,1450,381],{"class":140},[103,1452,124],{"class":113},[103,1454,1456,1458,1460,1462,1464,1466,1468,1470,1472,1474,1476],{"class":105,"line":1455},33,[103,1457,484],{"class":109},[103,1459,623],{"class":140},[103,1461,43],{"class":113},[103,1463,628],{"class":150},[103,1465,241],{"class":113},[103,1467,633],{"class":150},[103,1469,241],{"class":113},[103,1471,606],{"class":140},[103,1473,515],{"class":113},[103,1475,642],{"class":120},[103,1477,645],{"class":113},[103,1479,1481],{"class":105,"line":1480},34,[103,1482,426],{"class":113},[103,1484,1486],{"class":105,"line":1485},35,[103,1487,131],{"emptyLinePlaceholder":130},[103,1489,1491,1493,1495,1497,1499],{"class":105,"line":1490},36,[103,1492,467],{"class":109},[103,1494,660],{"class":150},[103,1496,241],{"class":113},[103,1498,665],{"class":475},[103,1500,479],{"class":113},[103,1502,1504,1506,1508,1510,1512,1514,1516,1518,1521,1523,1525,1527,1529,1532,1534,1536,1538],{"class":105,"line":1503},37,[103,1505,700],{"class":150},[103,1507,241],{"class":113},[103,1509,606],{"class":140},[103,1511,515],{"class":113},[103,1513,709],{"class":140},[103,1515,43],{"class":113},[103,1517,714],{"class":150},[103,1519,1520],{"class":113},"({ price, checkedAt: ",[103,1522,683],{"class":109},[103,1524,686],{"class":150},[103,1526,689],{"class":113},[103,1528,692],{"class":150},[103,1530,1531],{"class":113},"() }, ",[103,1533,720],{"class":140},[103,1535,515],{"class":113},[103,1537,725],{"class":140},[103,1539,728],{"class":113},[103,1541,1543],{"class":105,"line":1542},38,[103,1544,426],{"class":113},[103,1546,1548],{"class":105,"line":1547},39,[103,1549,131],{"emptyLinePlaceholder":130},[103,1551,1553,1555,1557,1559,1561,1563,1565,1567],{"class":105,"line":1552},40,[103,1554,194],{"class":109},[103,1556,197],{"class":109},[103,1558,880],{"class":150},[103,1560,241],{"class":113},[103,1562,863],{"class":475},[103,1564,515],{"class":113},[103,1566,825],{"class":475},[103,1568,479],{"class":113},[103,1570,1572,1574,1576,1578,1580,1582,1584,1586,1588,1590,1592],{"class":105,"line":1571},41,[103,1573,672],{"class":109},[103,1575,967],{"class":140},[103,1577,144],{"class":109},[103,1579,972],{"class":113},[103,1581,975],{"class":109},[103,1583,978],{"class":113},[103,1585,981],{"class":109},[103,1587,984],{"class":120},[103,1589,987],{"class":109},[103,1591,990],{"class":120},[103,1593,124],{"class":113},[103,1595,1597,1599,1601,1603,1605],{"class":105,"line":1596},42,[103,1598,209],{"class":109},[103,1600,1003],{"class":150},[103,1602,154],{"class":113},[103,1604,62],{"class":140},[103,1606,1010],{"class":113},[103,1608,1610,1612,1614],{"class":105,"line":1609},43,[103,1611,1015],{"class":113},[103,1613,1018],{"class":120},[103,1615,1021],{"class":113},[103,1617,1619,1621,1623,1625,1627],{"class":105,"line":1618},44,[103,1620,1026],{"class":113},[103,1622,1029],{"class":120},[103,1624,1032],{"class":113},[103,1626,1035],{"class":120},[103,1628,1038],{"class":113},[103,1630,1632,1634,1636,1638,1640],{"class":105,"line":1631},45,[103,1633,1043],{"class":113},[103,1635,709],{"class":140},[103,1637,43],{"class":113},[103,1639,714],{"class":150},[103,1641,1052],{"class":113},[103,1643,1645,1647,1649,1651,1653,1655,1657,1659,1661],{"class":105,"line":1644},46,[103,1646,1057],{"class":113},[103,1648,1060],{"class":120},[103,1650,1063],{"class":113},[103,1652,1066],{"class":120},[103,1654,863],{"class":113},[103,1656,1071],{"class":120},[103,1658,825],{"class":113},[103,1660,871],{"class":120},[103,1662,1021],{"class":113},[103,1664,1666],{"class":105,"line":1665},47,[103,1667,1082],{"class":113},[103,1669,1671],{"class":105,"line":1670},48,[103,1672,1087],{"class":113},[103,1674,1676],{"class":105,"line":1675},49,[103,1677,426],{"class":113},[103,1679,1681],{"class":105,"line":1680},50,[103,1682,131],{"emptyLinePlaceholder":130},[103,1684,1686,1688,1690,1692],{"class":105,"line":1685},51,[103,1687,194],{"class":109},[103,1689,197],{"class":109},[103,1691,755],{"class":150},[103,1693,203],{"class":113},[103,1695,1697,1699,1701,1703,1705,1707,1709,1711],{"class":105,"line":1696},52,[103,1698,672],{"class":109},[103,1700,764],{"class":140},[103,1702,144],{"class":109},[103,1704,470],{"class":150},[103,1706,241],{"class":113},[103,1708,773],{"class":109},[103,1710,200],{"class":150},[103,1712,778],{"class":113},[103,1714,1716,1718,1720,1722,1724],{"class":105,"line":1715},53,[103,1717,672],{"class":109},[103,1719,785],{"class":140},[103,1721,144],{"class":109},[103,1723,585],{"class":150},[103,1725,218],{"class":113},[103,1727,1729],{"class":105,"line":1728},54,[103,1730,131],{"emptyLinePlaceholder":130},[103,1732,1734,1736,1738,1740,1742],{"class":105,"line":1733},55,[103,1735,592],{"class":109},[103,1737,802],{"class":113},[103,1739,805],{"class":109},[103,1741,381],{"class":140},[103,1743,479],{"class":113},[103,1745,1747,1749,1751,1753,1755,1757,1759],{"class":105,"line":1746},56,[103,1748,814],{"class":113},[103,1750,817],{"class":150},[103,1752,241],{"class":113},[103,1754,822],{"class":120},[103,1756,825],{"class":113},[103,1758,828],{"class":120},[103,1760,159],{"class":113},[103,1762,1764,1766,1768,1770,1772,1774],{"class":105,"line":1763},57,[103,1765,393],{"class":113},[103,1767,837],{"class":109},[103,1769,840],{"class":109},[103,1771,843],{"class":113},[103,1773,846],{"class":109},[103,1775,849],{"class":113},[103,1777,1779,1781,1783,1785,1787,1789,1791,1793,1795],{"class":105,"line":1778},58,[103,1780,814],{"class":113},[103,1782,817],{"class":150},[103,1784,241],{"class":113},[103,1786,860],{"class":120},[103,1788,863],{"class":113},[103,1790,866],{"class":120},[103,1792,825],{"class":113},[103,1794,871],{"class":120},[103,1796,159],{"class":113},[103,1798,1800,1802,1804],{"class":105,"line":1799},59,[103,1801,233],{"class":109},[103,1803,880],{"class":150},[103,1805,883],{"class":113},[103,1807,1809,1811,1813],{"class":105,"line":1808},60,[103,1810,393],{"class":113},[103,1812,837],{"class":109},[103,1814,227],{"class":113},[103,1816,1818,1820,1822,1824,1826,1828,1830],{"class":105,"line":1817},61,[103,1819,814],{"class":113},[103,1821,817],{"class":150},[103,1823,241],{"class":113},[103,1825,902],{"class":120},[103,1827,825],{"class":113},[103,1829,828],{"class":120},[103,1831,159],{"class":113},[103,1833,1835],{"class":105,"line":1834},62,[103,1836,420],{"class":113},[103,1838,1840],{"class":105,"line":1839},63,[103,1841,131],{"emptyLinePlaceholder":130},[103,1843,1845,1847],{"class":105,"line":1844},64,[103,1846,921],{"class":150},[103,1848,924],{"class":113},[103,1850,1852],{"class":105,"line":1851},65,[103,1853,426],{"class":113},[103,1855,1857],{"class":105,"line":1856},66,[103,1858,131],{"emptyLinePlaceholder":130},[103,1860,1862,1865],{"class":105,"line":1861},67,[103,1863,1864],{"class":150},"checkPrice",[103,1866,218],{"class":113},[10,1868,1869,1870,43],{},"Run it with ",[29,1871,1872],{},"node check-price.js",[14,1874,1876],{"id":1875},"next-steps","Next steps",[19,1878],{},[22,1880,1881,1889,1902],{},[25,1882,1883,1884,1888],{},"Learn the full ",[48,1885,1886],{"href":73},[29,1887,76],{}," signature to paginate and cap results.",[25,1890,1891,1892,1896,1897,1901],{},"See ",[48,1893,1895],{"href":1894},"\u002Fdocs\u002Fdeveloper-hub\u002Fapi\u002Fstep-functions\u002Fstart-a-session","Start a session"," and the other ",[48,1898,1900],{"href":1899},"\u002Fdocs\u002Fdeveloper-hub\u002Fapi\u002Fstep-functions","Step API"," actions.",[25,1903,1904],{},"Track many products at once by looping the check over an array of URLs.",[1906,1907,1908],"style",{},"html pre.shiki code .sjeE4, html code.shiki .sjeE4{--shiki-default:#CF222E;--shiki-dark:#FF7B72}html pre.shiki code .s4rv2, html code.shiki .s4rv2{--shiki-default:#1F2328;--shiki-dark:#E6EDF3}html pre.shiki code .sSVrQ, html code.shiki .sSVrQ{--shiki-default:#0A3069;--shiki-dark:#A5D6FF}html pre.shiki code .sHrmB, html code.shiki .sHrmB{--shiki-default:#0550AE;--shiki-dark:#79C0FF}html pre.shiki code .sbjLL, html code.shiki .sbjLL{--shiki-default:#8250DF;--shiki-dark:#D2A8FF}html pre.shiki code .sU953, html code.shiki .sU953{--shiki-default:#6E7781;--shiki-dark:#8B949E}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sTDnQ, html code.shiki .sTDnQ{--shiki-default:#953800;--shiki-dark:#FFA657}",{"title":99,"searchDepth":134,"depth":134,"links":1910},[1911,1912,1913,1914,1915,1916,1917,1918],{"id":16,"depth":127,"text":17},{"id":65,"depth":127,"text":66},{"id":531,"depth":127,"text":532},{"id":735,"depth":127,"text":736},{"id":931,"depth":127,"text":932},{"id":1101,"depth":127,"text":1102},{"id":1133,"depth":127,"text":1134},{"id":1875,"depth":127,"text":1876},null,"Use the axiom.ai Step API to scrape a product price, store a baseline in code, compare it on each run, and send an alert when the price changes.","md",{"metaTitle":1923,"type":29,"format":1924,"intent_type":1925,"intent":1926,"tags":1927,"platfrom":1930},"Price monitoring with the Step API","guide","workflow","Monitor for price changes",[1928,665,270,1929],"monitor","claude code",[1925],"\u002Fguides\u002Fmonitor-a-price-step-api",{"title":5,"description":1920},"guides\u002Fmonitor-a-price-step-api","vEkEnKzWgIfyLJfJif2uYlnjkiwPLAVgzAQ_c8aVhos",{"id":4,"title":5,"body":6,"date":1919,"description":1920,"extension":1921,"meta":1922,"navigation":130,"order":1919,"path":1931,"seo":1932,"stem":1933,"__hash__":1934,"metaTitle":1923,"type":29,"format":1924,"intent_type":1925,"intent":1926,"tags":1927,"platfrom":1930},1784824789891]