Used Game Prices

analysis
hobby
Author

Kevin Swenson

Published

July 14, 2026

Being a Millennial gamer, I have a fairly well curated (if I do say so myself) Nintendo DS and 3DS catalog. I’ve been looking to get rid of some games I’ve already beaten. Anyone who has sold things on marketplace knows how annoying it can be. I’ve found that if you collect some price data, and find some basic statistics, it makes showing potential buyers that you’ve fairly priced things easier. I get it, everyone wants to save a buck and get a good deal on used games.

I’m looking to sell:

First, let’s load some libraries!

library(tidyverse)
library(rvest)

Price Charting

Let’s load in some data from price charting.

url_chrono_trigger <- "https://www.pricecharting.com/game/nintendo-ds/chrono-trigger"
url_pokemon_black <- "https://www.pricecharting.com/game/nintendo-ds/pokemon-black"
url_pokemon_platinum <- "https://www.pricecharting.com/game/nintendo-ds/pokemon-platinum#completed-auctions-cib"
url_pokemon_soulsilver <- "https://www.pricecharting.com/game/nintendo-ds/pokemon-soulsilver-version-pokewalker#completed-auctions-graded"

scrape_data <- function(url) {
    page <- read_html(url)
    sales_rows <- page |>
      html_elements(xpath = "//tr[.//*[contains(concat(' ', normalize-space(@class), ' '), ' date ')]]")
    
    sales <- sales_rows |>
      map_dfr(\(row) {
        tibble(
          date = row |>
            html_element(".date") |>
            html_text2(),
          title = row |>
            html_element(".title") |>
            html_text2(),
          price = row |>
            html_element(".numeric .js-price, .numeric") |>
            html_text2()
        )
      }) |>
      mutate(
        price = parse_number(price)
      ) 
    
    sales <- sales |>
        filter(!is.na(price))
    
    return(sales)
}

df_chrono_trigger <-            scrape_data(url_chrono_trigger)
df_pokemon_black <-             scrape_data(url_pokemon_black)
df_pokemon_platinum <-      scrape_data(url_pokemon_platinum)
df_pokemon_soulsilver <-    scrape_data(url_pokemon_soulsilver)

Cool, now that I have this data I can do a little clean up. Since all of the copies I have are “complete in box” I need to filter out sales that aren’t in the same category.

clean_filter <- function(sales_data) {
output_data <- sales_data |>
  filter(
    str_detect(
      str_to_lower(title),
      "cib|complete in box|poster|pokewalker"
    )
  )

return(output_data)
}

df_cleaned_chrono_trigger <- clean_filter(df_chrono_trigger)
df_cleaned_pokemon_black <- clean_filter(df_pokemon_black)
df_cleaned_pokemon_platinum <- clean_filter(df_pokemon_platinum)
df_cleaned_pokemon_soulsilver <- clean_filter(df_pokemon_soulsilver)

Last step here is just to find some basic statistics about the prices so I can make sure to offer a fair price. Median is best to use here since it will trim out the high graded values.

tibble(
  game = c(
    "Chrono Trigger",
    "Pokemon Black",
    "Pokemon Platinum",
    "Pokemon SoulSilver"
  ),
  median_price = c(
    median(df_cleaned_chrono_trigger$price, na.rm = TRUE),
    median(df_cleaned_pokemon_black$price, na.rm = TRUE),
    median(df_cleaned_pokemon_platinum$price, na.rm = TRUE),
    median(df_cleaned_pokemon_soulsilver$price, na.rm = TRUE)
  )
)
# A tibble: 4 × 2
  game               median_price
  <chr>                     <dbl>
1 Chrono Trigger             127.
2 Pokemon Black              120.
3 Pokemon Platinum           225 
4 Pokemon SoulSilver         382.

I’d love to scrape ebay for prices, but I need to sign up for their api