Need to see if your shared folder is taking up space on your dropbox 👨💻? Find out how to check here.
Forum Discussion
prjtprgn
9 months agoNew member | Level 1
Unable to Retrieve Dropbox "path_display" for Folders in Google Sheets Script
I'm developing a Google Sheets database that feeds an interactive map on My Maps. The script uses the Dropbox API to update folder names (in column A) and folder paths (in column F) based on metadata retrieved from Dropbox shared links. Although every folder is mounted on my account and I have full administrative rights on my Dropbox team, I'm not seeing the expected "path_display" value for my Dropbox folders.
The script calls several API endpoints (such as sharing/get_shared_link_metadata, sharing/list_folders, sharing/list_mountable_folders, and sharing/get_folder_metadata) to try to obtain the complete folder path, but the path_display property remains empty in my sheet.
I need to retrieve path_display because it lets me move folders in Dropbox while still keeping the most recent path in my map. The script is set to run weekly so that any folder moves are captured and my map stays up to date.
Below is the complete script (with sensitive credentials, paths, and other details replaced):
I tried this script in Google Apps Script:
```
// =====================================================
// CONFIGURATION & CONSTANTS
// =====================================================
// Google Maps API Key (for geocoding)
const API_KEY = "YOUR_GOOGLE_MAPS_API_KEY";
// OAuth2 Dropbox: Replace with your Dropbox credentials
const DROPBOX_CLIENT_ID = 'YOUR_DROPBOX_CLIENT_ID';
const DROPBOX_CLIENT_SECRET = 'YOUR_DROPBOX_CLIENT_SECRET';
const DROPBOX_SCOPES = 'sharing.read files.metadata.read files.team_metadata.read team_data.member';
// Replace with your Dropbox team member ID (the member on which you want to operate)
// Example: "dbmid:example_member_id"
const DROPBOX_TEAM_MEMBER_ID = 'YOUR_DROPBOX_TEAM_MEMBER_ID';
const BATCH_SIZE = 100;
const DELAY_BETWEEN_REQUESTS = 1000;
// =====================================================
// OAUTH2 FOR DROPBOX (using the OAuth2 library)
// =====================================================
function getDropboxService() {
return OAuth2.createService('Dropbox')
.setAuthorizationBaseUrl('https://www.dropbox.com/oauth2/authorize')
.setTokenUrl('https://api.dropboxapi.com/oauth2/token')
.setClientId(DROPBOX_CLIENT_ID)
.setClientSecret(DROPBOX_CLIENT_SECRET)
.setCallbackFunction('authCallback')
.setPropertyStore(PropertiesService.getUserProperties())
.setScope(DROPBOX_SCOPES)
// Request a refresh token
.setParam('token_access_type', 'offline');
}
function authCallback(request) {
const service = getDropboxService();
const authorized = service.handleCallback(request);
return HtmlService.createHtmlOutput(authorized ? 'Success! You can close this tab.' : 'Access denied.');
}
function authorizeDropbox() {
const service = getDropboxService();
if (!service.hasAccess()) {
const authorizationUrl = service.getAuthorizationUrl();
Logger.log('Open the following URL and authorize the app, then re-run the script: %s', authorizationUrl);
return authorizationUrl;
} else {
Logger.log('Already authorized with Dropbox.');
}
}
// =====================================================
// FUNCTIONS FOR UPDATING COORDINATES
// =====================================================
function updateCoordinates() {
const sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
const startRow = 4;
const addresses = sheet.getRange(`B${startRow}:B${sheet.getLastRow()}`).getValues().flat();
const gpsData = sheet.getRange(`D${startRow}:D${sheet.getLastRow()}`).getValues().flat();
addresses.forEach((address, index) => {
const gps = gpsData[index];
if (address && !gps) {
const coordinates = getCoordinates(address);
if (coordinates) {
sheet.getRange(index + startRow, 4).setValue(coordinates);
}
}
});
SpreadsheetApp.flush();
}
function getCoordinates(address) {
const url = `https://maps.googleapis.com/maps/api/geocode/json?address=${encodeURIComponent(address)}&key=${API_KEY}`;
try {
const response = UrlFetchApp.fetch(url);
const json = JSON.parse(response.getContentText());
if (json.status === "OK") {
const latitude = json.results[0].geometry.location.lat;
const longitude = json.results[0].geometry.location.lng;
return `${latitude}, ${longitude}`;
} else {
console.error(`API error for address: ${address} - Status: ${json.status}`);
return "API error";
}
} catch (error) {
console.error(`Error fetching coordinates: ${error.message}`);
return "API error";
}
}
// =====================================================
// FUNCTIONS FOR UPDATING STATUS (column E) FROM PATHS (column F)
// =====================================================
function updateStatusFromPaths() {
const sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
const startRow = 4;
const lastRow = sheet.getLastRow();
const paths = sheet.getRange(`F${startRow}:F${lastRow}`).getValues().flat();
const statuses = [];
paths.forEach(path => {
if (path) {
if (path.includes("ActiveDeals/Active") ||
path.includes("C:\\Path\\To\\ActiveDeals\\Active")) {
statuses.push(["Active"]);
} else if (path.includes("ActiveDeals/ToWatch") ||
path.includes("C:\\Path\\To\\ActiveDeals\\ToWatch")) {
statuses.push(["To watch"]);
} else if (path.includes("Properties/Owned") ||
path.includes("C:\\Path\\To\\Properties\\Owned")) {
statuses.push(["Owned"]);
} else if (path.includes("Properties/Sold") ||
path.includes("C:\\Path\\To\\Properties\\Sold")) {
statuses.push(["Sold"]);
} else if (path.includes("DeadDeals") ||
path.includes("C:\\Path\\To\\DeadDeals")) {
statuses.push(["Dead"]);
} else {
statuses.push(["Undefined"]);
}
} else {
statuses.push([""]);
}
});
sheet.getRange(startRow, 5, statuses.length, 1).setValues(statuses);
}
// =====================================================
// FUNCTIONS FOR UPDATING DROPBOX INFORMATION
// =====================================================
/**
* This script updates both the folder name (column A)
* and the folder path (column F) by comparing the path_display returned
* by Dropbox. Any changes are logged in column H.
*/
function updateFolderNamesInBatches() {
const sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
const startRow = 4;
const lastRow = sheet.getLastRow();
const scriptProperties = PropertiesService.getScriptProperties();
let batchIndex = parseInt(scriptProperties.getProperty('batchIndexDropbox') || '0');
const totalBatches = Math.ceil((lastRow - startRow + 1) / BATCH_SIZE);
// If all batches have been processed, reset and stop
if (batchIndex >= totalBatches) {
scriptProperties.deleteProperty('batchIndexDropbox');
Logger.log("All batches processed. Function stopped.");
return;
}
const batchStart = startRow + (batchIndex * BATCH_SIZE);
const batchEnd = Math.min(batchStart + BATCH_SIZE - 1, lastRow);
// Check if the batch contains any sharing links (Column C)
const linksRange = sheet.getRange(`C${batchStart}:C${batchEnd}`).getValues();
let hasLink = false;
for (let i = 0; i < linksRange.length; i++) {
if (linksRange[i][0].toString().trim() !== "") {
hasLink = true;
break;
}
}
// If no links are found in the batch, stop processing
if (!hasLink) {
scriptProperties.deleteProperty('batchIndexDropbox');
Logger.log("No sharing links found in batch. Function stopped.");
return;
}
// Clear previous statuses for the current batch (Column H)
sheet.getRange(`H${batchStart}:H${batchEnd}`).clearContent();
// Process the current batch
updateBatch(batchStart, batchEnd);
// Update the batch index
batchIndex++;
scriptProperties.setProperty('batchIndexDropbox', batchIndex.toString());
// Schedule the next batch if there are more rows to process
if (batchIndex < totalBatches) {
ScriptApp.newTrigger('updateFolderNamesInBatches')
.timeBased()
.after(1000)
.create();
}
}
/**
* Processes the rows in the batch,
* compares existing names/paths with those returned by Dropbox, and updates if necessary.
*/
function updateBatch(startRow, endRow) {
const sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
// Read columns A (folderNames), C (sharingLinks) and F (folderPaths)
const folderNames = sheet.getRange(`A${startRow}:A${endRow}`).getValues().flat();
const sharingLinks = sheet.getRange(`C${startRow}:C${endRow}`).getValues().flat();
const folderPaths = sheet.getRange(`F${startRow}:F${endRow}`).getValues().flat();
for (let index = 0; index < sharingLinks.length; index++) {
const link = sharingLinks[index];
const currentRow = startRow + index;
// Skip if the link is empty
if (!link || link.trim() === "") {
Logger.log(`Stopping at row ${currentRow}: No sharing link found.`);
return;
}
try {
const metadata = getDropboxMetadata(link);
let statusMessages = [];
if (metadata && metadata.name) {
// Compare folder name
if (folderNames[index] !== metadata.name) {
sheet.getRange(currentRow, 1).setValue(metadata.name);
statusMessages.push("Folder name updated");
}
// Compare folder path
if (metadata.path && folderPaths[index] !== metadata.path) {
sheet.getRange(currentRow, 6).setValue(metadata.path);
statusMessages.push("Path updated");
}
if (statusMessages.length === 0) {
statusMessages.push("No changes needed");
}
sheet.getRange(currentRow, 8).setValue(statusMessages.join(" | "));
} else {
sheet.getRange(currentRow, 8).setValue("Failed: No metadata/name");
}
} catch (err) {
sheet.getRange(currentRow, 8).setValue("Failed: " + err.message);
}
// Delay to respect rate limits
Utilities.sleep(DELAY_BETWEEN_REQUESTS);
}
SpreadsheetApp.flush();
}
/**
* getDropboxMetadata retrieves the name and path_display
* from the Dropbox sharing link.
*
* It uses multiple methods to try to obtain the full path,
* handling various limitations of the Dropbox API.
*/
function getDropboxMetadata(sharingLink) {
const service = getDropboxService();
if (!service.hasAccess()) {
throw new Error("Authorization required. Run authorizeDropbox() first.");
}
const accessToken = service.getAccessToken();
// First call to sharing/get_shared_link_metadata
let apiUrl = "https://api.dropboxapi.com/2/sharing/get_shared_link_metadata";
let payload = { url: sharingLink };
let options = {
method: "post",
contentType: "application/json",
headers: {
Authorization: "Bearer " + accessToken,
"Dropbox-API-Select-User": DROPBOX_TEAM_MEMBER_ID
},
payload: JSON.stringify(payload),
muteHttpExceptions: true
};
let response = UrlFetchApp.fetch(apiUrl, options);
let responseText = response.getContentText();
Logger.log("API Response for " + sharingLink + ": " + responseText);
let json;
try {
json = JSON.parse(responseText);
} catch (e) {
throw new Error("Invalid JSON response: " + responseText);
}
if (json.error_summary) {
throw new Error("Dropbox API error: " + json.error_summary);
}
// For shared folders, try a different approach to get the path
if (!json.path_display && json['.tag'] === 'folder' && json.id) {
// Try with sharing/list_folders endpoint if it's a shared folder
apiUrl = "https://api.dropboxapi.com/2/sharing/list_folders";
payload = {
limit: 100
};
options.payload = JSON.stringify(payload);
try {
response = UrlFetchApp.fetch(apiUrl, options);
responseText = response.getContentText();
Logger.log("List Folders API Response: " + responseText);
const listFolders = JSON.parse(responseText);
if (listFolders.entries && listFolders.entries.length > 0) {
// Look for matching entry by shared_folder_id
const matchingFolder = listFolders.entries.find(folder =>
folder.shared_folder_id === json.id.replace('id:', '')
);
if (matchingFolder && matchingFolder.path_lower) {
json.path_display = matchingFolder.path_lower;
}
}
} catch (e) {
Logger.log("Fallback list_folders method failed: " + e.message);
}
// If still no path_display, try with list_mountable_folders
if (!json.path_display) {
apiUrl = "https://api.dropboxapi.com/2/sharing/list_mountable_folders";
payload = {
limit: 100
};
options.payload = JSON.stringify(payload);
try {
response = UrlFetchApp.fetch(apiUrl, options);
responseText = response.getContentText();
Logger.log("List Mountable Folders API Response: " + responseText);
const listFolders = JSON.parse(responseText);
if (listFolders.entries && listFolders.entries.length > 0) {
// Look for matching entry by shared_folder_id
const matchingFolder = listFolders.entries.find(folder =>
folder.shared_folder_id === json.id.replace('id:', '')
);
if (matchingFolder && matchingFolder.path_lower) {
json.path_display = matchingFolder.path_lower;
}
}
} catch (e) {
Logger.log("Fallback list_mountable_folders method failed: " + e.message);
}
}
// Try one more approach with sharing/get_folder_metadata
if (!json.path_display) {
apiUrl = "https://api.dropboxapi.com/2/sharing/get_folder_metadata";
payload = {
shared_folder_id: json.id.replace('id:', '')
};
options.payload = JSON.stringify(payload);
try {
response = UrlFetchApp.fetch(apiUrl, options);
responseText = response.getContentText();
Logger.log("Get Folder Metadata API Response: " + responseText);
const folderMetadata = JSON.parse(responseText);
if (folderMetadata.path_lower) {
json.path_display = folderMetadata.path_lower;
}
} catch (e) {
Logger.log("Fallback get_folder_metadata method failed: " + e.message);
}
}
}
return {
name: json.name || "",
path: json.path_display || ""
};
}
function startFolderNameUpdate() {
PropertiesService.getScriptProperties().setProperty("batchIndexDropbox", "0");
updateFolderNamesInBatches();
}
// =====================================================
// SETUP TRIGGERS
// =====================================================
function setupTriggers() {
// Delete all existing triggers.
ScriptApp.getProjectTriggers().forEach(function(trigger) {
ScriptApp.deleteTrigger(trigger);
});
// Trigger on change to update coordinates.
ScriptApp.newTrigger("updateCoordinates")
.forSpreadsheet(SpreadsheetApp.getActiveSpreadsheet())
.onChange()
.create();
// Trigger on change to update statuses from paths.
ScriptApp.newTrigger("updateStatusFromPaths")
.forSpreadsheet(SpreadsheetApp.getActiveSpreadsheet())
.onChange()
.create();
// Time-based trigger to update Dropbox folder names & paths in batches (every Monday at 15:00).
ScriptApp.newTrigger("updateFolderNamesInBatches")
.timeBased()
.onWeekDay(ScriptApp.WeekDay.MONDAY)
.atHour(15)
.create();
Logger.log("Triggers set: onChange for updateCoordinates and updateStatusFromPaths; weekly for updateFolderNamesInBatches.");
}
// Function to manually reset the batch index.
function resetBatchIndex() {
PropertiesService.getScriptProperties().deleteProperty('batchIndexDropbox');
Logger.log("Batch index reset to start from the beginning (row 4)");
}
// Function to reset old Dropbox credentials.
function resetDropboxAuth() {
PropertiesService.getUserProperties().deleteAllProperties();
Logger.log("Dropbox authorization reset. Re-run authorizeDropbox().");
}
```
Typical Log Output:
Here are examples of the log messages I typically see when the script runs:
API Response Log:
`3 March 2025, 15:10:42 Info API Response for https://www.dropbox.com/scl/fo/REPLACE_URL?dl=0: {"tag": "folder", "url": "https://www.dropbox.com/scl/fo/REPLACE_URL?dl=0", "id": "id:REPLACE_ID", "name": "REPLACE_NAME", "link_permissions": { ... }, "team_member_info": { ... }}`
Fallback Log:
`3 March 2025, 15:10:42 Info Fallback API Response for https://www.dropbox.com/scl/fo/REPLACE_URL?dl=0: Error in call to API function "files/get_metadata": request body: unknown field 'shared_link'`
In these logs, all sensitive details (such as URLs, IDs, names, and team names) have been replaced with placeholders.
My question is:
Why isn't the path_display property being returned by the Dropbox API, even though my account has the necessary permissions? Retrieving this property is crucial because it allows me to move folders in Dropbox and still maintain the most recent folder path in my interactive map. This is why I run weekly updates—to ensure that the map reflects any changes in folder structure.
Any help or suggestions would be greatly appreciated!
Hi prjtprgn,
It looks like there are a couple of issues with your script. First, it is expected for the endpoint /sharing/get_shared_link_metadata to not return a "path_display" property.
The next issue appears to be after a request is sent to /sharing/list_folders and /sharing/list_mountable_folders, the script seems to be trying to match the "shared_folder_id" with a "folder_id" property:
const matchingFolder = listFolders.entries.find(folder => folder.shared_folder_id === json.id.replace('id:', '') );These two values are completely different. Even though a "shared_folder_id" is a string, it is usually a numeric-only value, as such "84528192421". Whereas a "folder_id" is an alphanumeric value, prepended by the word "id", as such: "id:a4ayc_80_OEAAAAAAAAAXw".
Additionally, /sharing/get_folder_metadata is expecting a "shared_folder_id" value, the script is sending it a "folder_id" value:
payload = { shared_folder_id: json.id.replace('id:', '') };As previously mentioned, these are completely different values, even for the same folder.
Lastly, the following fallback log:
Fallback Log: `3 March 2025, 15:10:42 Info Fallback API Response for https://www.dropbox.com/scl/fo/REPLACE_URL?dl=0: Error in call to API function "files/get_metadata": request body: unknown field 'shared_link'`Indicates a request was possibly sent to /files/get_metadata endpoint. This endpoint does not accept a "shared_link" as a parameter.
To get better insight into what’s happening, I’d recommend adding additional loggers to your script. This will help you visualize all the data being returned by the API and confirm whether you’re working with the right values.
1 Reply
Replies have been turned off for this discussion
- DB-Des9 months ago
Dropbox Community Moderator
Hi prjtprgn,
It looks like there are a couple of issues with your script. First, it is expected for the endpoint /sharing/get_shared_link_metadata to not return a "path_display" property.
The next issue appears to be after a request is sent to /sharing/list_folders and /sharing/list_mountable_folders, the script seems to be trying to match the "shared_folder_id" with a "folder_id" property:
const matchingFolder = listFolders.entries.find(folder => folder.shared_folder_id === json.id.replace('id:', '') );These two values are completely different. Even though a "shared_folder_id" is a string, it is usually a numeric-only value, as such "84528192421". Whereas a "folder_id" is an alphanumeric value, prepended by the word "id", as such: "id:a4ayc_80_OEAAAAAAAAAXw".
Additionally, /sharing/get_folder_metadata is expecting a "shared_folder_id" value, the script is sending it a "folder_id" value:
payload = { shared_folder_id: json.id.replace('id:', '') };As previously mentioned, these are completely different values, even for the same folder.
Lastly, the following fallback log:
Fallback Log: `3 March 2025, 15:10:42 Info Fallback API Response for https://www.dropbox.com/scl/fo/REPLACE_URL?dl=0: Error in call to API function "files/get_metadata": request body: unknown field 'shared_link'`Indicates a request was possibly sent to /files/get_metadata endpoint. This endpoint does not accept a "shared_link" as a parameter.
To get better insight into what’s happening, I’d recommend adding additional loggers to your script. This will help you visualize all the data being returned by the API and confirm whether you’re working with the right values.
About Dropbox API Support & Feedback
Find help with the Dropbox API from other developers.
The Dropbox Community team is active from Monday to Friday. We try to respond to you as soon as we can, usually within 2 hours.
If you need more help you can view your support options (expected response time for an email or ticket is 24 hours), or contact us on X, Facebook or Instagram.
For more info on available support options for your Dropbox plan, see this article.
If you found the answer to your question in this Community thread, please 'like' the post to say thanks and to let us know it was useful!