Is Price Tracker 2.0 - Price Graph & Auto Buy safe?
Price Tracker 2.0 collects product data from 15+ e-commerce sites and transmits it, along with user agent and timezone, to api.indiadesire.com.
On each product page visit across sites including Amazon, Flipkart, Myntra, and a dozen others, the extension reads product ID, title, price, images, availability, seller details, and category, then POSTs that data to api.indiadesire.com together with the extension ID, user agent string, platform, and timezone. The DOM selectors that drive this extraction are fetched from api2.indiadesire.com every 30 minutes, meaning the operator can change what data is collected without an extension update.
Who publishes itPrijatel Internet Private Limited - no other listings under this identity, 3 shared hostnames
Prijatel Internet Private Limited - no other listings under this identity, 3 shared hostnames
What this publisher told the store about itself, and the other listings that told it the same thing.
Shared hosts - 3 hostnames
Hostnames hardcoded in this extension that few other listings call. That can mean one operator behind both, and it can equally mean a small shared vendor, so it is context rather than a conclusion. Hosts that many listings call are left out: they are services, not connections.
AI-generated. Findings may contain errors. Those marked Verified have been manually reviewed.
Publishers can request a review.
Findings
Remote selectors control shopping-page collection
Price Tracker 2.0 fetches selector rules from api2.indiadesire.com, cached under ptRemoteSelectors for flipkart.com.
Content scripts read keys like title_sel, cprice_sel, img_sel, CatNames, cat_title_sel, cat_cprice_sel to pick page fields.
What actually happens
You browse a supported shopping page.
The extension runs content scripts on retail sites listed in its manifest, including Flipkart, Amazon, Myntra, Ajio, Snapdeal, Nykaa, Croma, and Tata Cliq.
The extension applies server-provided CSS selectors that decide which product details and page URL are collected.
The selector rules can change through the remote API response without shipping a new extension version.
Captured request
The response is parsed as JSON selector data; dynamic analysis observed the parsed flipkart.com selector data being written to chrome.storage.local.
What's stored on your device
This cached record lets the extension reuse server-provided page-reading rules while you browse supported shopping sites.
- Location
- chrome.storage.local key 'ptRemoteSelectors'
Dynamic analysis captured chrome.storage.local.set from the service worker writing ptRemoteSelectors with selector data for flipkart.com. The source writes an object shaped as { data: selectorsByDomain, timestamp: Date.now() }, where selectorsByDomain is built from the JSON returned by the getSelectors API.Selector-driven fields read from shopping pages
- Shopping page URLhttps://www.flipkart.com/apple-iphone-15-blue-128-gb/p/itm6ac6485515ae4 (illustrative)
This ties the collected product record to the exact retail page you visited.
- Product identifierMOBGTAGPAQNVFZZY (illustrative)
This identifies the item on the retailer's site and makes repeat price checks possible.
- Product titleApple iPhone 15 (Blue, 128 GB) (illustrative)
This records the visible product name from the page you viewed.
- Current price54999 (illustrative)
This records the current displayed price for the product page or listing card.
- Product image URLhttps://rukminim2.flixcart.com/image/416/416/xif0q/mobile/g/x/h/-original-imagtc5fz9spysyk.jpeg (illustrative)
This records the product image URL selected from the retailer page.
- Availability and sellermarketplace=FLIPKART&lid=LSTMOBGTAGPAQNVFZZYFZVY2V; in stock (illustrative)
This adds shopping context such as stock status, marketplace, listing ID, and seller details.
- Category pathMobiles > Smartphones > Apple (illustrative)
This records category text from the page, which can describe what kind of products you viewed.
When this fires
Every 30 minutes
The selector rules are fetched once when the extension loads and then refreshed about every 30 minutes.
The code that does this
Remote selector fetch, cache, delivery, and use
Service worker fetches and caches remote selectors
background.jsconst STORAGE_KEY = 'ptRemoteSelectors';const API_URL = 'https://api2.indiadesire.com/n/m/api.php?rquest=getSelectors';const PTSELECTORS_DOMAINS = [ 'amazon', 'flipkart', 'myntra', 'ajio', 'jiomart', 'purplle', 'snapdeal', 'shopclues', 'tatacliq', 'pepperfry', 'reliancedigital', 'nykaa', 'clovia', 'zivame', 'croma', 'meesho', 'amazoncn', 'amazonfr', 'amazonus', 'amazonuk', 'amazones'];async function fetchRemoteSelectors() { try { console.log('[PTExtn2] Fetching selectors from API...'); const response = await fetch(API_URL); if (!response.ok) throw new Error('API fetch failed'); const json = await response.json(); // Convert array format to domain-keyed object const selectorsByDomain = {}; for (const entry of json) { for (const [domain, data] of Object.entries(entry)) { if (Array.isArray(data) && data.length > 0) { selectorsByDomain[domain] = data[0]; } } } // Cache in chrome.storage await chrome.storage.local.set({ [STORAGE_KEY]: { data: selectorsByDomain, timestamp: Date.now() } }); console.log('[PTExtn2] Cached selectors for:', Object.keys(selectorsByDomain).join(', ')); } catch (err) { console.error('[PTExtn2] Fetch error:', err); }}function initRemoteSelectors() { fetchRemoteSelectors(); // Initial fetch setInterval(fetchRemoteSelectors, 30 * 60 * 1000); // Every 30 mins}async function handleGetSelectors(domain, sendResponse) { try { // Try cache first var cached = await chrome.storage.local.get(STORAGE_KEY); if (cached[STORAGE_KEY]) { var data = cached[STORAGE_KEY]; // Check if cache is fresh (30 mins TTL) if (Date.now() - data.timestamp < 30 * 60 * 1000) { var selectors = data.data[domain]; if (selectors) { console.log('[PTExtn2] Serving cached selectors for:', domain); sendResponse({success: true, selectors: selectors}); return; } } } // Fetch fresh if not in cache or expired await fetchRemoteSelectors(); var fresh = await chrome.storage.local.get(STORAGE_KEY); var selectors = fresh[STORAGE_KEY] ? fresh[STORAGE_KEY].data[domain] : null; if (selectors) { sendResponse({success: true, selectors: selectors}); } else { sendResponse({success: false, error: 'No selectors for domain: ' + domain}); } } catch (err) { console.error('[PTExtn2] Error:', err); sendResponse({success: false, error: err.message}); }}initRemoteSelectors();Flipkart content script requests and applies those selectors
scripts/FlipV211.jsasync function loadRemoteSelectors() { await chrome.runtime.sendMessage({ action: 'getSelectors', domain: DOMAIN }, function(resp) { if (resp && resp.success) { remoteSelectors = resp.selectors; setTimeout(sendPairs(1), 3000); console.log('[Flipkart] Loaded remote selectors:', remoteSelectors ? Object.keys(remoteSelectors) : 'none'); } });}function getExtn3Data() { var m="",c="",s=""; if (!remoteSelectors || !remoteSelectors.CatNames) return { maincat: m, cat: c, subcat: s }; var sel = remoteSelectors.CatNames.split(',')[0].trim(); // First selector only var all = $(sel); if (all.length > 0) m = all.eq(1).text().trim(); // eq(0) - 1st if (all.length > 1) c = all.eq(2).text().trim(); // eq(1) - 2nd if (all.length > 2) s = all.eq(3).text().trim(); // eq(2) - 3rd return { maincat: m, cat: c, subcat: s };}function getText(selKey) { if (!remoteSelectors || !remoteSelectors[selKey]) return ''; var parts = remoteSelectors[selKey].split(',').map(s => s.trim()).filter(s => s); for (var p of parts) { var el = $(p); if (el && el.length > 0) { var txt = el.text().trim(); if (txt) return txt; } } return '';}function getAttr(selKey, attr) { if (!remoteSelectors || !remoteSelectors[selKey]) return ''; var parts = remoteSelectors[selKey].split(',').map(s => s.trim()).filter(s => s); for (var p of parts) { var el = $(p); if (el && el.length > 0) { var val = el.attr(attr) || el.prop(attr); if (val) return val; } } return '';}function getImage(){ var image = ""; image = getAttr('img_sel','src')||getAttr('img_sel','data-src'); return image;}function getCatDataSel() { return (remoteSelectors && remoteSelectors.cat_data_sel) || ''; }function getCatTitleSel() { return (remoteSelectors && remoteSelectors.cat_title_sel) || ''; }function getStockSel() { return (remoteSelectors && remoteSelectors.stock_sel) || ''; }function getCatPriceSel() { return (remoteSelectors && remoteSelectors.cat_cprice_sel) || ''; }function getCatImgSel() { return (remoteSelectors && remoteSelectors.cat_img_sel) || ''; }function getCatBrandSel() { return (remoteSelectors && remoteSelectors.cat_brand_sel) || ''; }function getCatCards() { var s = getCatDataSel(); return s ? $(s) : $(); }function getPrice(){ var currentPrice = getText('cprice_sel'); currentPrice = currentPrice.replace(/[^\d.]/g, '').replace(/^\./, ''); return filter_price(currentPrice);}function getTitle(){ var title = getText('title_sel'); return title;}Background upload function posts collected records
background.jsfunction sendPairs(data, store,ctimezone) { chrome.storage.local.get(['ptextid','ptextauth'], (result) => { var parameters = "extnid="+result.ptextid+"&extnauth="+result.ptextauth+"&data=" + encodeURIComponent(data) + "&store=" + store+"&ctimezone="+ctimezone+"&version="+chrome.runtime.getManifest().version+"&useragent="+navigator.userAgent+"&platform="+navigator.platform; let fetchData = { method: 'POST', body: parameters, headers: new Headers({ 'Content-Type': 'application/x-www-form-urlencoded' }) }; fetch("https://api.indiadesire.com/v10/api.php?rquest=uploadData", fetchData) .then((response) => response.json()) .then((responseData) => { var abc=JSON.stringify(responseData); }); }); // console.log("Success1");}Remote hosts used by this behavior
- api2.indiadesire.com
Provides the remote selector rules fetched by the service worker.
- api.indiadesire.com
Receives product records through the uploadData endpoint after content scripts build the data array.
Where it sends data
Destinations our analysis observed Price Tracker 2.0 contacting. Sending data somewhere is not a finding on its own - an extension that syncs your settings has to talk to its own server - but it is where your data can go, and who else it goes to.
- api.indiadesire.com
Price Tracker 2.0 sends data to api.indiadesire.com. No other extension we have analyzed sends data here.
- api2.indiadesire.com
Price Tracker 2.0 sends data to api2.indiadesire.com. No other extension we have analyzed sends data here.
