Lat-lon coordinates from Google Takeout for Saved Places

2026-04-26

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.

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.

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.

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!1s0x89b7b9d7207fab4f:0x4ea43e59870c19cc
Rock Creek Park https://www.google.com/maps/place/Rock+Creek+Park/data=!4m2!3m1!1s0x89b7c8f06a225943: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!1s0x89b7b9d7207fab4f: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!1s0x89b7c8f06a225943:0x601267b7d15c622d Rock Creek Park Rock Creek Park, Washington, DC, USA 87C4XX83+XW 38.97 -77.05