Skip to content

Instantly share code, notes, and snippets.

@thealphadollar
Last active September 16, 2025 08:47
Show Gist options
  • Save thealphadollar/7c0ee76664cbd28aecc1bd235f0202fd to your computer and use it in GitHub Desktop.
Save thealphadollar/7c0ee76664cbd28aecc1bd235f0202fd to your computer and use it in GitHub Desktop.
JS script to send connection requests to your LinkedIn search results with customisation options, accept all received connection requests, and withdraw pending sent connection requests.
// If the script does not work, you may need to allow same site scripting https://stackoverflow.com/a/50902950
Linkedin = {
config: {
scrollDelay: 3000,
actionDelay: 5000,
nextPageDelay: 5000,
// set to -1 for no limit
maxRequests: -1,
totalRequestsSent: 0,
// set to false to skip adding note in invites
addNote: true,
note: "Hey {{name}}, I'm looking forward to connecting with you!",
},
init: function (data, config) {
console.info("INFO: script initialized on the page...");
console.debug(
"DEBUG: scrolling to bottom in " + config.scrollDelay + " ms"
);
setTimeout(() => this.scrollBottom(data, config), config.actionDelay);
},
scrollBottom: function (data, config) {
window.scrollTo({ top: document.body.scrollHeight, behavior: "smooth" });
console.debug("DEBUG: scrolling to top in " + config.scrollDelay + " ms");
setTimeout(() => this.scrollTop(data, config), config.scrollDelay);
},
scrollTop: function (data, config) {
window.scrollTo({ top: 0, behavior: "smooth" });
console.debug(
"DEBUG: inspecting elements in " + config.scrollDelay + " ms"
);
setTimeout(() => this.inspect(data, config), config.scrollDelay);
},
inspect: function (data, config) {
var totalRows = this.totalRows();
console.debug("DEBUG: total search results found on page are " + totalRows);
if (totalRows >= 0) {
this.compile(data, config);
} else {
console.warn("WARN: end of search results!");
this.complete(config);
}
},
compile: function (data, config) {
var elements = document.querySelectorAll("button");
data.pageButtons = [...elements].filter(function (element) {
return element.textContent.trim() === "Connect";
});
if (!data.pageButtons || data.pageButtons.length === 0) {
console.warn("ERROR: no connect buttons found on page!");
console.info("INFO: moving to next page...");
setTimeout(() => {
this.nextPage(config);
}, config.nextPageDelay);
} else {
data.pageButtonTotal = data.pageButtons.length;
console.info("INFO: " + data.pageButtonTotal + " connect buttons found");
data.pageButtonIndex = 0;
var names = document.getElementsByClassName("entity-result__title-text");
names = [...names].filter(function (element) {
return element.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.textContent.includes(
"Connect\n"
);
});
data.connectNames = [...names].map(function (element) {
return element.innerText.split(" ")[0];
});
console.debug(
"DEBUG: starting to send invites in " + config.actionDelay + " ms"
);
setTimeout(() => {
this.sendInvites(data, config);
}, config.actionDelay);
}
},
sendInvites: function (data, config) {
console.debug("remaining requests " + config.maxRequests);
if (config.maxRequests == 0) {
console.info("INFO: max requests reached for the script run!");
this.complete(config);
} else {
console.debug(
"DEBUG: sending invite to " +
(data.pageButtonIndex + 1) +
" out of " +
data.pageButtonTotal
);
var button = data.pageButtons[data.pageButtonIndex];
button.click();
if (config.addNote && config.note) {
console.debug(
"DEBUG: clicking Add a note in popup, if present, in " +
config.actionDelay +
" ms"
);
setTimeout(() => this.clickAddNote(data, config), config.actionDelay);
} else {
console.debug(
"DEBUG: clicking done in popup, if present, in " +
config.actionDelay +
" ms"
);
setTimeout(() => this.clickDone(data, config), config.actionDelay);
}
}
},
clickAddNote: function (data, config) {
var buttons = document.querySelectorAll("button");
var addNoteButton = Array.prototype.filter.call(buttons, function (el) {
return el.textContent.trim() === "Add a note";
});
// adding note if required
if (addNoteButton && addNoteButton[0]) {
console.debug("DEBUG: clicking add a note button to paste note");
addNoteButton[0].click();
console.debug("DEBUG: pasting note in " + config.actionDelay);
setTimeout(() => this.pasteNote(data, config), config.actionDelay);
} else {
console.debug(
"DEBUG: add note button not found, clicking send on the popup in " +
config.actionDelay
);
setTimeout(() => this.clickDone(data, config), config.actionDelay);
}
},
pasteNote: function (data, config) {
noteTextBox = document.getElementById("custom-message");
noteTextBox.value = config.note.replace(
"{{name}}",
data.connectNames[data.pageButtonIndex]
);
noteTextBox.dispatchEvent(
new Event("input", {
bubbles: true,
})
);
console.debug(
"DEBUG: clicking send in popup, if present, in " +
config.actionDelay +
" ms"
);
setTimeout(() => this.clickDone(data, config), config.actionDelay);
},
clickDone: function (data, config) {
var buttons = document.querySelectorAll("button");
var doneButton = Array.prototype.filter.call(buttons, function (el) {
return el.textContent.trim() === "Send";
});
// Click the first send button
if (doneButton && doneButton[0]) {
console.debug("DEBUG: clicking send button to close popup");
doneButton[0].click();
} else {
console.debug(
"DEBUG: send button not found, clicking close on the popup in " +
config.actionDelay
);
}
setTimeout(() => this.clickClose(data, config), config.actionDelay);
},
clickClose: function (data, config) {
var closeButton = document.getElementsByClassName(
"artdeco-modal__dismiss artdeco-button artdeco-button--circle artdeco-button--muted artdeco-button--2 artdeco-button--tertiary ember-view"
);
if (closeButton && closeButton[0]) {
closeButton[0].click();
}
console.info(
"INFO: invite sent to " +
(data.pageButtonIndex + 1) +
" out of " +
data.pageButtonTotal
);
config.maxRequests--;
config.totalRequestsSent++;
if (data.pageButtonIndex === data.pageButtonTotal - 1) {
console.debug(
"DEBUG: all connections for the page done, going to next page in " +
config.actionDelay +
" ms"
);
setTimeout(() => this.nextPage(config), config.actionDelay);
} else {
data.pageButtonIndex++;
console.debug(
"DEBUG: sending next invite in " + config.actionDelay + " ms"
);
setTimeout(() => this.sendInvites(data, config), config.actionDelay);
}
},
nextPage: function (config) {
var pagerButton = document.getElementsByClassName(
"artdeco-pagination__button--next"
);
if (
!pagerButton ||
pagerButton.length === 0 ||
pagerButton[0].hasAttribute("disabled")
) {
console.info("INFO: no next page button found!");
return this.complete(config);
}
console.info("INFO: Going to next page...");
pagerButton[0].click();
setTimeout(() => this.init({}, config), config.nextPageDelay);
},
complete: function (config) {
console.info(
"INFO: script completed after sending " +
config.totalRequestsSent +
" connection requests"
);
},
totalRows: function () {
var search_results = document.getElementsByClassName("search-result");
if (search_results && search_results.length != 0) {
return search_results.length;
} else {
return 0;
}
},
};
Linkedin.init({}, Linkedin.config);
@DbDibyendu
Copy link

ClickDone "Send without a Note" Logic wasn't working for me, I resolved it using below code !
Hope it helps !

Just set addNote: false, in the configs and modify the clickDone function in the script

async clickDone(data, config) {
  await new Promise((resolve) => setTimeout(resolve, 800));

  const sendWithoutBtn = Array.from(
    document.querySelectorAll(
      "button, input[type='button'], input[type='submit']",
    ),
  ).find(
    (btn) =>
      (btn.innerText && btn.innerText.includes("Send without")) ||
      (btn.value && btn.value.includes("Send without")),
  );

  if (sendWithoutBtn) {
    console.debug("DEBUG: clicking 'Send without' button to close popup");
    sendWithoutBtn.click();
  } else {
    console.debug(
      "DEBUG: 'Send without' button not found, clicking close on the popup in " +
        config.actionDelay,
    );
  }

  setTimeout(() => this.clickClose(data, config), config.actionDelay);
},

@valerioviale
Copy link

Hello, the tag to refer to next page has been changed, in nextPage you can use: let nextButton = document.querySelector('button[aria-label="Next page"][data-testid="pagination-control-next-btn"]:not([disabled])');

@valerioviale
Copy link

last working script I use to add connections (no message is sent) is:

Linkedin = {
config: {
scrollDelay: 3000,
actionDelay: 4000,
nextPageDelay: 9000,
maxRequests: -1,
totalRequestsSent: 0,
addNote: false,
note: "Hello"
},
init: function () {
console.info("INFO: script initialized on the page...");
setTimeout(() => this.scrollBottom(), this.config.scrollDelay);
},
scrollBottom: function () {
window.scrollTo({ top: document.body.scrollHeight, behavior: 'smooth' });
setTimeout(() => this.scrollTop(), this.config.scrollDelay);
},
scrollTop: function () {
window.scrollTo({ top: 0, behavior: 'smooth' });
setTimeout(() => this.findConnectButtons(), this.config.scrollDelay);
},
findConnectButtons: function () {
let buttons = document.querySelectorAll("button");
let connectButtons = [...buttons].filter(btn => btn.textContent.trim() === "Connect");

    if (connectButtons.length === 0) {
        console.warn("No 'Connect' buttons found. Moving to next page...");
        setTimeout(() => this.nextPage(), this.config.nextPageDelay);
    } else {
        console.info(`Found ${connectButtons.length} 'Connect' buttons`);
        this.sendInvites(connectButtons, 0);
    }
},
sendInvites: function (buttons, index) {
    if (index >= buttons.length || this.config.maxRequests === 0) {
        console.info("Finished sending invites on this page.");
        setTimeout(() => this.nextPage(), this.config.nextPageDelay);
        return;
    }

    buttons[index].click();
    console.debug("Clicked 'Connect' button");

    setTimeout(() => this.clickSendWithoutNote(buttons, index), this.config.actionDelay);
},
clickSendWithoutNote: function (buttons, index) {
    let sendWithoutNoteButton = document.querySelector("button[aria-label='Send without a note']");
    if (sendWithoutNoteButton) {
        sendWithoutNoteButton.click();
        console.info("Clicked 'Send without a note'");
    } else {
        console.warn("'Send without a note' button not found, closing popup.");
        let closeButton = document.querySelector(".artdeco-modal__dismiss");
        if (closeButton) closeButton.click();
    }
    
    this.config.totalRequestsSent++;
    this.config.maxRequests--;
    setTimeout(() => this.sendInvites(buttons, index + 1), this.config.actionDelay);
},
nextPage: function () {
let nextButton = document.querySelector('button[aria-label="Next page"][data-testid="pagination-control-next-btn"]:not([disabled])');
    if (nextButton) {
        nextButton.click();
        console.info("Navigating to next page...");
        setTimeout(() => this.init(), this.config.nextPageDelay);
    } else {
        console.info("No more pages to process.");
    }
}

};

Linkedin.init();

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