Skip to content

Instantly share code, notes, and snippets.

@CGCode1000
Last active January 6, 2026 14:17
Show Gist options
  • Select an option

  • Save CGCode1000/8f0873d0849c69684c88290bf5481a9f to your computer and use it in GitHub Desktop.

Select an option

Save CGCode1000/8f0873d0849c69684c88290bf5481a9f to your computer and use it in GitHub Desktop.

Salesforce Backup Butler

A Throttled, UI-Enhanced Bookmarklet for Safe Salesforce Data Export Downloads. Designed primarily for Chrome, but works in any modern Chromium-based browser.

Salesforce Backup Butler is a JavaScript bookmarklet that automates downloading Salesforce Data Export ZIP files—one at a time, with a configurable delay— to avoid Salesforce’s aggressive rate-limiting (“HTTP 429 Too Many Requests”).

It adds:

  • 🔸 Row highlighting (shows which file is downloading next)
  • 🔸 An on-screen mini-control panel
  • 🔸 Pause, Resume, and Abort buttons
  • 🔸 “All downloads started” + “All downloads completed” alerts
  • 🔸 Super-throttled clicks to keep Salesforce happy

Perfect for orgs with 20–100 export ZIPs per month.


🧩 How to Install

  1. Open the file salesforce-backup-butler.min.js from this Gist.
  2. Copy the entire line or all lines if viewing as raw.
  3. In Chrome:
  • Right-click the bookmarks bar → Add Page
  • Name it: Salesforce Backup Butler
  • Paste the JavaScript into the URL field
  • Save

🚀 How to Use

Designed for the Salesforce Classic Data Export page

  1. Log into Salesforce.
  2. Go to:
    Setup → Data Management → Data Export
    (the page that shows your “Download” links)
  3. Once all export ZIP files are visible on the page, click the bookmark: Salesforce Backup Butler
  4. You’ll see:
  • A confirmation popup
  • A control overlay in the upper-right corner
  • Highlighting of the active row
  • Downloads initiating one by one

🎛 Controls

These buttons appear on screen:

Button Action
Pause Temporarily stop downloading mid-sequence
Resume Restart where you left off
Abort Cancel the remaining downloads completely

📝 Notes:

  • No data is ever transmitted anywhere.
    This bookmarklet runs entirely inside your already-authenticated Salesforce browser session and only clicks the download links you can already see.

  • The browser handles all actual downloads.
    Once a click is triggered, the download process is controlled by your browser’s internal download manager.

  • Webpage scripts cannot detect the completion of file downloads.
    For security reasons, browsers do not expose download lifecycle events to page JavaScript.
    “All downloads completed” simply means all download clicks have been safely issued.

  • Designed for the Salesforce Classic Data Export page.
    Lightning sometimes wraps export links inside dynamic components that may require a different selector.


⚠️ Disclaimers:

Salesforce Backup Butler is provided as-is, without warranty of any kind.
It performs only the automated clicking of visible download links inside your existing, authenticated Salesforce session—nothing more.

By using this bookmarklet, you acknowledge and agree that:

  • You are solely responsible for ensuring that its usage complies with your organization's policies.
  • You assume all risks associated with running custom scripts in your browser.
  • The creator(s) of this script are not liable for any direct or indirect damages, data loss, rate-limiting, or unintended behavior arising from its use.
  • Salesforce may change internal page structures or throttling behavior at any time, which may affect functionality.

This utility is offered purely as a convenience for reducing repetitive manual clicking.
If you do not understand or accept these terms, please refrain from using the script.


⚙️ Configuration

Inside the script is the 'pause between downloads' line. It is set to 70 seconds. Adjust as needed:

const delayMs = 70000;
/*
// 🔴 STOP — READ THIS FIRST
// ------------------------------------------------------------
// This is the *readable, commented* version of Salesforce Backup Butler.
// It is provided for clarity, auditing, and customization.
//
// ❗ DO NOT paste this file into a browser bookmark.
// ❗ This version will NOT run correctly as a bookmarklet.
//
// 🟢 To use the tool, please copy the one-line minified version:
// 🟢 → salesforce-backup-butler.min.js
// 🟢 and paste THAT entire line into your browser’s bookmark URL field.
//
// This file is for reference only.
// ------------------------------------------------------------
Salesforce Backup Butler
A safe, throttled bookmarklet for downloading all Salesforce Data Export ZIP files
one by one with built-in delay, row highlighting, progress overlay, and pause/resume/abort controls.
*/
(function () {
const EXPORT_SELECTOR = 'a[href*="servlet/servlet.OrgExport"]';
const links = [...document.querySelectorAll(EXPORT_SELECTOR)];
if (!links.length) {
alert('No export links found. Make sure you are on the Salesforce Data Export page.');
return;
}
// If a previous session is running, offer to abort it
if (window.__sfBackupButler && window.__sfBackupButler.running) {
if (!confirm('A download session is already running. Abort it and start over?')) {
return;
}
window.__sfBackupButler.abort();
}
const total = links.length;
let index = 0;
let paused = false;
let aborted = false;
const delayMs = 70000; // 70 seconds per file — adjust if throttled
function log(msg) {
console.log('[BackupButler]', msg);
}
// === Overlay UI ============================================================
const overlay = document.createElement('div');
overlay.style.position = 'fixed';
overlay.style.top = '10px';
overlay.style.right = '10px';
overlay.style.zIndex = '999999';
overlay.style.background = 'rgba(0,0,0,0.85)';
overlay.style.color = '#fff';
overlay.style.padding = '8px 10px';
overlay.style.fontSize = '12px';
overlay.style.borderRadius = '4px';
overlay.style.fontFamily = 'system-ui, -apple-system, BlinkMacSystemFont, sans-serif';
overlay.style.boxShadow = '0 0 5px rgba(0,0,0,0.6)';
overlay.innerHTML = `
<div id="sbb-status">Starting…</div>
<div style="margin-top:4px;">
<button id="sbb-pause" style="margin-right:4px;">Pause</button>
<button id="sbb-resume" style="margin-right:4px;">Resume</button>
<button id="sbb-abort">Abort</button>
</div>
`;
document.body.appendChild(overlay);
const statusEl = overlay.querySelector('#sbb-status');
const pauseBtn = overlay.querySelector('#sbb-pause');
const resumeBtn = overlay.querySelector('#sbb-resume');
const abortBtn = overlay.querySelector('#sbb-abort');
pauseBtn.onclick = function () {
paused = true;
statusEl.textContent = `Paused at ${index}/${total}`;
log('Paused');
};
resumeBtn.onclick = function () {
if (!aborted) {
paused = false;
statusEl.textContent = `Resuming… ${index}/${total}`;
log('Resuming');
}
};
abortBtn.onclick = function () {
aborted = true;
window.__sfBackupButler.running = false;
clearHighlight();
statusEl.textContent = `Aborted at ${index}/${total}`;
log('Aborted by user.');
};
// === Row Highlighting ======================================================
function clearHighlight() {
links.forEach(a => {
const row = a.closest('tr');
if (row) row.style.outline = '';
});
}
function highlight(link) {
clearHighlight();
const row = link.closest('tr');
if (row) {
row.style.outline = '3px solid orange';
row.scrollIntoView({ behavior: 'smooth', block: 'center' });
}
}
// === Main Loop =============================================================
function step() {
if (aborted) {
log('Stopped (aborted).');
return;
}
if (paused) {
setTimeout(step, 1000);
return;
}
if (index >= total) {
statusEl.textContent = `Done: ${total}/${total}`;
window.__sfBackupButler.running = false;
clearHighlight();
log(`All downloads initiated (${total} files).`);
alert(`All downloads completed (${total} files).`);
return;
}
const link = links[index];
highlight(link);
link.click();
const current = index + 1;
statusEl.textContent = `Downloading ${current}/${total}`;
log(`Downloading ${current}/${total}: ${link.href}`);
index++;
setTimeout(step, delayMs);
}
// Expose for console use
window.__sfBackupButler = {
pause: function () { paused = true; },
resume: function () { paused = false; },
abort: function () { aborted = true; this.running = false; clearHighlight(); },
running: true
};
alert(`All downloads started (${total} files).`);
statusEl.textContent = `Starting… 0/${total}`;
log(`Starting backup sequence with ${total} files, delay ${delayMs / 1000}s.`);
step();
})();
javascript:(function(){const EXPORT_SELECTOR='a[href*="servlet/servlet.OrgExport"]';const links=[...document.querySelectorAll(EXPORT_SELECTOR)];if(!links.length){alert('No export links found. Make sure you are on the Salesforce Data Export page.');return;}if(window.__sfBackupButler&&window.__sfBackupButler.running){if(!confirm('A download session is already running. Abort it and start over?')){return;}window.__sfBackupButler.abort();}const total=links.length;let index=0,paused=false,aborted=false;const delayMs=70000;function log(m){console.log('[BackupButler]',m);}const ui=document.createElement('div');ui.style='position:fixed;top:10px;right:10px;z-index:999999;background:rgba(0,0,0,0.85);color:#fff;padding:8px 10px;font-size:12px;border-radius:4px;font-family:system-ui,-apple-system,BlinkMacSystemFont,sans-serif;box-shadow:0 0 5px rgba(0,0,0,0.6)';ui.innerHTML='<div id="sbb-status">Starting…</div><div style="margin-top:4px;"><button id="sbb-pause" style="margin-right:4px;">Pause</button><button id="sbb-resume" style="margin-right:4px;">Resume</button><button id="sbb-abort">Abort</button></div>';document.body.appendChild(ui);const statusEl=ui.querySelector('#sbb-status'),pauseBtn=ui.querySelector('#sbb-pause'),resumeBtn=ui.querySelector('#sbb-resume'),abortBtn=ui.querySelector('#sbb-abort');pauseBtn.onclick=()=>{paused=true;statusEl.textContent=`Paused at ${index}/${total}`;log('Paused');};resumeBtn.onclick=()=>{if(!aborted){paused=false;statusEl.textContent=`Resuming… ${index}/${total}`;log('Resuming');}};abortBtn.onclick=()=>{aborted=true;window.__sfBackupButler.running=false;clearHL();statusEl.textContent=`Aborted at ${index}/${total}`;log('Aborted by user.')};function clearHL(){links.forEach(a=>{const row=a.closest('tr');if(row)row.style.outline='';});}function highlight(a){clearHL();const r=a.closest('tr');if(r){r.style.outline='3px solid orange';r.scrollIntoView({behavior:'smooth',block:'center'});}}function step(){if(aborted){log('Stopped');return;}if(paused){setTimeout(step,1000);return;}if(index>=total){statusEl.textContent=`Done: ${total}/${total}`;window.__sfBackupButler.running=false;clearHL();alert(`All downloads completed (${total} files).`);return;}const a=links[index];highlight(a);a.click();const current=index+1;statusEl.textContent=`Downloading ${current}/${total}`;log(`Downloading ${current}/${total}: ${a.href}`);index++;setTimeout(step,delayMs);}window.__sfBackupButler={pause:()=>{paused=true;},resume:()=>{paused=false;},abort:()=>{aborted=true;clearHL();this.running=false;},running:true};alert(`All downloads started (${total} files).`);statusEl.textContent=`Starting… 0/${total}`;log(`Starting (${total} files) delay ${delayMs/1000}s`);step();})();
@rehgarde
Copy link

rehgarde commented Dec 9, 2025

Thank you for this! It is very helpful and works great!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment