Set up the sheet
Create a sheet with a header row and one search per row in column A. Write searches the way you would on eBay, with the details that change the price (model, size, grade). Columns B to F fill in automatically.
A (keyword) B (median) C (low) D (high) E (sales) F (updated)
1 Keyword Median P25 P75 Sales Updated
2 nintendo switch oled ... ... ... ... ...
3 2023 topps chrome wembanyama psa 10 ... ... ... ... ...
4 shure sm7b ... ... ... ... ...Then create a free CompSniper account and copy your API key from the dashboard.
Paste the script and add your key
Open Extensions > Apps Script, replace the sample code with the script below and save. In Project Settings > Script properties, add COMPSNIPER_API_KEY with your key, so the key never sits in a cell. Reload the sheet: a Sold prices menu appears. Click Update sold prices and approve the permissions once.
// Extensions > Apps Script, paste this, then save.
// Project Settings > Script properties: add COMPSNIPER_API_KEY = your cs_live_ key.
const API = "https://api.compsniper.com/v1/scrape";
function onOpen() {
SpreadsheetApp.getUi()
.createMenu("Sold prices")
.addItem("Update sold prices", "updateSoldPrices")
.addToUi();
}
function updateSoldPrices() {
const key = PropertiesService.getScriptProperties().getProperty("COMPSNIPER_API_KEY");
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheets()[0];
const lastRow = sheet.getLastRow();
if (lastRow < 2) return;
const keywords = sheet.getRange(2, 1, lastRow - 1, 1).getValues();
const rows = keywords.map(([keyword]) => {
if (!keyword) return ["", "", "", "", ""];
const url = API + "?count=240&bestOfferPrice=true&keyword=" + encodeURIComponent(keyword);
const res = UrlFetchApp.fetch(url, {
headers: { Authorization: "Bearer " + key },
muteHttpExceptions: true,
});
if (res.getResponseCode() !== 200) {
return ["Error " + res.getResponseCode(), "", "", "", new Date()];
}
const s = JSON.parse(res.getContentText()).summary;
if (!s || !s.count) return ["No sales", "", "", 0, new Date()];
return [s.median, s.p25, s.p75, s.count, new Date()];
});
sheet.getRange(2, 2, rows.length, 5).setValues(rows);
}Each keyword is one request to GET /v1/scrape, which returns up to 240 recent eBay sales. The median and range are calculated after accessories, parts and wrong models are removed. Add &ebaySite=ebay.co.uk (or .de, .fr, .it, .es, .ca, .com.au) for local prices on another eBay site.
Refresh it automatically every day
Add this function to the same script and run it once from the editor. Google then runs updateSoldPrices every morning, with the date of the last refresh in column F.
// Run this once from the Apps Script editor: refreshes the sheet every morning.
function createDailyRefresh() {
ScriptApp.newTrigger("updateSoldPrices")
.timeBased()
.everyDays(1)
.atHour(6)
.create();
}List every sale behind a price
The median is the quick answer. When you want the evidence, this optional function writes every sale for the keyword in A2 to a Sales tab: sold date, title, eBay's listed price, what the buyer actually paid on accepted Best Offers, shipping and the listing link.
// Optional: list every sale for one keyword on a "Sales" tab.
function pullSales() {
const key = PropertiesService.getScriptProperties().getProperty("COMPSNIPER_API_KEY");
const keyword = SpreadsheetApp.getActiveSpreadsheet().getSheets()[0].getRange("A2").getValue();
const res = UrlFetchApp.fetch(
API + "?count=240&bestOfferPrice=true&keyword=" + encodeURIComponent(keyword),
{ headers: { Authorization: "Bearer " + key } }
);
const items = JSON.parse(res.getContentText()).items;
const tab = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sales")
|| SpreadsheetApp.getActiveSpreadsheet().insertSheet("Sales");
tab.clear();
tab.appendRow(["Sold", "Title", "Sold price", "Paid (Best Offer)", "Shipping", "Currency", "Link"]);
items.forEach((i) => tab.appendRow([
i.endedAt, i.title, i.soldPrice, i.acceptedOfferPrice || "", i.shippingPrice, i.soldCurrency, i.url,
]));
}Prefer no code? The dashboard exports any search as a CSV you can open in Sheets: see how to export eBay sold listings to CSV and Excel. For more than 90 days, call GET /v1/research/sold from the same script for up to 3 years of sales.
Frequently asked questions
eBay sold prices in Google Sheets
How do I get eBay sold prices into Google Sheets automatically?
Paste a short Apps Script into your sheet that calls the CompSniper API for each keyword in column A and writes the median, typical range and number of sales next to it. Add a daily time-driven trigger and the sheet refreshes itself every morning. A free CompSniper key includes 100 requests a month.
Can I use IMPORTDATA or IMPORTXML instead?
No. IMPORTDATA cannot send the Authorization header an API needs, and eBay's sold pages do not load reliably through IMPORTXML. Apps Script's UrlFetchApp can send the header, so it is the reliable way.
Does it show the real price on Best Offer sales?
Yes. The script adds bestOfferPrice=true, so accepted Best Offer sales carry acceptedOfferPrice, what the buyer actually paid, on US-dollar sales. The optional Sales tab shows it next to eBay's listed price.
How many keywords can one sheet update?
Each keyword is one API request. Apps Script runs for up to 6 minutes per run, which is enough for a few dozen keywords; split a longer list across several tabs or triggers. Each request returns up to 240 sales.
Can I get sales older than 90 days in Sheets?
Yes. Call GET /v1/research/sold from the same script for up to 3 years of eBay sold history, on every plan including free.
Do I need an eBay developer account?
No. You only need a CompSniper API key. No eBay account, developer approval or cookies.