Skip to content

Instantly share code, notes, and snippets.

@vampirepapi
Created March 16, 2026 14:43
Show Gist options
  • Select an option

  • Save vampirepapi/d465bf7f5c9e1fde6605d27e469b51b0 to your computer and use it in GitHub Desktop.

Select an option

Save vampirepapi/d465bf7f5c9e1fde6605d27e469b51b0 to your computer and use it in GitHub Desktop.
Instahyre Auto-Apply Script - Automatically applies to all matching jobs on Instahyre with bulk-apply popup handling, pagination, and a floating status panel.
// ============================================
// INSTAHYRE AUTO-APPLY SCRIPT
// ============================================
// Paste this in browser console (F12 β†’ Console)
// on https://www.instahyre.com/candidate/opportunities/
//
// Features:
// - Opens each job listing and clicks "Apply"
// - Handles bulk-apply popups (selects all similar jobs)
// - Auto-advances through the job detail modal
// - Navigates to next pages automatically
// - Recovers from stuck modals, empty popups, loading states
// - Floating status panel with Stop/Resume controls
//
// Console commands while running:
// window._autoApply.stop() β†’ Pause
// window._autoApply.resume() β†’ Resume
// window._autoApply.count() β†’ Check total applied
(function () {
let count = 0,
running = true;
const MAX = 500;
let emptyTicks = 0;
// --- Floating Status Panel ---
const panel = document.createElement("div");
panel.id = "auto-apply-panel";
panel.style.cssText =
"position:fixed;top:10px;right:10px;z-index:99999;background:#1a1a2e;color:#0f8;padding:15px 20px;border-radius:10px;font-family:monospace;font-size:14px;box-shadow:0 4px 20px rgba(0,0,0,.5);min-width:320px;border:1px solid #0f8;max-width:400px";
panel.innerHTML = `
<div style="font-size:16px;font-weight:bold;margin-bottom:8px">πŸš€ Auto-Apply</div>
<div>Applied: <span id="aa-c" style="color:#fff;font-size:22px">0</span></div>
<div>Status: <span id="aa-s" style="color:gold">Starting...</span></div>
<div id="aa-log" style="margin-top:8px;font-size:11px;color:#999;max-height:160px;overflow-y:auto"></div>
<div style="margin-top:10px">
<button id="aa-stop" style="background:#f44;color:#fff;border:none;padding:8px 20px;border-radius:5px;cursor:pointer;font-size:13px">⏹ Stop</button>
<button id="aa-go" style="background:#0a5;color:#fff;border:none;padding:8px 20px;border-radius:5px;cursor:pointer;font-size:13px;margin-left:8px;display:none">β–Ά Resume</button>
</div>`;
document.body.appendChild(panel);
const cEl = document.getElementById("aa-c"),
sEl = document.getElementById("aa-s"),
logEl = document.getElementById("aa-log");
// --- Stop / Resume Buttons ---
document.getElementById("aa-stop").onclick = () => {
running = false;
sEl.textContent = "Stopped";
sEl.style.color = "#f44";
document.getElementById("aa-stop").style.display = "none";
document.getElementById("aa-go").style.display = "inline-block";
};
document.getElementById("aa-go").onclick = () => {
running = true;
sEl.textContent = "Resuming...";
sEl.style.color = "gold";
document.getElementById("aa-stop").style.display = "inline-block";
document.getElementById("aa-go").style.display = "none";
emptyTicks = 0;
mainLoop();
};
// --- Helpers ---
function log(m) {
const d = document.createElement("div");
d.textContent = new Date().toLocaleTimeString().substring(0, 8) + " " + m;
logEl.prepend(d);
if (logEl.children.length > 60) logEl.lastChild.remove();
}
function status(m, c) {
sEl.textContent = m;
if (c) sEl.style.color = c;
}
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
// Wait for a condition to become true (with timeout)
async function waitFor(fn, timeout = 5000) {
const start = Date.now();
while (Date.now() - start < timeout) {
const result = fn();
if (result) return result;
await sleep(400);
}
return null;
}
// --- Page State Detectors ---
// Find the "Apply" button inside the job detail modal (not the bulk popup)
function findApplyBtn() {
for (const b of document.querySelectorAll("button")) {
if (
b.textContent.trim() === "Apply" &&
b.offsetParent !== null &&
getComputedStyle(b).display !== "none" &&
!b.closest(".candidate-apply-all-modal")
) {
return b;
}
}
return null;
}
// Check if the bulk-apply popup is visible
function isBulkVisible() {
const m = document.querySelector(".candidate-apply-all-modal");
return m && !m.classList.contains("ng-hide");
}
// Check if the job detail modal is open
function isModalOpen() {
const bg = document.querySelector(
".candidate-apply-modal .application-modal-backdrop"
);
return bg && getComputedStyle(bg).display !== "none";
}
// Check if the page is in "Fetching" loading state
function isPageFetching() {
for (const h of document.querySelectorAll("h1, h2, h3, h4")) {
if (h.textContent.trim().includes("Fetching") && h.offsetParent !== null)
return true;
}
return false;
}
// Get the current job title from the detail modal
function getJobTitle() {
for (const s of [
".candidate-apply-modal h3",
".candidate-apply-modal h4",
".candidate-apply-modal .heading",
]) {
const el = document.querySelector(s);
if (el && el.offsetParent !== null) {
const txt = el.textContent.trim();
if (txt && txt !== "Hold on, loading..." && txt.length > 2)
return txt.substring(0, 45);
}
}
return "(loading...)";
}
// --- Bulk Apply Popup Handler ---
async function handleBulkPopup() {
if (!isBulkVisible()) return;
const m = document.querySelector(".candidate-apply-all-modal");
const cbs = m.querySelectorAll('input[type="checkbox"]');
if (cbs.length > 0) {
// Select all checkboxes and click Apply
cbs.forEach((cb) => {
if (!cb.checked) cb.click();
});
await sleep(400);
const btn = m.querySelector('button[ng-click="applyBulk()"]');
if (btn && !btn.disabled) {
btn.click();
log("πŸ“‹ Bulk applied (" + cbs.length + " extra)");
await sleep(2000);
} else {
m.querySelector('button[ng-click="applyBulkCancel()"]')?.click();
log("πŸ“‹ Bulk: Apply disabled, cancelled");
await sleep(1000);
}
} else {
// Empty bulk popup β€” just cancel
m.querySelector('button[ng-click="applyBulkCancel()"]')?.click();
log("πŸ“‹ Bulk: no extras, cancelled");
await sleep(1000);
}
// Safety: if still showing, force cancel
if (isBulkVisible()) {
document
.querySelector(
'.candidate-apply-all-modal button[ng-click="applyBulkCancel()"]'
)
?.click();
await sleep(1000);
}
}
// --- Main Loop ---
async function mainLoop() {
while (running && count < MAX) {
if (!running) return;
// STEP 1: Handle bulk popup if visible
if (isBulkVisible()) {
await handleBulkPopup();
emptyTicks = 0;
continue;
}
// STEP 2: If Apply button is visible in job detail, click it
let applyBtn = findApplyBtn();
if (applyBtn) {
const title = getJobTitle();
applyBtn.click();
count++;
cEl.textContent = count;
log("βœ… #" + count + ": " + title);
status("Applied #" + count + ": " + title, "#0f8");
emptyTicks = 0;
await sleep(2000); // wait for bulk popup or auto-advance
await handleBulkPopup();
await sleep(2000); // wait for next job to load
continue;
}
// STEP 3: Modal is open but no Apply button (already applied / loading)
if (isModalOpen()) {
status("Waiting for Apply button...", "gold");
applyBtn = await waitFor(findApplyBtn, 4000);
if (applyBtn) continue; // found it β†’ loop will click it
emptyTicks++;
if (emptyTicks > 3) {
log("⚠️ Closing stuck modal");
document
.querySelectorAll(".application-modal-close")
.forEach((c) => c.click());
emptyTicks = 0;
await sleep(1500);
}
await sleep(1000);
continue;
}
// STEP 4: We're on the job list β€” click first "View Β»" button
emptyTicks = 0;
if (isPageFetching()) {
status("Page loading...", "gold");
await sleep(3000);
continue;
}
const viewBtns = Array.from(
document.querySelectorAll("button.button-interested")
).filter(
(b) => b.offsetParent !== null && b.textContent.trim() === "View Β»"
);
if (viewBtns.length > 0) {
const row = viewBtns[0].closest("[ng-repeat], li, div");
const name =
row
?.querySelector("a")
?.textContent?.trim()
?.substring(0, 45) || "?";
viewBtns[0].click();
status("Opening: " + name, "gold");
log("πŸ“‚ Opening: " + name);
await waitFor(findApplyBtn, 5000);
continue;
}
// STEP 5: No View buttons β€” try next page
let foundNext = false;
for (const a of document.querySelectorAll(".pagination a, a")) {
if (
a.textContent.trim().includes("Next") &&
a.offsetParent !== null
) {
a.click();
log("πŸ“„ Next page");
status("Next page...", "gold");
await sleep(4000);
foundNext = true;
break;
}
}
if (foundNext) continue;
// STEP 6: Nothing found β€” retry a few times before finishing
emptyTicks++;
if (emptyTicks > 5) {
status("πŸŽ‰ All done! " + count + " jobs applied", "#0f8");
log("πŸŽ‰ Finished! Total: " + count);
return;
}
status("Waiting for jobs... (" + emptyTicks + "/5)", "gold");
await sleep(3000);
}
if (count >= MAX)
status("πŸŽ‰ Max reached! " + count + " applied", "#0f8");
}
// --- Start ---
status("Starting in 2s...", "gold");
setTimeout(() => mainLoop(), 2000);
// --- Console API ---
window._autoApply = {
stop: () => {
running = false;
status("Stopped", "#f44");
},
resume: () => {
running = true;
emptyTicks = 0;
status("Resuming", "gold");
mainLoop();
},
count: () => count,
};
console.log("=== INSTAHYRE AUTO-APPLY RUNNING ===");
console.log("Stop: window._autoApply.stop()");
console.log("Resume: window._autoApply.resume()");
console.log("Count: window._autoApply.count()");
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment