If you’ve ever worked on Amazon Mechanical Turk (MTurk), you know how time-sensitive high-paying HITs can be. Blink, and they’re gone. Manually scanning for HITs that meet your pay criteria can be tedious — and sometimes impossible when competing with other workers.
That’s why I built a Tampermonkey userscript to automatically accept MTurk HITs based on two conditions:
- Reward threshold (minimum pay I’m willing to work for)
- Set ID (to target specific requesters or tasks)
This post walks you through how I implemented it, so you can understand the logic, customize it for your own needs, and maybe even improve it.
Why Tampermonkey?
Tampermonkey is a browser extension that allows you to run custom JavaScript on specified web pages. For MTurk, this means you can inject your own automation into the interface without modifying any server-side code.
- Easy to install
- Works across browsers
- Flexible and scriptable
The Problem
Manually hunting for high-paying HITs wastes time and causes missed opportunities. I wanted:
Automatic filtering so only HITs above a certain reward appear in my workflow.
Direct acceptance of those HITs without extra clicks.
My Approach
I broke the problem into three steps:
Fetch & parse HIT data from the MTurk search results page.
Apply filters based on reward and set ID.
Trigger acceptance via MTurk’s accept URL when criteria are met.
Implementation
Install Tampermonkey
Chrome:
https://tampermonkey.net/
After installation, click the Tampermonkey icon → "Create a new script."
The Script
Script Metadata
Tampermonkey scripts start with a header that describes the script, permissions, and the pages it should run on.
// ==UserScript==
// @name MTurk Auto-HIT Catcher
// @namespace http://tampermonkey.net/
// @version 0.1
// @description Automatically accepts MTurk HITs based on reward & HIT Set ID
// @author YourName
// @match https://worker.mturk.com/match
// @grant GM.getValue
// @grant GM.setValue
// ==/UserScript==
Setup & Global Variables
We use an async function so we can load stored data (like the block list) before starting.
(async function() {
'use strict';
var blockList = await loadBlockList();
var baseUrl = 'https://worker.mturk.com';
var tasks = {};
var isTimerRunning = false;
var timer;
let isSearching = false;
let acceptedCount = 0;
const maxAcceptedHits = 10;
let searchHitId = null;
Adding the Control Panel to MTurk
We create an HTML template for our settings and inject it into MTurk’s DOM.
function appendHTMLElements() {
var rewardTemplate = `
<div class="row">
<div class="col-sm-3 form-inline">
<h3><strong>Parameters</strong></h3>
<label for="filters-min-reward">
<span class="m-r-xs">Pays at least $</span>
<input class="form-control reward-filter" id="filters-min-reward" value="1" type="number" step="0.01">
</label>
</div>
<!-- Additional UI controls omitted for brevity -->
</div>
`;
var el = document.querySelector('#MainContent');
if (el) {
el.innerHTML = rewardTemplate;
}
// Attach button handlers
document.getElementById("timer").onclick = toggleTimer;
document.getElementById("block").onclick = blockRequestor;
document.getElementById("search-toggle").onclick = toggleSearchByHitId;
document.getElementById("close-captcha").onclick = () => {
document.getElementById("captcha-modal").style.display = "none";
};
updateBlockEl();
}
Managing Blocked Requesters
We can save unwanted requesters so they’re ignored in future searches.
async function blockRequestor() {
var block = getBlockText();
if (blockList.indexOf(block) < 0) {
blockList.push(block);
await saveBlockList();
}
}
function updateBlockEl() {
var listEl = document.getElementById('block-list');
listEl.innerHTML = '';
for (var id of blockList) {
var blockEl = document.createElement('span');
blockEl.id = id;
blockEl.innerHTML = `<span><a href="#"><i class="fa fa-minus-circle"></i>${id}</a></span>`;
blockEl.onclick = removeBlockList;
listEl.appendChild(blockEl);
}
}
async function removeBlockList() {
blockList.splice(blockList.indexOf(this.id), 1);
await saveBlockList();
}
async function loadBlockList() {
return JSON.parse(await GM.getValue("autoHitBL", '[]'));
}
async function saveBlockList() {
await GM.setValue("autoHitBL", JSON.stringify(blockList));
updateBlockEl();
}
Searching & Auto-Accepting by HIT Set ID
This is the core automation — repeatedly trying to accept a HIT until it succeeds or reaches the limit.
function toggleSearchByHitId() {
const btn = document.getElementById("search-toggle");
if (!isSearching) {
const hitId = document.getElementById("search-hit-id").value.trim();
if (!hitId) {
alert("Please enter a HIT Set ID.");
return;
}
searchHitId = hitId;
acceptedCount = 0;
const acceptUrl = `/projects/${hitId}/tasks/accept_random?ref=w_pl_prvw`;
const task = { hit_set_id: hitId, accept_project_task_url: acceptUrl };
isSearching = true;
btn.textContent = "Stop";
btn.classList.replace("btn-primary", "btn-danger");
acceptMultipleHits(task);
} else {
isSearching = false;
searchHitId = null;
btn.textContent = "Start";
btn.classList.replace("btn-danger", "btn-primary");
}
}
function acceptMultipleHits(task) {
async function attempt() {
if (!isSearching || acceptedCount >= maxAcceptedHits) return;
try {
const res = await fetch(baseUrl + task.accept_project_task_url, { credentials: 'include' });
const html = await res.text();
if (html.toLowerCase().includes("captcha")) {
isSearching = false;
document.getElementById("captcha-modal").style.display = "flex";
return;
}
// Extract HIT details and add to accepted list
addAcceptedHitToList(task);
acceptedCount++;
} catch (err) {
console.error("Error accepting HIT:", err);
}
if (isSearching) setTimeout(attempt, 750);
}
attempt();
}
Displaying Accepted HITs
We show a live-updating list of accepted tasks.
function addAcceptedHitToList(task) {
const listEl = document.getElementById("accepted-hits-list");
const listItem = document.createElement("li");
const fullUrl = task.work_url.startsWith('http') ? task.work_url : `${baseUrl}${task.work_url}`;
listItem.innerHTML = `
<div>
<strong>${task.requester_name}</strong> —
<a href="${task.accept_project_task_url}" target="_blank">${task.title}</a>
— <span style="color: green;">$${task.monetary_reward.amount_in_dollars}</span>
— <a href="${fullUrl}" target="_blank">[Work Link]</a>
</div>
`;
listEl.insertBefore(listItem, listEl.firstChild);
}
Fetching & Processing New HITs
The script can also scan for new tasks automatically.
function getNewTasks() {
if (isSearching) return;
getContent('/?page_size=20&filters[qualified]=true&filters[masters]=false&sort=updated_desc&filters[min_reward]=' + getMinReward(),
function(err, doc) {
var currTasks = parseTasks(doc);
for (let task of currTasks) {
if (task.assignable_hits_count > 0 && blockList.indexOf(task.requester_id) < 0) {
processTask(task);
break;
}
}
});
}
async function processTask(task) {
try {
const res = await fetch(baseUrl + task.accept_project_task_url, { credentials: 'include' });
task.work_url = res.url;
task.isAccepted = true;
updateTaskEl(task);
addAcceptedHitToList(task);
} catch (err) {
console.error("Error processing task:", err);
}
}
Utility Functions
Helpers for fetching and parsing HITs.
function getContent(url, callback) {
var httpRequest = new XMLHttpRequest();
httpRequest.open('GET', url);
httpRequest.send();
httpRequest.onreadystatechange = function() {
if (httpRequest.readyState === XMLHttpRequest.DONE) {
if (httpRequest.status === 200 || httpRequest.status === 403) {
var doc = new DOMParser().parseFromString(httpRequest.responseText, "text/html");
callback(null, doc);
}
}
}
}
function parseTasks(doc) {
let el = doc.querySelector('div[data-react-class*="HitSetTable"]');
if (el) {
return JSON.parse(el.getAttribute("data-react-props").replace('"', '"')).bodyData;
}
return [];
}
Disclaimer
This script is for educational purposes only. Automating HIT acceptance may violate MTurk’s Terms of Service. Always check the rules and use responsibly.
Conclusion
With Tampermonkey, we can build a custom automation layer on MTurk that saves time, filters tasks, and streamlines work. You can extend this script with:
More filters (qualifications, keywords)
Notifications when HITs are accepted
A UI for setting parameters without editing code
Top comments (2)
Not working brother in tampermonkey
Does your script have the right @match URLs for the version of MTurk you’re on (e.g. worker.mturk.com)? and If you open DevTools → Console, do you see any errors when you load a HIT?