DocsDevelopers
TaskerData API
Place campaigns, collect workers' submissions and review them from your own code, or connect an external qualification test. Every request is authenticated with your API key.
Overview
The Campaign API lets your code do what you do in the requester dashboard: create campaigns, follow their progress, download submissions with their proof and approve or reject them. The external qualification endpoints let a test on your own website tell TaskerData whether each worker passed.
All requests go to https://taskerdata.com over HTTPS. Send and receive JSON.
Campaigns created through the API cost the same and follow the same rules as in the dashboard. TaskerData reviews them before they go live (trusted requesters go live straight away), and money is reserved and paid out the same way.
Authentication
Open the requester dashboard and go to API. You will find two keys there:
- API key: for real requests. The Campaign API reads it from the Authorization header. The qualification endpoint takes it as api_token.
- Test API key: only for the qualification test endpoint, so you can try that integration without touching real workers.
Generating a new key replaces the old one straight away, so update your backend when you do. You can generate up to 3 new keys per hour.
Important: Keep your API key secret. Use it only on your server, never in a web page or app that visitors can open.
Send your API key with every Campaign API request:
Authorization: Bearer YOUR_API_KEYYou can make up to 120 Campaign API requests per minute. The X-RateLimit-Remaining header shows how many are left. After that you get status 429, and the Retry-After header says how many seconds to wait.
Campaign API
Create campaigns and handle the work they bring in, from your own code.
List campaigns
GET/api/v1/campaigns
Your campaigns, newest first. total is how many campaigns match, so you can page through them with limit and offset.
Query parameters
| Parameter | Description |
|---|---|
status | Only campaigns with this status: pending, in review, active, paused, completed, terminated or rejected |
limit | How many to return, 1 to 100 (default 20) |
offset | How many to skip, for paging (default 0) |
curl "https://taskerdata.com/api/v1/campaigns?status=active&limit=20" \
-H "Authorization: Bearer $TASKERDATA_API_KEY"Response · 200
{
"success": true,
"campaigns": [
{
"id": "Ab12…-Cd34…-Ef56…",
"title": "Test our sign-up page",
"status": "active",
"category": "Website Testing",
"type": "instructions",
"url": "https://example.com/signup",
"payment": 0.5,
"positions": 50,
"filled_positions": 12,
"time_to_complete": 10,
"time_to_rate": 3,
"cost": 34.06,
"proof": {
"text": "What went wrong?",
"images": [
"Screenshot after signing up"
],
"video": null
},
"qualification_id": null,
"created_at": "2026-10-16T09:30:00.000Z"
}
],
"total": 1,
"limit": 20,
"offset": 0
}Error example
// status 401
{
"success": false,
"error": {
"code": "invalid_api_key",
"message": "Invalid API key."
}
}Get a campaign
GET/api/v1/campaigns/{id}
One campaign with its settings and how many submissions are waiting for review, waiting for a revision, approved and rejected. Use it to follow a campaign's progress.
In the address
| Parameter | Description |
|---|---|
id required | The campaign ID (id in the campaign list) |
curl "https://taskerdata.com/api/v1/campaigns/CAMPAIGN_ID" \
-H "Authorization: Bearer $TASKERDATA_API_KEY"Response · 200
{
"success": true,
"campaign": {
"id": "Ab12…-Cd34…-Ef56…",
"title": "Test our sign-up page",
"status": "active",
"category": "Website Testing",
"type": "instructions",
"url": "https://example.com/signup",
"payment": 0.5,
"positions": 50,
"filled_positions": 12,
"time_to_complete": 10,
"time_to_rate": 3,
"cost": 34.06,
"proof": {
"text": "What went wrong?",
"images": [
"Screenshot after signing up"
],
"video": null
},
"qualification_id": null,
"created_at": "2026-10-16T09:30:00.000Z",
"submissions": {
"total": 14,
"to_review": 3,
"revision_requested": 1,
"approved": 9,
"rejected": 1
}
}
}Error example
// status 404
{
"success": false,
"error": {
"code": "not_found",
"message": "Campaign not found."
}
}Create a campaign
POST/api/v1/campaigns
Places a new campaign and reserves its full cost from your available balance, exactly like the dashboard. The answer includes the campaign's id, its cost and its status: pending while TaskerData reviews it, or active straight away if you are a trusted requester.
JSON body
| Parameter | Description |
|---|---|
title required | Campaign title, up to 100 characters |
category required | A category from GET /api/v1/options. Leave it out when you use a template |
instructions required | What workers should do, up to 5,000 characters |
url | The link workers open. Required for categories where needs_url is true |
payment required | Payment per task in USD, at least 0.12 |
positions required | How many workers, at least 10. With a template data file it is worked out from the rows |
time_to_complete required | Minutes a worker has to finish, 1 to 120 |
time_to_rate required | Days you have to review each submission, 1 to 30. After that it is approved automatically |
proof.text | Ask for a text answer: tell the worker what to write |
proof.images | Ask for screenshots or photos: one description per image |
proof.video | Ask for a video or a recording: what it should show |
targeting.region | international (default), usa, west-europe, east-europe, latin-america or asia-africa |
targeting.exclude_countries | Country names to leave out of the region |
targeting.min_success_rate | Minimum worker success rate: 75 (default), 80, 85, 90, 95 or 100 |
targeting.type, targeting.group_id | Set type to "group" and group_id to one of your worker groups to send the task only to that group |
qualification_id | Only workers who passed this qualification test can take the task |
template | Use one of your templates instead of instructions: id, plus data with columns and rows that fill its placeholders, one row per task. answers_per_row (1 to 5) and rows_per_task (1 to 50) are optional |
drip_feed | Release workers in batches: min_workers and max_workers (1 to 50) and interval_minutes (15, 30, 60, 120, 180, 360, 720 or 1440) |
location | For Local Field Tasks and Mystery Shopping: lat, lng, radius_m (50 to 50,000) and an optional address |
For now, categories that need files you upload (such as Audio Transcription or Image Tagging), video tasks and example images can only be created in the dashboard. The Costs and fees guide explains how the price is calculated.
curl -X POST "https://taskerdata.com/api/v1/campaigns" \
-H "Authorization: Bearer $TASKERDATA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"title": "Test our sign-up page",
"category": "Website Testing",
"url": "https://example.com/signup",
"instructions": "Open the page, create an account and tell us anything that went wrong.",
"payment": 0.5,
"positions": 50,
"time_to_complete": 10,
"time_to_rate": 3,
"proof": {
"text": "What went wrong?",
"images": [
"Screenshot after signing up"
]
},
"targeting": {
"region": "international",
"min_success_rate": 90
}
}'Response · 201
{
"success": true,
"campaign": {
"id": "Ab12…-Cd34…-Ef56…",
"title": "Test our sign-up page",
"status": "pending",
"category": "Website Testing",
"type": "instructions",
"url": "https://example.com/signup",
"payment": 0.5,
"positions": 50,
"filled_positions": 0,
"time_to_complete": 10,
"time_to_rate": 3,
"cost": 34.06,
"proof": {
"text": "What went wrong?",
"images": [
"Screenshot after signing up"
],
"video": null
},
"qualification_id": null,
"created_at": "2026-10-16T09:30:00.000Z"
},
"balance": {
"available": 65.94,
"reserved": 34.06
}
}Error example
// status 402
{
"success": false,
"error": {
"code": "insufficient_balance",
"message": "Insufficient balance"
}
}Get submissions
GET/api/v1/campaigns/{id}/submissions
The work workers sent for one campaign, oldest first, with their proof: the text answer, links to images and video, template answers (answers), the data row they worked on and, for field tasks, their location.
In the address
| Parameter | Description |
|---|---|
id required | The campaign ID (id in the campaign list) |
Query parameters
| Parameter | Description |
|---|---|
status | to_review (pending and resubmitted), pending, resubmitted, revision_requested, approved or rejected |
since | Only submissions sent at or after this time, for example 2026-10-01T00:00:00Z |
after_id | For paging: only submissions with a higher id. Use next_after_id from the previous page |
limit | How many to return, 1 to 100 (default 50) |
To collect new work regularly, keep the last id you received and send it as after_id next time. raw holds everything the worker sent, in case you need something that isn't listed.
curl "https://taskerdata.com/api/v1/campaigns/CAMPAIGN_ID/submissions?status=to_review&after_id=0" \
-H "Authorization: Bearer $TASKERDATA_API_KEY"Response · 200
{
"success": true,
"submissions": [
{
"id": 1842,
"campaign_id": "Ab12…-Cd34…-Ef56…",
"worker_id": "wK3…",
"status": "pending",
"started_at": "2026-10-16 10:02:11",
"submitted_at": "2026-10-16 10:09:45",
"revision_count": 0,
"revision_note": null,
"rejection_reason": null,
"payment": 0.5,
"proof": {
"text": "The confirm button did nothing on mobile.",
"image_urls": [
"https://storage.taskerdata.com/…/proof_0.png"
],
"video_url": null
},
"answers": null,
"data_row": null,
"data_row_number": null,
"items": null,
"location": null,
"raw": {
"textProof": "…"
}
}
],
"has_more": false,
"next_after_id": null
}Error example
// status 400
{
"success": false,
"error": {
"code": "invalid_status",
"message": "status must be one of: to_review, pending, resubmitted, revision_requested, approved, rejected."
}
}Review a submission
POST/api/v1/submissions/{id}/review
Approve, reject or ask for a revision, the same as the buttons in the dashboard. Approving pays the worker from the reserved money. Rejecting returns the payment and its fee to your balance and frees the position for another worker.
In the address
| Parameter | Description |
|---|---|
id required | The submission ID |
JSON body
| Parameter | Description |
|---|---|
action required | approve, reject or revision |
reason | With reject: the reason the worker sees |
note | With revision (required): what the worker should fix |
You can review a submission only while it is waiting for review (pending or resubmitted). Revisions are limited by the campaign's revision limit; after that you can only approve or reject.
curl -X POST "https://taskerdata.com/api/v1/submissions/1842/review" \
-H "Authorization: Bearer $TASKERDATA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"action": "revision",
"note": "Please add a screenshot of the error message."
}'Response · 200
{
"success": true,
"submission": {
"id": 1842,
"status": "revision_requested"
},
"balance": {
"available": 65.94,
"reserved": 34.06
}
}Error example
// status 409
{
"success": false,
"error": {
"code": "already_reviewed",
"message": "Submission already reviewed"
}
}Check your balance
GET/api/v1/balance
available is what you can spend on new campaigns, reserved is held for live campaigns, and spent is what you have paid workers so far.
curl "https://taskerdata.com/api/v1/balance" \
-H "Authorization: Bearer $TASKERDATA_API_KEY"Response · 200
{
"success": true,
"balance": {
"available": 65.94,
"reserved": 34.06,
"spent": 212.4
}
}Options and IDs
GET/api/v1/options
Lists the categories you can use through the API (needs_url and needs_location show which ones need a link or a location), the allowed targeting and drip feed values, and the IDs of your templates, worker groups and qualification tests.
curl "https://taskerdata.com/api/v1/options" \
-H "Authorization: Bearer $TASKERDATA_API_KEY"Response · 200
{
"success": true,
"categories": [
{
"name": "Website Testing",
"needs_url": true,
"needs_location": false
},
"…"
],
"regions": [
"international",
"usa",
"west-europe",
"east-europe",
"latin-america",
"asia-africa"
],
"min_success_rates": [
75,
80,
85,
90,
95,
100
],
"drip_feed_intervals": [
15,
30,
60,
120,
180,
360,
720,
1440
],
"templates": [
{
"id": 12,
"title": "Sentiment check"
}
],
"worker_groups": [
{
"id": 3,
"name": "Trusted testers",
"workers": 25
}
],
"qualifications": [
{
"id": "Qa1…",
"title": "English test",
"status": "active"
}
]
}Errors
The Campaign API uses HTTP status codes. When something goes wrong, success is false and error has a code to check in your code and a message to show or log. Campaign checks use the same messages as the dashboard.
| Status | error.code | What it means |
|---|---|---|
| 400 | invalid_body, invalid_campaign, invalid_region, category_needs_files, invalid_status, invalid_since, invalid_action, note_required | Something in the request is missing or not allowed. message says what. |
| 401 | missing_api_key, invalid_api_key | No API key, or the key is wrong or was replaced by a newer one. |
| 402 | insufficient_balance | Your available balance doesn't cover the campaign. Add funds and try again. |
| 403 | test_key_not_allowed, account_suspended | Use your live API key. A suspended account can't use the API. |
| 404 | not_found | There is no campaign or submission with that ID in your account. |
| 409 | already_reviewed, review_refused | The submission was already reviewed, or the review isn't possible right now, for example because the revision limit was reached. |
| 429 | rate_limited | More than 120 requests in a minute. Wait the number of seconds in Retry-After. |
| 500 | internal_error | A problem on our side. Try again, and contact support if it keeps happening. |
External qualification tests
For qualification tests that run on your own website.
Report a test result Live
GET/api/worker/external_qualification/data
For qualification tests marked as external. The worker opens your test from the qualification and enters their worker token on your website. When they finish, call this endpoint with approve or reject.
| Parameter | Description |
|---|---|
qualification_id required | The ID of your qualification test |
worker_token required | The worker token the worker entered on your website |
api_token required | Your API key (the test key for the test endpoint) |
decision required | approve or reject |
If the test pays a bonus for passing and your requester balance can't cover it, the submission is saved as pending instead, so you can top up and approve it in the dashboard.
A worker can be evaluated once per test. The test must be active and marked as external, and the API key must belong to the test's owner.
curl -G "https://taskerdata.com/api/worker/external_qualification/data" \
--data-urlencode "qualification_id=QUAL_ID" \
--data-urlencode "worker_token=WORKER_TOKEN" \
--data-urlencode "api_token=YOUR_API_KEY" \
--data-urlencode "decision=approve"Response · 200
{
"success": true
}Error example
{
"success": false,
"error": "This worker has already been evaluated for this qualification"
}Test your setup
POST/api/worker/external_qualification/data_test
Send the same fields as JSON to the test endpoint, with your test API key. Nothing is saved and no worker is affected: it only checks your request.
| Parameter | Description |
|---|---|
qualification_id required | The ID of your qualification test |
worker_token required | The worker token the worker entered on your website |
api_token required | Your API key (the test key for the test endpoint) |
decision required | approve or reject |
A wrong test key returns status 401; missing fields or a wrong decision return status 400.
curl -X POST "https://taskerdata.com/api/worker/external_qualification/data_test" \
-H "Content-Type: application/json" \
-d '{
"qualification_id": "QUAL_ID",
"worker_token": "WORKER_TOKEN",
"api_token": "YOUR_TEST_API_KEY",
"decision": "approve"
}'Response · 200
{
"success": true
}Error example
// status 401
{
"statusCode": 401,
"statusMessage": "Invalid API test key"
}Test result errors
The result endpoint always answers with status 200. Check success: when it is false, error says why:
| error | What it means |
|---|---|
Missing required parameters: qualification_id, worker_token, api_token, decision | One of the four parameters is missing. |
Decision must be 'approve' or 'reject' | decision has another value. |
Invalid API token | The API key is wrong or was replaced by a newer one. |
Worker token not found for this qualification | The token doesn't belong to this test. Ask the worker to check it. |
API token does not belong to the qualification owner | Use the key of the account that created the test. |
This qualification is not configured for external submissions | The test isn't marked as external. |
This qualification is no longer active | The test is paused or finished. |
This worker has already been evaluated for this qualification | Each worker gets one result per test. |
Worker account is suspended | The worker can't take tests right now. |
No remaining positions for this qualification | All places in the test are taken. |