TITLE: Lat-lon coordinates from Google Takeout for Saved Places DATE: 2026-04-26 AUTHOR: John L. Godlee ==================================================================== I have lots of custom lists on Google Maps to journal places I've visited. I wanted to create an offline backup of these lists, just in case my Google account ever gets lost. Google Takeout allows you to export these lists, but inconveniently these exports don't include the lat-lon coordinates of the saved places, just a URL and the name of the place. [Google Takeout]: There are a few websites like Export Google Maps and Takeout Tools that can process Google Maps URLs, but they both have their drawbacks. Export Google Maps has an extremely slow processing time based on a queue system; I was quoted 18 days to process one file. Takeout Tools is a paid service, and this isn't something I care about enough to pay for. [Export Google Maps]: https://exportgooglemaps.com/ [Takeout Tools]: https://www.takeout-tools.com/ In the end I wrote an R script using the googleway package hooked up to the Google Geocoding API. The geocode_url() function in the script first tests whether a URL can be reached, extracts the place name from the Google Maps URL, uses googleway::google_geocode() to query the place name, and finally extracts additional information from the API query including the lat-lon coordinates, place name, address, and Plus Code. I'm using pass-cli to manage my API key, but this part of the script could be replaced to have the API key hard-coded as a variable. [googleway]: https://symbolixau.github.io/googleway/ [Google Geocoding API]: https://developers.google.com/maps/documentation/geocoding/guides-v3 /overview [pass-cli]: https://www.passwordstore.org/ This is the form of the URLs returned by Google Takeout: Place name URL -------------------------- -------------------------------------------------------------------- ---------------------------------------------- The Phillips Collection https://www.google.com/maps/place/The+Phillips+Collection/data=!4m2! 3m1!1s0x89b7b7c8c18d1011:0x5ebf642c05a560f7 National Building Museum https://www.google.com/maps/place/National+Building+Museum/data=!4m2 !3m1!1s0x89b7b78ee8345b73:0x48233bd191725f45 The Yards https://www.google.com/maps/place/The+Yards/data=!4m2!3m1!1s0x89b7b9 d7207fab4f:0x4ea43e59870c19cc Rock Creek Park https://www.google.com/maps/place/Rock+Creek+Park/data=!4m2!3m1!1s0x 89b7c8f06a225943:0x601267b7d15c622d # Extract lat-lon coordinates from Google Maps URL # John L. Godlee (johngodlee@gmail.com) # Last updated: 2026-04-26 # Packages library(httr2) library(googleway) # Call 'pass' to get API key api_key <- trimws( system2("pass", args = c("show", "google_geocoding_api_key"), stdout = TRUE)) # Check if key retrieved successfully if (length(api_key) == 0 || api_key == "") { stop("Failed to retrieve API key from pass.") } # Set API key set_key(api_key) # Define function geocode_url <- function(url) { # Expand URL resp <- tryCatch({ req <- request(url) req_perform(req) }, error = function(e) { message("Failed to resolve URL (Dead link/Timeout): ", url) return(NULL) }) # Standardise failure output fail_out <- data.frame( url = url, name = NA, address = NA, plus_code = NA, lat = NA, lng = NA) if (is.null(resp)) { return(fail_out) } # Extract URL from response full_url <- resp_url(resp) # Extract place name from URL regex_pattern <- "/place/([^/]+)/" match_info <- regexec(regex_pattern, full_url) place_match <- regmatches(full_url, match_info) # If place name matched if (length(place_match) > 0 && length(place_match[[1]]) >= 2) { raw_place_name <- place_match[[1]][2] place_name <- gsub("\\+", " ", raw_place_name) place_name <- URLdecode(place_name) # Query Google Geocoding API api_result <- google_geocode(address = place_name) # Extract coordinates and other information if (api_result$status == "OK") { coords <- access_result(api_result, "coordinates") address <- access_result(api_result, "address") plus_code <- NA if ("plus_code" %in% names(api_result$results)) { plus_code <- api_result$results$plus_code[1, "global_code"] } out <- data.frame( url = url, name = place_name, address = address, plus_code = plus_code, coords)[1, ] return(out) } else { message("API returned status '", api_result$status, "' for: ", place_name) return(fail_out) } } else { message("Could not extract place name from resolved URL: ", full_url) return(fail_out) } } # Import URLs google_takeout <- read.csv("./Takeout/Saved/Edinburgh food.csv") urls <- google_takeout$URL urls <- urls[urls != "" & !is.na(urls)] # Also filter out NAs just in case # Run function on URLs result_list <- lapply(urls, geocode_url) result_df <- do.call(rbind, result_list) result_df url name address plus_code lat lng -------------------------------------------------------------------- ---------------------------------------------- -------------------------- --------------------------------------------------- ------------- ------- -------- https://www.google.com/maps/place/The+Phillips+Collection/data=!4m2! 3m1!1s0x89b7b7c8c18d1011:0x5ebf642c05a560f7 The Phillips Collection 1600 21st St NW, Washington, DC 20009, USA 87C4WX63+P6 38.91 -77.05 https://www.google.com/maps/place/National+Building+Museum/data=!4m2 !3m1!1s0x89b7b78ee8345b73:0x48233bd191725f45 National Building Museum 401 F St NW, Washington, DC 20001, USA 87C4VXXJ+4X 38.90 -77.02 https://www.google.com/maps/place/The+Yards/data=!4m2!3m1!1s0x89b7b9 d7207fab4f:0x4ea43e59870c19cc The Yards 254 Alta Mar Dr, Ponte Vedra Beach, FL 32082, USA 862W6J75+XW 30.21 -81.39 https://www.google.com/maps/place/Rock+Creek+Park/data=!4m2!3m1!1s0x 89b7c8f06a225943:0x601267b7d15c622d Rock Creek Park Rock Creek Park, Washington, DC, USA 87C4XX83+XW 38.97 -77.05