Skip to content

Instantly share code, notes, and snippets.

@virgiliu
Created November 1, 2020 14:13
Show Gist options
  • Select an option

  • Save virgiliu/eefbadef4de9d2ecb2e01020ae471892 to your computer and use it in GitHub Desktop.

Select an option

Save virgiliu/eefbadef4de9d2ecb2e01020ae471892 to your computer and use it in GitHub Desktop.
Gumroad bulk download
// Run this in the content download page and it will trigger download for everything
var sleep = (milliseconds) => {
return new Promise(resolve => setTimeout(resolve, milliseconds))
}
var waitTime = 1500; //ms
var x = $( "button:contains('Download')" );
for(var i = 0; i < x.length ; i++)
{
(function(idx) {
// Wait needed because browser blocks network calls if you make too many too fast
sleep(i * waitTime).then(() => {
x[idx].click();
});
})(i)
}
@InfiniteCanvas
Copy link

I forgot to tell, I fixed it about an hour ago lol
I tried it out and it should work. Probably..

@Kawaru86
Copy link

I forgot to tell, I fixed it about an hour ago lol I tried it out and it should work. Probably..

Yup, looks like its working, thanks a bunch!!!!

@AzureArtism
Copy link

AzureArtism commented Mar 21, 2024

I tried @Kawaru86 's gumload plugin, and what I got was this:
Updating Library...
Failed to update library due to ['NoneType' object has no attribute 'string']
Downloading 0 files

My config.json:
{
"threads": 5,
"only_specified_creators": true,
"match_size_using_content_info": true,
"db_path": "gumload.json",
"refresh": true,
"folder": "E:\_GumroadResults\",
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36 OPR/107.0.0.0",
"_gumroad_app_session": "redacted",
"_gumroad_guid": "redacted",
"creators": [

]
}

Maybe it's because the creators array was empty... How do I get the right values to put in for name, id and count? I'd also like to note that I tried the stuff in the article that itswzyss made, but JDownloader didn't work with the text files outputted by either script.

@InfiniteCanvas
Copy link

Try changing using

"only_specified_creators": false

instead of "only_specified_creators": true

@Kawaru86
Copy link

Kawaru86 commented Mar 22, 2024 via email

@rursache
Copy link

rursache commented Apr 5, 2024

@InfiniteCanvas thanks for your work, the script works flawlessly!

@obsessedcake
Copy link

Hi all! I've update https://github.com/obsessedcake/gumroad-utils. If anyone interested, take a look ๐Ÿ˜„

@AzureArtism
Copy link

@InfiniteCanvas @Kawaru86 Thanks, it worked!

@virgiliu
Copy link
Author

virgiliu commented May 2, 2024

Hi all! I've update https://github.com/obsessedcake/gumroad-utils. If anyone interested, take a look ๐Ÿ˜„

@obsessedcake Thanks for building and sharing that. For me the original script was a one-time thing which I made public by mistake, but since people started talking I decided to leave it up even though I don't have the time to maintain it ๐Ÿ˜…

Copy link

ghost commented May 21, 2024

Can anyone please help me to download from the beginning and how to code it please.. that'd be great help thankyou

@H1mawari
Copy link

H1mawari commented Sep 24, 2024

Hi all! I've update https://github.com/obsessedcake/gumroad-utils. If anyone interested, take a look ๐Ÿ˜„

I've tried using the library but can't download any files via the URL I'm getting an error
image
What is this error and is there any way to fix it

@Beagon
Copy link

Beagon commented Nov 20, 2024

@itswzyss Good idea. Went through little tests with my file. Once a number of links attained, Gumroad decide to cut the flow and stop the connexion. So, I decided to reduce the file in multiple files, once per artist (even if you have multiple purchases). ๐Ÿคฃ One thing more. Content creator can use external links to host their files instead of gumroad. I alert of that in the files & console.

let promises = await Promise.all(Array.from(document.querySelectorAll("article a.stretched-link")) // Get promises with purchases download links.
    .map((link) => { return link.href })
    .map((link) => {
        return fetch(link) // Get link from purchase link.
            .then(res => res.text())
            .then(text => {

                let parser = new DOMParser(); // Create DOMContent from fetch content with download links.
                let doc = parser.parseFromString(text, "text/html");

                var script = doc.querySelector("script[data-component-name]");// Get script in which the download content JS is.
                
                if(JSON.parse(script.innerText).content.content_items.length === 0) 
                    console.log(JSON.parse(script.innerText).creator.name + " use an external hosting service. Please watch their files to get the purchased download links"); // Alert in console for external hosting services.

                return {
                    artist: JSON.parse(script.innerText).creator.name,
                    links: (JSON.parse(script.innerText).content.content_items.length > 0 ?
                        JSON.parse(script.innerText).content.content_items.map((item) => { return "https://app.gumroad.com" + item.download_url }) :
                        ["external link in following page : " + link])
                };// Return both the artist and the associated download URLs (if content is in external website from gumroad, the page will be alerted).
            });
    }));

let timer = 0; // Timer to delay the download (to avoid download throttle).

promises // Need the promises to be resolved from here.
    .reduce((acc, d) => {
        const found = acc.find(a => a.artist === d.artist);
        const value = d.links.flat(Infinity);
        if (!found) acc.push({ artist: d.artist, links: [value] })
        else found.links.push(value);
        return acc;
    }, [])// Regroup links per artist.
    .sort(function (a, b) {
        return a.artist.localeCompare(b.artist);
    })// Sort artist per name.
    .forEach((data) => {

        setTimeout(function () {
            var blob = new Blob([data.links.flat(Infinity).join("\n")], { type: "text/plain;charset=utf-8" });
            var url = window.URL || window.webkitURL;
            var link = url.createObjectURL(blob);// Creation of download link.

            var a = document.createElement("a");
            a.download = "downloads_" + data.artist + "_gumroad.txt";
            document.body.appendChild(a);
            a.href = link;// Creation of the download button.


            a.click(); // Click to begin download.
            a.remove(); // Remove the download button.
        }, timer += 1500);// Delay to avoid download throttle.
    });// From this, download
    ```

Small problem found, the element selector for the JSON wasn't correct anymore. Here is an updated version:

let promises = await Promise.all(Array.from(document.querySelectorAll("article a.stretched-link")) // Get promises with purchases download links.
    .map((link) => { return link.href })
    .map((link) => {
        return fetch(link) // Get link from purchase link.
            .then(res => res.text())
            .then(text => {

                let parser = new DOMParser(); // Create DOMContent from fetch content with download links.
                let doc = parser.parseFromString(text, "text/html");

                var script = doc.querySelector("script[data-component-name=\"DownloadPageWithContent\"]");// Get script in which the download content JS is.
                
                if(JSON.parse(script.innerText).content.content_items.length === 0) 
                    console.log(JSON.parse(script.innerText).creator.name + " use an external hosting service. Please watch their files to get the purchased download links"); // Alert in console for external hosting services.

                return {
                    artist: JSON.parse(script.innerText).creator.name,
                    links: (JSON.parse(script.innerText).content.content_items.length > 0 ?
                        JSON.parse(script.innerText).content.content_items.map((item) => { return "https://app.gumroad.com" + item.download_url }) :
                        ["external link in following page : " + link])
                };// Return both the artist and the associated download URLs (if content is in external website from gumroad, the page will be alerted).
            });
    }));

let timer = 0; // Timer to delay the download (to avoid download throttle).

promises // Need the promises to be resolved from here.
    .reduce((acc, d) => {
        const found = acc.find(a => a.artist === d.artist);
        const value = d.links.flat(Infinity);
        if (!found) acc.push({ artist: d.artist, links: [value] })
        else found.links.push(value);
        return acc;
    }, [])// Regroup links per artist.
    .sort(function (a, b) {
        return a.artist.localeCompare(b.artist);
    })// Sort artist per name.
    .forEach((data) => {

        setTimeout(function () {
            var blob = new Blob([data.links.flat(Infinity).join("\n")], { type: "text/plain;charset=utf-8" });
            var url = window.URL || window.webkitURL;
            var link = url.createObjectURL(blob);// Creation of download link.

            var a = document.createElement("a");
            a.download = "downloads_" + data.artist + "_gumroad.txt";
            document.body.appendChild(a);
            a.href = link;// Creation of the download button.


            a.click(); // Click to begin download.
            a.remove(); // Remove the download button.
        }, timer += 1500);// Delay to avoid download throttle.
    });// From this, download

@n0c0ntext
Copy link

n0c0ntext commented Nov 30, 2024

hi sorry am I missing something? I've put the code in developer tools same with the download page, It just shows an error or does nothing apart from says undefined. Do I need to put the code somewhere else?

@ayancey
Copy link

ayancey commented Dec 5, 2024

hi sorry am I missing something? I've put the code in developer tools same with the download page, It just shows an error or does nothing apart from says undefined. Do I need to put the code somewhere else?

Try replacing button:contains('Download') with a:contains('Download')

@dreamspy
Copy link

dreamspy commented Jan 31, 2025

I had to modify the script for it to work. I'm using the "href" attribute, and simply adding http://gumroad.com in front of that variable. The previous script was using the "data-resource-id" which is empty / undefined.

Also I had some problems using console download managers to download the urls, since gumroad asks for confirmation before starting the file download. But I was able to use the Chrono Chrome extension. Just simply press the "new" button and paste in the list of URLs with each URL in a new line.

var x = $("a.button:contains('Download')");

var result = [];

for (var i = 0; i < x.length; i++) {
  result.push(x[i].getAttribute("href"));
}

var currentUrl = window.location.href;
var newBaseUrl = currentUrl.replace("/d/", "/r/");
var newUrls = [];

result.forEach(function (resourceId) {
  newUrls.push("https://gumroad.com" + resourceId);
});

var blob = new Blob([newUrls.join("\n")], { type: "text/plain" });

var a = document.createElement("a");
a.href = URL.createObjectURL(blob);
a.download = "urls.txt";
a.style.display = "none";
document.body.appendChild(a);
a.click();

document.body.removeChild(a);
URL.revokeObjectURL(a.href);

@christophhdesign
Copy link

Something updated that worked for me in 02.2026.

Just copy & paste into your browser console while being on the Gurmoad downloads page


/**
 * Gumroad Bulk Downloader
 * 
 * A simple script to download all files from a Gumroad purchase page at once.
 * Useful when you've purchased a bundle with many files and don't want to
 * click each download link individually.
 * 
 * Usage:
 * 1. Open the Gumroad page with your purchased content
 * 2. Open Developer Tools (F12 or right-click > Inspect)
 * 3. Go to the "Console" tab
 * 4. Paste this entire script and press Enter
 * 5. Files will download with a delay between each to avoid rate limiting
 * 
 * License: MIT
 */

(async function() {
    'use strict';

    // --- Configuration ---
    const BASE_URL = 'https://app.gumroad.com';
    const DELAY_SECONDS = 5; // Delay between downloads to avoid rate limiting

    // --- Helper Functions ---
    const sleep = (seconds) => new Promise(resolve => setTimeout(resolve, seconds * 1000));

    const downloadFile = (url) => {
        const link = document.createElement('a');
        link.href = url;
        link.style.display = 'none';
        document.body.appendChild(link);
        link.click();
        document.body.removeChild(link);
    };

    // --- Main Logic ---
    console.log('%c๐Ÿš€ Gumroad Bulk Downloader', 'color: #4CAF50; font-size: 18px; font-weight: bold;');
    console.log('โ”'.repeat(50));

    // Find all download links
    const links = document.querySelectorAll('a[href*="/r/"][href*="product_files"]');
    const seen = new Set();
    const downloads = [];

    links.forEach(link => {
        const href = link.getAttribute('href');
        if (href && href.includes('product_file_ids') && !seen.has(href)) {
            seen.add(href);

            // Try to get the filename
            const container = link.closest('.embed') || link.closest('[role="treeitem"]') || link.parentElement;
            const h4 = container?.querySelector('h4');
            const fileName = h4?.textContent.trim() || `File ${downloads.length + 1}`;

            downloads.push({ url: href, name: fileName });
        }
    });

    // Report findings
    console.log(`%c๐Ÿ“‹ Found ${downloads.length} files to download`, 'color: #2196F3; font-size: 14px;');
    downloads.forEach((d, i) => console.log(`   ${i + 1}. ${d.name}`));

    if (downloads.length === 0) {
        console.log('%cโŒ No download links found. Are you on a Gumroad purchase page?', 'color: #f44336;');
        return;
    }

    console.log('โ”'.repeat(50));
    console.log(`%cโฑ๏ธ Starting downloads (${DELAY_SECONDS}s delay between each)...`, 'color: #FF9800;');

    // Download each file
    for (let i = 0; i < downloads.length; i++) {
        const { url, name } = downloads[i];
        const fullUrl = url.startsWith('http') ? url : BASE_URL + url;

        console.log(`%c\n[${i + 1}/${downloads.length}] ${name}`, 'color: #4CAF50; font-weight: bold;');

        downloadFile(fullUrl);

        if (i < downloads.length - 1) {
            console.log(`   Waiting ${DELAY_SECONDS}s...`);
            await sleep(DELAY_SECONDS);
        }
    }

    // Done!
    console.log('\n' + 'โ”'.repeat(50));
    console.log('%cโœ… All downloads started!', 'color: #4CAF50; font-size: 16px; font-weight: bold;');
    console.log('%c๐Ÿ“ Check your browser downloads folder.', 'color: #2196F3;');
})();

@niilevv
Copy link

niilevv commented Mar 8, 2026

Something updated that worked for me in 02.2026.

Just copy & paste into your browser console while being on the Gurmoad downloads page

worked like a charm thanks!(08/03/2026)
I am a bit puzzled how there is no bulk zip download available on gumroad for each Bundle of purchased Files

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