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
| Parameter | Type | Required | Description |
|---|---|---|---|
| title | string | Yes | Task title (1-200 characters) |
| description | string | Yes | Detailed task description |
| location | object | Yes | Geographic location |
| location.latitude | float | Yes | Latitude (-90 to 90) |
| location.longitude | float | Yes | Longitude (-180 to 180) |
| location.address | string | No | Human-readable address |
| location.max_distance_meters | integer | No | Maximum distance from location (default: 1000m) |
| budget_cents | integer | Yes | Task budget in cents (minimum 1000 = 10 RMB) |
| deadline_at | string | Yes | ISO 8601 deadline (must be future) |
| task_type | string | Yes | Type: photo, verification, data_collection, delivery, other |
| max_workers | integer | No | Maximum workers (1-10, default: 1) |
| requirements | object | No | Worker requirements |
| requirements.skills | array | No | Required skills (e.g., ["photography"]) |
| requirements.min_reputation | float | No | Minimum reputation (0.0-5.0) |
| metadata | object | No | Custom metadata (max 1KB JSON) |
| idempotency_key | string | No | Unique 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 Code | Error Code | Description |
|---|---|---|
| 400 | validation_error | Invalid request parameters |
| 401 | invalid_api_key | API key invalid or missing |
| 402 | insufficient_balance | Account balance too low |
| 429 | rate_limit_exceeded | Too 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
| Parameter | Type | Description |
|---|---|---|
| task_id | UUID | Unique 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 Code | Error Code | Description |
|---|---|---|
| 401 | invalid_api_key | Invalid authentication |
| 404 | task_not_found | Task doesn't exist or not owned by you |
List Tasks
Retrieve a paginated list of your tasks.
Endpoint
GET /api/v1/tasks
Query Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
| status | string | all | Filter by status: pending, assigned, in_progress, submitted, completed, failed, cancelled |
| limit | integer | 20 | Number of results (max: 100) |
| starting_after | UUID | - | 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 Code | Error Code | Description |
|---|---|---|
| 404 | task_not_found | Task doesn't exist |
| 404 | result_not_available | Task 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
| Parameter | Type | Required | Description |
|---|---|---|---|
| accept_result | boolean | Yes | Accept (true) or reject (false) result |
| rating | integer | Yes | Rating 1-5 stars |
| quality_score | float | Yes | Quality score 0.0-5.0 |
| feedback_text | string | No | Written 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 workersassigned- Worker found and assigned to taskin_progress- Worker actively working on tasksubmitted- Worker submitted result, awaiting your reviewcompleted- You approved result, payment releasedfailed- 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)
Related Documentation
- Payment API - Manage balance and payments
- Webhooks - Real-time task notifications
- Error Codes - Complete error reference
- Python Examples - Production-ready code