Nexus Download Collection

Download every mods of a collection in a single click

< Feedback on Nexus Download Collection

Review: Good - script works

§
Posted: 2026-05-03

**[FIXED]** "Failed to get download link" - Nexus migrated to React, regex approach is broken

For anyone getting "Failed to get download link" on every mod: the root cause is that Nexus Mods rebuilt their site with React/Next.js. The download URL is no longer present in the initial HTML - it's injected by JavaScript after the page loads, so regex matching against the raw HTML will always fail.

The fix is to skip HTML scraping entirely and call the `GenerateDownloadUrl` API endpoint directly (the same one Nexus's own frontend calls internally), with the correct headers.

**How to apply the fix:**
In Tampermonkey, open the script editor and replace the entire `fetchDownloadLink` method (starting at `async fetchDownloadLink(mod) {`) with the code below.

```javascript
async fetchDownloadLink(mod) {
this.bypassNexusAdsCookie();

let downloadUrl = "";
let text = "";

// --- Helper: call the GenerateDownloadUrl API directly ---
const callGenerateApi = async (nmm = false) => {
try {
const body = `fid=${mod.fileId}&game_id=${mod.file.mod.game.id}${nmm ? "&nmm=1" : ""}`;
const apiResponse = await fetch(
"https://www.nexusmods.com/Core/Libs/Common/Managers/Downloads?GenerateDownloadUrl",
{
headers: {
"content-type": "application/x-www-form-urlencoded; charset=UTF-8",
"Referer": mod.file.url,
"X-Requested-With": "XMLHttpRequest",
},
body,
method: "POST",
credentials: "include",
},
);
if (!apiResponse.ok) return "";
const json = await apiResponse.json();
return json?.url || json?.URI || json?.src || json?.download_url || "";
} catch (e) {
console.error("[NDC] GenerateDownloadUrl API error:", e);
return "";
}
};

if (this.downloadMethod === NDCDownloadButton.DOWNLOAD_METHOD_VORTEX) {
const response = await fetch(`${mod.file.url}&nmm=1`);
text = await response.text();

if (!response.ok) return { downloadUrl: "", text };

// Try all known regex patterns first (kept as fallback)
const patterns = [
/const downloadUrl = '([^']+)'/,
/id="slowDownloadButton".*?data-download-url="([^"]+)"/,
/data-download-url="([^"]+)"/,
/]+download-url="([^"]+)"/,
/download-url="([^"]+)"/,
/(nxm:\/\/[^\s"'<]+)/,
/"url"\s*:\s*"([^"]+)"/,
];

for (const pattern of patterns) {
const match = text.match(pattern);
if (match) {
downloadUrl = match[1].replaceAll('&', '&');
break;
}
}

// If HTML scraping failed, hit the API directly
if (!downloadUrl) downloadUrl = await callGenerateApi(true);
if (!downloadUrl) downloadUrl = await callGenerateApi(false);

} else {
const response = await fetch(mod.file.url);
text = await response.text();

if (!response.ok) return { downloadUrl: "", text };

downloadUrl = await callGenerateApi(false);
}

return { downloadUrl, text };
}
```

§
Posted: 2026-05-03

I tried it and it works perfectly thanks!!!!

§
Posted: 2026-05-04

OMG THANK YOU SO MUCH IT WORKED FLAWLESSLY!

§
Posted: 2026-05-05
Edited: 2026-05-05

Actually, uh. the mods get sent to vortex to download, but then it says it failed to request the URL with a 403 status... guessing i'll need to download modpacks via browser now? :v

Mods are sent to vortex but gives url 403 status (inside vortex)

§
Posted: 2026-05-05

L,

try this:

```
async fetchDownloadLink(mod) {
this.bypassNexusAdsCookie();

let downloadUrl = "";
let text = "";

const decodeDownloadUrl = (url) => {
if (!url) return "";

return url
.replaceAll("&", "&")
.replaceAll("\\/", "/")
.replaceAll("/", "/")
.replaceAll(":", ":")
.replaceAll("?", "?")
.replaceAll("=", "=")
.replaceAll("&", "&");
};

const callGenerateApi = async (nmm = false) => {
try {
const body = new URLSearchParams();

/*
Nexus changed/standardized this.
The old script used "fid".
The current endpoint expects "file_id".
*/
body.set("file_id", String(mod.fileId));
body.set("game_id", String(mod.file.mod.game.id));

if (nmm) {
body.set("nmm", "1");
}

const apiResponse = await fetch(
"https://www.nexusmods.com/Core/Libs/Common/Managers/Downloads?GenerateDownloadUrl",
{
headers: {
"content-type": "application/x-www-form-urlencoded; charset=UTF-8",
"Referer": mod.file.url,
"X-Requested-With": "XMLHttpRequest",
},
body: body.toString(),
method: "POST",
credentials: "include",
},
);

const responseText = await apiResponse.text();

if (!apiResponse.ok) {
console.error("[NDC] GenerateDownloadUrl failed:", apiResponse.status, responseText);
return "";
}

let json;
try {
json = JSON.parse(responseText);
} catch (e) {
console.error("[NDC] GenerateDownloadUrl returned invalid JSON:", responseText);
return "";
}

const url =
json?.url ||
json?.URI ||
json?.src ||
json?.download_url ||
"";

return decodeDownloadUrl(url);
} catch (e) {
console.error("[NDC] GenerateDownloadUrl API error:", e);
return "";
}
};

if (this.downloadMethod === NDCDownloadButton.DOWNLOAD_METHOD_VORTEX) {
/*
This is the same thing as pressing "Mod Manager Download".
It asks Nexus for the NXM/Vortex link.
*/
const response = await fetch(`${mod.file.url}&nmm=1`, {
method: "GET",
credentials: "include",
headers: {
"X-Requested-With": "XMLHttpRequest",
},
});

text = await response.text();

if (!response.ok) {
return { downloadUrl: "", text };
}

const patterns = [
/const downloadUrl = '([^']+)'/,
/const downloadUrl = "([^"]+)"/,
/id="slowDownloadButton".*?data-download-url="([^"]+)"/s,
/data-download-url="([^"]+)"/,
/[\w-]*download-url="([^"]+)"/,
/download-url="([^"]+)"/,
/(nxm:\/\/[^\s"'<]+)/,
/"url"\s*:\s*"([^"]+)"/,
];

for (const pattern of patterns) {
const match = text.match(pattern);
if (match) {
downloadUrl = decodeDownloadUrl(match[1]);
break;
}
}

/*
If Nexus changed the page HTML and the NXM link was not found,
fall back to the download generator using nmm=1.
*/
if (!downloadUrl) {
downloadUrl = await callGenerateApi(true);
}

/*
Final fallback.
Sometimes Nexus returns the same usable URL without nmm=1.
*/
if (!downloadUrl) {
downloadUrl = await callGenerateApi(false);
}

console.log("[NDC] Vortex download URL:", downloadUrl);
} else {
/*
Browser/manual download mode.
*/
const response = await fetch(mod.file.url, {
method: "GET",
credentials: "include",
});

text = await response.text();

if (!response.ok) {
return { downloadUrl: "", text };
}

downloadUrl = await callGenerateApi(false);

console.log("[NDC] Browser download URL:", downloadUrl);
}

return { downloadUrl, text };
}

```

find 'async fetchDownloadLink(mod) {' and replace up until 'bypassNexusAdsCookie() {'. I fixed it for me with these changes, maybe you might too.

§
Posted: 2026-05-05

ughhhhh i don't know if its a problem on my end now or what, but every fix people put that say it works for them just results in errors for me! the one you put has the same problem as the original poster for me T_T

§
Posted: 2026-05-06

ughhhhh i don't know if its a problem on my end now or what, but every fix people put that say it works for them just results in errors for me! the one you put has the same problem as the original poster for me T_T

Same here i have the same error

§
Posted: 2026-05-06

**UPDATE — additional fix for people getting a 403 error in Vortex**

If you applied the fix from my previous comment but are still getting a 403 error inside Vortex, there is one more small change needed.

In the `fetchDownloadLink` method you already replaced, find the `patterns` array and remove the `nxm://` line so it looks exactly like this:

```javascript
const patterns = [
/const downloadUrl = '([^']+)'/,
/id="slowDownloadButton".*?data-download-url="([^"]+)"/,
/data-download-url="([^"]+)"/,
/]+download-url="([^"]+)"/,
/download-url="([^"]+)"/,
/"url"\s*:\s*"([^"]+)"/,
];
```

Why: NXM links found raw in the HTML don't include an auth key, so Vortex tries to resolve them via the Nexus API and gets a 403. `GenerateDownloadUrl` (which runs after the patterns fail) returns an NXM link *with* a proper key that Vortex can use directly — so we just make sure the script always gets there.

---

If you don't want to deal with the code at all, I've put together a fully patched version of the script here:

🔗 **https://drive.google.com/file/d/1e3D496uLyv9eRgzZYzCI8FRNnMPhBI4j/view?usp=sharing**

Open Tampermonkey → click the script → Ctrl+A → Delete → Ctrl+V → Save. Done.

drigtimeAuthor
§
Posted: 2026-05-06
Edited: 2026-05-06

Thanks @Jed1_D0ge_Dar1t0s for the time you spent fixing the issues! If you don't mind, could I use your updated code to publish an official update? I will, of course, credit you in the changelog.

I'm not as invested in the maintenance of the script as I used to be, so I'm really glad to see people still enjoying and updating it. :)

§
Posted: 2026-05-06

Thanks @Jed1_D0ge_Dar1t0s for the time you spent fixing the issues! If you don't mind, could I use your updated code to publish an official update? I will, of course, credit you in the changelog.

I'm not as invested in the maintenance of the script as I used to be, so I'm really glad to see people still enjoying and updating it. :)

Of course! Thanks for creating this and saving this community so much money!

§
Posted: 2026-05-08

Does not work it pops up a window asking to open microsoft store for xml or something like that. i did the previous patches like Jed1_D0ge Dar1t0s
showed but it doesnt work.

Post reply

Sign in to post a reply.