Skip to main content

Task API Reference

The Task API allows you to create, retrieve, update, and manage tasks on the ZhenRent platform.

Base URL

https://www.zhenrent.com/api/v1

Authentication

All endpoints require API Key authentication in the Authorization header:

Authorization: Bearer zr_live_your_api_key

Create Task

Create a new task with escrow payment.

Endpoint

POST /api/v1/tasks

Request Body

ParameterTypeRequiredDescription
titlestringYesTask title (1-200 characters)
descriptionstringYesDetailed task description
locationobjectYesGeographic location
location.latitudefloatYesLatitude (-90 to 90)
location.longitudefloatYesLongitude (-180 to 180)
location.addressstringNoHuman-readable address
location.max_distance_metersintegerNoMaximum distance from location (default: 1000m)
budget_centsintegerYesTask budget in cents (minimum 1000 = 10 RMB)
deadline_atstringYesISO 8601 deadline (must be future)
task_typestringYesType: photo, verification, data_collection, delivery, other
max_workersintegerNoMaximum workers (1-10, default: 1)
requirementsobjectNoWorker requirements
requirements.skillsarrayNoRequired skills (e.g., ["photography"])
requirements.min_reputationfloatNoMinimum reputation (0.0-5.0)
metadataobjectNoCustom metadata (max 1KB JSON)
idempotency_keystringNoUnique key for idempotent requests

Example Request

curl -X POST "https://www.zhenrent.com/api/v1/tasks" \
-H "Authorization: Bearer zr_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"title": "Verify restaurant opening hours",
"description": "Take a photo of the hour sign at the front entrance. Ensure the text is clearly readable.",
"location": {
"latitude": 39.916527,
"longitude": 116.397128,
"address": "123 Main St, Beijing",
"max_distance_meters": 500
},
"budget_cents": 5000,
"deadline_at": "2026-04-15T18:00:00Z",
"task_type": "photo",
"max_workers": 1,
"requirements": {
"skills": ["photography"],
"min_reputation": 4.0
},
"metadata": {
"order_id": "ORD-12345",
"customer_id": "CUST-789"
},
"idempotency_key": "550e8400-e29b-41d4-a716-446655440000"
}'

Python Example

import requests
from datetime import datetime, timedelta

response = requests.post(
"https://www.zhenrent.com/api/v1/tasks",
headers={
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json"
},
json={
"title": "Verify restaurant opening hours",
"description": "Take a photo of the hour sign at the front entrance",
"location": {
"latitude": 39.916527,
"longitude": 116.397128,
"max_distance_meters": 500
},
"budget_cents": 5000,
"deadline_at": (datetime.now() + timedelta(days=7)).isoformat() + "Z",
"task_type": "photo",
"max_workers": 1
}
)

JavaScript Example

const response = await fetch('https://www.zhenrent.com/api/v1/tasks', {
method: 'POST',
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
title: 'Verify restaurant opening hours',
description: 'Take a photo of the hour sign at the front entrance',
location: {
latitude: 39.916527,
longitude: 116.397128,
max_distance_meters: 500
},
budget_cents: 5000,
deadline_at: new Date(Date.now() + 7*24*60*60*1000).toISOString(),
task_type: 'photo',
max_workers: 1
})
});

Success Response (201 Created)

{
"task_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "pending",
"estimated_completion_at": "2026-04-15T16:00:00Z",
"cost_breakdown": {
"worker_payout_cents": 4500,
"platform_fee_cents": 500,
"total_cents": 5000
},
"created_at": "2026-03-31T10:00:00Z"
}

Error Responses

Status CodeError CodeDescription
400validation_errorInvalid request parameters
401invalid_api_keyAPI key invalid or missing
402insufficient_balanceAccount balance too low
429rate_limit_exceededToo many requests

400 Example (Validation Error)

{
"error": {
"code": "validation_error",
"message": "Request validation failed",
"details": [
{
"field": "budget_cents",
"message": "Input should be greater than or equal to 1000"
}
]
}
}

402 Example (Insufficient Balance)

{
"error": {
"code": "insufficient_balance",
"message": "Account balance (3000 cents) insufficient for task cost (5000 cents)",
"balance_cents": 3000,
"required_cents": 5000
}
}

Get Task Details

Retrieve detailed information about a specific task.

Endpoint

GET /api/v1/tasks/{task_id}

Path Parameters

ParameterTypeDescription
task_idUUIDUnique task identifier

Example Request

curl "https://www.zhenrent.com/api/v1/tasks/550e8400-e29b-41d4-a716-446655440000" \
-H "Authorization: Bearer zr_live_your_api_key"

Success Response (200 OK)

{
"task_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "in_progress",
"title": "Verify restaurant opening hours",
"description": "Take a photo of the hour sign at the front entrance",
"progress": {
"assigned_workers": 1,
"completed_workers": 0,
"estimated_completion_at": "2026-04-15T17:00:00Z"
},
"assignments": [
{
"worker_id": "worker_abc123",
"worker_reputation": 4.8,
"assigned_at": "2026-03-31T10:05:00Z",
"status": "in_progress"
}
],
"created_at": "2026-03-31T10:00:00Z",
"deadline_at": "2026-04-15T18:00:00Z"
}

Error Responses

Status CodeError CodeDescription
401invalid_api_keyInvalid authentication
404task_not_foundTask doesn't exist or not owned by you

List Tasks

Retrieve a paginated list of your tasks.

Endpoint

GET /api/v1/tasks

Query Parameters

ParameterTypeDefaultDescription
statusstringallFilter by status: pending, assigned, in_progress, submitted, completed, failed, cancelled
limitinteger20Number of results (max: 100)
starting_afterUUID-Cursor for pagination

Example Request

curl "https://www.zhenrent.com/api/v1/tasks?status=completed&limit=10" \
-H "Authorization: Bearer zr_live_your_api_key"

Python Example

response = requests.get(
"https://www.zhenrent.com/api/v1/tasks",
headers={"Authorization": f"Bearer {API_KEY}"},
params={"status": "completed", "limit": 20}
)

tasks = response.json()
for task in tasks['data']:
print(f"{task['title']}: {task['status']}")

Success Response (200 OK)

{
"data": [
{
"task_id": "550e8400-e29b-41d4-a716-446655440000",
"title": "Verify restaurant opening hours",
"status": "completed",
"budget_cents": 5000,
"created_at": "2026-03-31T10:00:00Z",
"deadline_at": "2026-04-15T18:00:00Z"
},
{
"task_id": "660e8400-e29b-41d4-a716-446655440001",
"title": "Check store inventory",
"status": "completed",
"budget_cents": 3000,
"created_at": "2026-03-30T14:00:00Z",
"deadline_at": "2026-04-10T18:00:00Z"
}
],
"has_more": true,
"next_cursor": "660e8400-e29b-41d4-a716-446655440001"
}

Pagination Example

all_tasks = []
cursor = None

while True:
params = {"limit": 100}
if cursor:
params["starting_after"] = cursor

response = requests.get(
"https://www.zhenrent.com/api/v1/tasks",
headers={"Authorization": f"Bearer {API_KEY}"},
params=params
)

data = response.json()
all_tasks.extend(data['data'])

if not data['has_more']:
break
cursor = data['next_cursor']

print(f"Total tasks: {len(all_tasks)}")

Get Task Result

Retrieve the completed task result (available only after completion).

Endpoint

GET /api/v1/tasks/{task_id}/result

Example Request

curl "https://www.zhenrent.com/api/v1/tasks/550e8400-e29b-41d4-a716-446655440000/result" \
-H "Authorization: Bearer zr_live_your_api_key"

Success Response (200 OK)

{
"task_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "completed",
"result": {
"data": {
"verification_status": "confirmed",
"opening_hours": "Mon-Fri 9AM-9PM, Sat-Sun 10AM-8PM",
"additional_notes": "Store was open during visit"
},
"photos": [
{
"url": "https://storage.zhenrent.com/photos/abc123.jpg",
"taken_at": "2026-04-10T14:30:00Z"
},
{
"url": "https://storage.zhenrent.com/photos/def456.jpg",
"taken_at": "2026-04-10T14:31:00Z"
}
],
"worker_notes": "Photo taken during business hours, sign clearly visible"
},
"completed_at": "2026-04-10T14:35:00Z",
"quality_score": 4.9
}

Error Responses

Status CodeError CodeDescription
404task_not_foundTask doesn't exist
404result_not_availableTask not yet completed

404 Example (Not Completed)

{
"error": {
"code": "result_not_available",
"message": "Task is still in progress",
"current_status": "in_progress"
}
}

Submit Feedback

Submit feedback on a completed task submission.

Endpoint

POST /api/v1/tasks/{task_id}/feedback

Request Body

ParameterTypeRequiredDescription
accept_resultbooleanYesAccept (true) or reject (false) result
ratingintegerYesRating 1-5 stars
quality_scorefloatYesQuality score 0.0-5.0
feedback_textstringNoWritten feedback (max 500 chars)

Example Request

curl -X POST "https://www.zhenrent.com/api/v1/tasks/550e8400-e29b-41d4-a716-446655440000/feedback" \
-H "Authorization: Bearer zr_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"accept_result": true,
"rating": 5,
"quality_score": 4.9,
"feedback_text": "Excellent work! Photo is clear and meets all requirements."
}'

Success Response (200 OK)

{
"feedback_id": "fb_abc123",
"recorded_at": "2026-04-10T15:00:00Z"
}

Note: When you accept a result (accept_result: true), the escrowed payment is released to the worker.


Task Status Lifecycle

Understanding task status progression:

pending → assigned → in_progress → submitted → completed

failed/cancelled

Status Descriptions:

  • pending - Task created, searching for eligible workers
  • assigned - Worker found and assigned to task
  • in_progress - Worker actively working on task
  • submitted - Worker submitted result, awaiting your review
  • completed - You approved result, payment released
  • failed - Task failed to complete (deadline expired, worker failed)
  • cancelled - Task cancelled by you

Rate Limits

  • Free tier: 100 requests/minute per API key
  • Paid tier: 1,000 requests/minute per API key

When rate limit exceeded, response:

{
"error": {
"code": "rate_limit_exceeded",
"message": "Too many requests. Please retry after 60 seconds.",
"retry_after": 60
}
}

Rate Limit Headers:

X-RateLimit-Limit: 100
X-RateLimit-Remaining: 45
X-RateLimit-Reset: 1680261600

Best Practices

1. Use Idempotency Keys

Always include idempotency_key to prevent duplicate tasks:

import uuid

response = requests.post(url, json={
"idempotency_key": str(uuid.uuid4()),
# ... other fields
})

2. Handle Timeouts

Set appropriate timeout values:

try:
response = requests.post(url, json=data, timeout=30)
except requests.Timeout:
print("Request timed out, please retry")

3. Implement Exponential Backoff

For rate limit errors:

import time

def create_task_with_retry(data, max_retries=3):
for attempt in range(max_retries):
try:
response = requests.post(url, json=data)
response.raise_for_status()
return response.json()
except requests.HTTPError as e:
if e.response.status_code == 429:
wait_time = 2 ** attempt # 1s, 2s, 4s
time.sleep(wait_time)
else:
raise
raise Exception("Max retries exceeded")

4. Monitor Task Progress

Use webhooks instead of polling:

# Instead of polling every 30 seconds
while task['status'] != 'completed':
time.sleep(30)
task = get_task(task_id)

# Use webhooks (see Webhook documentation)