Authentication and scopesSending your key, and what each scope allows›
Send your key in the Authorization header as Bearer mwk_…, or as X-API-Key. Nothing else signs you in to the API — not a website session, not a cookie.
readAlways on. Your account, balance, tasks, submissions, proof files and deposits.
tasksCreate and edit drafts, add pictures, pause, resume and cancel tasks, and change a live task’s words, pace and notes. Cannot spend.
fundMove your balance into a task’s escrow, which publishes it, and change a live task’s places and pay.
reviewReject submissions only. Approval and payment happen automatically.
conversionsConfirm sign-ups from your server for automatic payment on conversion tasks.
Keys are for servers. Never put one in a web page, a browser extension or a mobile app — anybody who opens it can read it. If a key leaks, revoke it above; it stops working at the next request.
The ids you can send as category_id when you create a task. min_plan is the plan a task in that category needs before it can be funded (null for any account).
What a task should pay per worker — the same guide as the Post a task page. You still set the price, from the minimum up.
POST/pricing/guideread
Recommend a reward per worker
Reads what the posting asks for, how many steps and proofs it has, any duration it states, where workers must be, the devices they must be on (target_devices: iPhones, Macs or computers only pay more), and whether it is only for your own workers (audience: "my_workers"). Screenshot and file uploads add estimated capture/upload handling, including the first proof and on duration-based recommendations. Text, URLs and copied codes have no media handling premium. Every factor comes back in reasons.
Example
curl -X POST https://microworker.app/api/v1/pricing/guide \
-H "Authorization: Bearer $MW_KEY" \
-H "Content-Type: application/json" \
-d '{"title":"Sign up and verify your email","instructions":"1. Open the link\n2. Create a free account\n3. Click the link we email you","target_countries":["US"],"target_devices":["ios"]}'
A task starts as a draft: nothing is charged and nobody sees it. Funding it moves the money into escrow and publishes it.
POST/taskstasks
Create a draft
reward_minor is per approved worker, in cents; slots_total is how many workers. Proofs: screenshot, text, url, file, or one code — a completion code you show at the end of your own flow, which pays on the spot when a worker types it back. A screenshot proof can carry must_show: text we read off every screenshot. Every proof is reviewed automatically within seconds (the checks, platform verification and an AI reading of the proof against your steps): paid, sent back to the worker for a fix, not accepted, or held for you to decide; with the automatic review off, proof is paid as soon as the checks are clear and anything they question within 24 h unless you reject it. auto_approve_hours is that window (0 by default, never more than 24 — a larger value is stored as 24); target_countries limits who can take it, and target_devices (android, ios, desktop, or windows / mac for a computer running that system) the device they take it on. max_per_country spreads the workers out: at most that many from any one country, counted by the country each worker connects from when they start (null = no limit; a value at or above slots_total limits nothing and is stored as null). audience: "my_workers" shows the task only to the workers you saved from your own tasks’ review page — funding answers 409 team_empty until you have saved one; hiring: true tells workers you are choosing people from this task for more work. project_id puts the task in one of your projects, and audience: "project" gives that project’s team the first go — backup_hours (6, 24 or 72; 0 = never, default 24) after funding, the spots they have not taken open to everyone. For sign-ups your own site can confirm, see Conversion confirmations.
Example
curl -X POST https://microworker.app/api/v1/tasks \
-H "Authorization: Bearer $MW_KEY" \
-H "Content-Type: application/json" \
-d '{"title":"Join our Telegram group","instructions":"1. Open t.me/yourgroup\n2. Join the group\n3. Screenshot the group with your name visible","proofs":[{"type":"screenshot","note":"The group open, with your name visible","must_show":"Your Group Name"}],"reward_minor":5,"slots_total":100}'
Response
{ "id": 311, "budget_minor": 500, "fee_minor": 125, "total_minor": 625, "target_countries": [], "target_devices": [], "audience": "everyone", "hiring": false, "proofs": [ { "type": "screenshot", "note": "The group open, with your name visible" } ], "media": [] }
GET/tasksread
Your tasks, newest first
Filter with ?status=draft|open|paused|completed|cancelled|expired; page with ?page= and ?pageSize= (up to 100). held_count is pending work the automatic checks held for you.
Field edits work while it is a draft; a live task’s words, pace, notes, places and pay have calls of their own (Managing a live job). On a live task send only an action: pause, unpause (resume — its links are checked again first) or cancel. Cancelling refunds the unspent escrow to your balance once no proof is waiting for its review. max_per_country: null removes the per-country limit.
Moves reward × workers plus the fee from your available balance into escrow. The posting is screened first: a refusal is a 422 content_policy_violation and nothing is charged. Gambling, software downloads, categories with a min_plan and private tasks (audience "project" or "my_workers") need a live plan: without one the answer is a 403 verified_required (category says which; private tasks answer category "private_jobs") and the task stays a draft. A first task may publish with under_review: true.
Example
curl -X POST https://microworker.app/api/v1/tasks/311/fund \
-H "Authorization: Bearer $MW_KEY"
Once a task is funded it is live: workers see it and start it. These are the controls of the website’s job page — change its words, slow it down, talk to the people working on it, change its places and pay — and, with PATCH /tasks/{id} above, pause, resume and cancel it.
PATCH /tasks/{id}Pause, resume or cancel: action "pause", "unpause" or "cancel". A paused task leaves the board; workers who already started can still send their proof.
POST /tasks/{id}/editChange the title, the steps and each proof requirement’s note. The kinds of proof stay as they are.
POST /tasks/{id}/paceSlow down: at most so many new workers an hour or a day.
POST /tasks/{id}/notesTell the people working on it something: shown on the task page and sent to them.
POST /tasks/{id}/resizeFewer or more places, higher pay. Only the difference moves; needs the Fund scope.
Try first: send "dry_run": true to edit or resize. Every check runs and nothing changes, and the answer says what would happen — how many people would be told, or the money that would come back or be taken.
A worker who started before an edit is judged against the version they started with, and a note posted after a worker started never counts against them on its own.
POST/tasks/{id}/edittasks
Change a live task’s words
An open or paused task. Send only what changes: title, instructions, proof_notes (one entry per proof requirement, in order — the kinds of proof cannot change on a live task, so a list of another length answers 409 proof_kinds_locked) and media_steps. The new words are checked as at funding, and a refusal (422 content_policy_violation, task_link_broken, task_other_platform, task_material_missing or task_not_doable; 403 verified_required; 409 edit_needs_review) changes nothing. The people working on it are told: message is what they are told, and it is also posted as a note. Proof already sent is judged against the version its worker started with.
Example
curl -X POST https://microworker.app/api/v1/tasks/311/edit \
-H "Authorization: Bearer $MW_KEY" \
-H "Content-Type: application/json" \
-d '{"instructions":"1. Open t.me/yourgroup\n2. Join the group\n3. React to the three newest posts\n4. Screenshot the group with your name visible","message":"Step 3 is new: react to the three newest posts.","dry_run":true}'
At most limit new workers (1–1000) per window, "hour" or "day"; limit: null takes the brake off. Every claim made inside the window counts as a start. While the window is full the task is off the board for anyone not already on it, and a worker who tries to start is told when the next place opens (task_paced, with opens_at). Drafts can have one too.
Up to 500 characters, on an open or paused task. Shown on the task page to everyone who can see the task and sent to whoever is working on it. The proof reviewer reads it too, but a worker who started before it is never refused only because of it. E-mail addresses and phone numbers are hidden (masked: true); links and @handles stay. At most 20 notes on a task at a time, and 10 an hour across your tasks.
Example
curl -X POST https://microworker.app/api/v1/tasks/311/notes \
-H "Authorization: Bearer $MW_KEY" \
-H "Content-Type: application/json" \
-d '{"body":"Please react to every message from the last 24 hours, not only the pinned one."}'
Response
{ "ok": true, "note": { "id": 27, "body": "Please react to every message from the last 24 hours, not only the pinned one.", "created_at": "2026-10-13T09:30:00Z", "masked": false }, "notified": 4 }
DELETE/tasks/{id}/notes/{noteId}tasks
Take a note down
It leaves the task page and the proof reviewer stops reading it; it cannot be put back. note_not_found when it is not one of this task’s notes, or already down.
An open or paused task. slots_total: fewer or more places — never fewer than the places in use, and one must stay unpaid. reward_minor: pay can only go up; workers who started get the new pay, and proof already sent keeps its price. Only the difference moves: refund_minor comes back to your balance, extra_minor is taken from it (402 insufficient_balance when it is not there). Needs the Fund scope and counts toward the money limit — dry runs too.
Every submission is checked automatically — copied, recycled or blank screenshots, reused text and links, and any must_show text. check_status is clear, warn or hold; held proof is excluded from automatic payment. You can reject invalid work or contact support about a false finding. Employers cannot manually approve submissions.
GET/tasks/{id}/submissionsread
Submissions on a task
Filter with ?status=pending. The approved filter includes automatic approvals. check_flags says what the checks found. Poll it, or have new work pushed to you with a webhook.
No money moves and the slot goes back to the pool. The worker sees your reason.
Example
curl -X POST https://microworker.app/api/v1/submissions/5013/reject \
-H "Authorization: Bearer $MW_KEY" \
-H "Content-Type: application/json" \
-d '{"rejection_reason":"Your name is not visible in the screenshot."}'
Response
{ "ok": true }
GET/proofs/{file}read
Download a proof screenshot
For /uploads/proofs/<file> in screenshot_paths, request /proofs/<file>. Only the task’s owner can.
Instead of polling, give us an HTTPS address and we POST each event to it as it happens. Every request is signed with that endpoint’s own secret.
Events
submission.createdA worker sent work, or said they finished a conversion task. check_status is null if the checks were still running.
submission.approvedWork was paid automatically after its review window, a successful check or a confirmed sign-up, or through a staff dispute resolution.
submission.rejectedYou rejected work. rejection_reason is what the worker was told.
task.completedThe last slot was paid and the task is finished.
pingOnly from the test button, to check your endpoint.
Each one is a POST with a JSON body. Screenshot URLs in it are API addresses: fetch them with your key.
Every request carries Microworker-Signature: t=<unix time>,v1=<signature>. Compute an HMAC-SHA256 of t, a dot and the raw request body, keyed with the endpoint's whole secret (whsec_ included), and compare it with v1 in constant time. Refuse a t more than five minutes old, and check before you parse the JSON.
Example code
import crypto from 'node:crypto';
import express from 'express';
const app = express();
const SECRET = process.env.MW_WEBHOOK_SECRET; // the whole secret, whsec_ included
// express.raw: the signature is over the exact bytes we sent, so check it before parsing.
app.post('/hooks/microworker', express.raw({ type: 'application/json' }), (req, res) => {
const sig = Object.fromEntries((req.get('Microworker-Signature') || '').split(',').map((p) => p.split('=')));
const expected = crypto.createHmac('sha256', SECRET).update(sig.t + '.' + req.body).digest('hex');
const fresh = Math.abs(Date.now() / 1000 - Number(sig.t)) < 300;
const same = typeof sig.v1 === 'string' && sig.v1.length === expected.length &&
crypto.timingSafeEqual(Buffer.from(sig.v1), Buffer.from(expected));
if (!fresh || !same) return res.status(400).end();
const event = JSON.parse(req.body);
// A retry sends the same event.id: store it, and skip an id you have already handled.
if (event.type === 'submission.created') {
const { submission, task } = event.data;
console.log('New work on', task.title, '— checks:', submission.check_status);
}
res.status(200).end();
});
Answering and retries
Answer with any 2xx within 8 seconds; do slow work after you answer. Anything else is tried again after 1 minute, 5 minutes, 30 minutes, 2, 6 and 12 hours, then a day — eight tries in all. Microworker-Event-Id is the same on every try, so store it and skip an event you have already handled. After 50 failed deliveries in a row an endpoint is switched off.
Addresses must be https on the public internet — we refuse private and local networks, and never follow redirects.
Your endpoints
Sign in to add endpoints. They belong to your account, like your keys.
url must be https on a public address. events defaults to all four. The answer carries secret — the only time it is shown, so store it with your key. Up to 5 endpoints.
For sign-up and install tasks: pay only for sign-ups your own site saw happen. No screenshots, and nothing to review.
Post the task with "verify_method": "postback" and your sign-up page as "verify_target". It asks workers for no proof.
Every worker who claims it opens a personal link: your page with mw_click=mwc_… added to the address. Keep that value with the account they create — a hidden form field is enough.
When the sign-up counts — email confirmed, first login, whatever you decide — send the click to POST /conversions with a key that has the Conversions scope. The worker is paid at once, from the task's escrow.
Example code
// 1. On your sign-up page: keep the click with the account being created.
const click = new URL(location.href).searchParams.get('mw_click'); // "mwc_…"
// …send it with the sign-up form and store it on the new user…
// 2. On your server, once the sign-up counts (email confirmed, first login — your call):
await fetch('https://microworker.app/api/v1/conversions', {
method: 'POST',
headers: { Authorization: 'Bearer ' + process.env.MW_KEY, 'Content-Type': 'application/json' },
body: JSON.stringify({ click: user.mwClick, reference: String(user.id) })
});
Nothing on these tasks is paid by a review window. A worker whose sign-up you never confirm waits, and you can reject their pending submission but cannot manually approve it. A confirmation has to arrive while their claim is open or waiting (a claim that ran out answers claim_expired), a click works only on your own tasks, and sending one twice pays once. With a webhook, submission.approved tells you the payment went through.
POST/conversionsconversions
Confirm a sign-up — this pays the worker
click is the mw_click value your page received; reference is your own id for the account (optional, shown in your review). Sending the same click twice is safe: the second answer says already: true and pays nothing.
All amounts are whole US cents in fields that end _minor: reward_minor: 5 is $0.05. You set the reward for every task, from $0.02 per worker ($0.05 when it asks for a screenshot or a file).
The platform fee is 5% of the budget plus $1.00 per task, charged when you fund. Verified employers pay no fee. The live figures are always in GET /api/version (platform_fee_bps, platform_flat_fee_minor, min_reward_minor, min_reward_screenshot_minor) — read them from there rather than hard-coding them.
Rewards sit in escrow until work is approved. Cancelling a task returns what is left to your balance. Money is never taken from a worker.
Checks, codes and instant reviewCopied proof, completion codes, must-show text›
Every submission is checked for copied, recycled and blank screenshots and for reused text and links. check_status is clear, warn or hold; held proof is excluded from automatic payment. You can reject invalid work or contact support if a finding looks wrong.
A code proof is a completion code you show at the end of your own flow (a survey's last page, a sign-up confirmation). When a worker types it back correctly they are paid on the spot. Codes are stored as a fingerprint and never returned.
auto_approve_hours: 0 is instant review: each submission is paid as soon as its checks come back clear. Proof flagged by the checks stays pending.
A screenshot proof can name text it must show — "must_show": "Your Group Name". We read each screenshot on our own server (nothing is sent anywhere): a submission whose screenshots read clearly but do not show it is held (missing_text), one too hard to read gets a warning (text_unreadable), and a match is reported as text_found.
Errors42 error codes›
A failed call answers with an HTTP status and { "error": "<code>" }. The code is stable; build on it, not on the message.
400 validation_failedA field is missing or wrong; issues says which.
400 reward_below_minimumUnder the per-worker minimum (min_minor says what it is).
400 budget_below_minimumreward × workers is under the smallest task budget.
400 verify_target_invalidverify_target must be the full https:// address of your sign-up page (422 when funding).
400 url_not_httpsA webhook address must start with https://.
400 url_not_publicThe webhook address points at a private or local network.
400 url_invalidThe webhook address is not a web address, or uses a port under 1024 other than 443 (url_port).
401 api_key_requiredNo key was sent.
401 invalid_api_keyThe key is wrong or was revoked.
402 insufficient_balanceNot enough available balance to fund it. Add funds on the website.
403 insufficient_scopeThe key lacks the scope named in required.
403 email_not_verifiedConfirm your email on the website first.
403 verified_requiredOnly a plan may post this kind of task; plan says which (verified or company) and category what kind of task it is. Nothing was charged.
404 task_not_foundNo such task on your account (also what another account’s task answers).
404 click_not_foundNo claim on your tasks has that click.
404 webhook_not_foundNo such endpoint on your account.
404 note_not_foundNo such note on that task, or it was already taken down.
404 media_not_foundAn attachment id in media_steps is not one of the task’s.
409 invalid_task_stateNot possible in the task’s current state, e.g. a PATCH field edit on a live task (use /edit), or pausing a task that is not open.
409 task_under_reviewThe task is held for a staff review; it cannot be edited or resumed until then.
409 task_changedThe task changed while your request was being checked. Read it again and retry.
409 proof_kinds_lockedOn a live task, proof_notes needs exactly one entry per proof requirement (count says how many): the kinds of proof cannot change.
409 edit_needs_reviewThe new words need a person to look at them before they go live. Nothing changed.
409 too_many_notesThe task has as many notes as it can hold (max). Take one down first.
409 reward_cannot_decreasePay on a live task can only go up (min_minor is its pay now).
409 slots_below_takenFewer places than the task can have now (min_slots says the fewest).
409 task_has_open_disputesA dispute on the task is open; its places and pay cannot change until it is decided.
409 cannot_cancel_with_pending_submissionsProof is waiting for its review. Cancel once it is decided.
409 cannot_cancel_with_open_disputesA dispute on the task is open. Cancel once it is decided.
409 submission_not_pendingThat submission was already approved, rejected or closed.
409 claim_expiredThe worker’s claim ran out before your confirmation arrived; nothing was paid.
409 too_many_webhooksYou already have 5 endpoints. Remove one first.
422 content_policy_violationThe posting breaks the rules; reason may say what to change. Nothing was charged.
422 task_link_brokenA link in the task does not work (url says which). Fix it and try again; nothing was charged or changed.
422 task_other_platformThe task sends workers to another micro-task site (names says which).
422 task_material_missingThe task refers to material, such as images to copy from, that is neither attached nor linked.
422 task_not_doableA worker here could not do it; reason says why.
429 rate_limit_exceededToo many requests. Wait for the time in the RateLimit headers.
429 too_many_attemptsToo many changes from your server in an hour, or more than 10 notes in an hour.
503 api_unavailableThe API is switched off for maintenance. Try again later.
503 webhooks_unavailableWebhooks are switched off for maintenance; conversions_unavailable is the same for confirmations.
503 live_jobs_unavailableManaging live tasks is switched off for maintenance. Nothing changed.
Limits120 requests a minute per key›
120 requests a minute per key, of which 30 may be funding, places-and-pay, rejection or conversion confirmation calls. Past that the API answers 429 with RateLimit headers saying when to try again. Creating and funding tasks are also limited to a few hundred an hour per server address.
Prefer webhooks to polling. If you do poll GET /tasks/{id}/submissions?status=pending, once a minute or two is plenty.
ChangelogWhat changed, newest first›
— Managing a live job: change a live task’s words (POST /tasks/{id}/edit), slow it down (/pace), post and take down notes to the people working on it (/notes), and change its places and pay (/resize, Fund scope). Edit and resize take "dry_run": true. GET /tasks/{id} now carries notes, pace and edited_at. Their examples come in curl, Node.js and Python.
3 Oct 2026 — Submission approval and payment are automatic. Review keys can reject only. Conversion integrations need a new key with the Conversions permission; existing Review keys no longer confirm sign-ups.
29 September 2026. Webhooks; conversion confirmations (pay only for sign-ups your site confirms); must_show text on screenshot proofs.
v1 — 29 September 2026. First release: tasks, funding, review, proof files, balance, deposits and the pricing guide.