API
The AfriAnnotate API exposes everything the web UI does β create projects, import tasks, manage members, export annotations. Use it to script bulk operations, integrate AfriAnnotate into a pipeline, or build a custom client.
Base URL: https://afriannotate.waraka.org/ β the URL your hoster
gave you, e.g. https://afriannotate.waraka.org/ for the reference
deployment or whatever your team's deployment uses.
All endpoints live under /api/. Most are JSON-in, JSON-out;
file uploads are multipart/form-data.
Authenticationβ
API calls authenticate with a Personal Access Token (PAT) β a JWT issued from your user profile.
Get a tokenβ
- Sign in at afriannotate.waraka.org.
- Open the user menu (your avatar, top right) and choose API tokens; it is also a tab on your profile.
- Click Make a token in the card's title row.
- Give it a name, choose what it may do (Full access or Read only), where it reaches (every project, or only the projects you choose), how long it lasts, and re-enter your account password.
- Copy the token immediately β AfriAnnotate shows it once.
A token confined to projects answers only those projects' endpoints
(and /api/current-user/whoami); anything else gets 403 token_scope_project. A read-only token gets 403 token_scope_readonly
on any write.
You won't be able to retrieve it later (only regenerate or
delete + replace). The list shows a masked stub afterwards.
The token is a JWT carrying the afriannotate_ prefix, so secret
scanners recognise it. Use it exactly as copied: the Authorization
header, the revoke endpoint (POST /api/token/blacklist/) and the rotate
endpoint (POST /api/token/rotate/) all accept the prefixed form.
You can hold one active token at a time; use Regenerate⦠in the
same modal to swap it for a fresh one. Tokens don't expire on their own β
rotate by regenerating.
For scripts that only read data (exports, dashboards), use a Read-only token β any write request it makes is rejected. Rotate with Regenerateβ¦ + updating the env var.
See Your account β API access tokens for the full management UI.
Use the token in HTTP requestsβ
Pass the token as a Bearer credential in the Authorization header:
curl https://{{platformHost}}/api/projects/ \
-H 'Authorization: Bearer <your-token>'
That's it. No session cookies, no CSRF dance β every authenticated request works with this header.
Use the token from the Python SDKβ
The official Python SDK auto-handles the header for you:
from label_studio_sdk import LabelStudio
client = LabelStudio(
base_url="https://{{platformHost}}",
api_key="<your-token>",
)
projects = client.projects.list()
for p in projects:
print(p.id, p.title)
See Python SDK for the full client reference.
Common endpointsβ
These are the API endpoints you'll reach for most often. The full endpoint reference is auto-generated from the backend schema:
- OpenAPI schema:
https://afriannotate.waraka.org/docs/api/schema/(also/docs/api/schema/json/and/docs/api/schema/yaml/) - Browsable docs:
/docs/api/schema/swagger-ui/and/docs/api/schema/redoc/
List your projectsβ
curl https://{{platformHost}}/api/projects/ \
-H 'Authorization: Bearer <token>'
Returns a paginated list of projects you have access to in your
active organisation. Need a project's id (the pk) for most
other endpoints.
Create a projectβ
curl -X POST https://{{platformHost}}/api/projects/ \
-H 'Authorization: Bearer <token>' \
-H 'Content-Type: application/json' \
-d '{
"title": "Hausa sentiment v1",
"label_config": "<View><Choices name=\"sentiment\" toName=\"text\"><Choice value=\"positive\"/><Choice value=\"negative\"/></Choices><Text name=\"text\" value=\"$text\"/></View>"
}'
Returns the new project with id. Validate the labeling config
ahead of time with POST /api/projects/<id>/validate/.
Import tasksβ
JSON-in:
curl -X POST https://{{platformHost}}/api/projects/<id>/import \
-H 'Authorization: Bearer <token>' \
-H 'Content-Type: application/json' \
-d '[
{"data": {"text": "Yana da kyau sosai"}},
{"data": {"text": "Ban so wannan ba"}}
]'
Each task's data keys must match the $varname references in
the labeling config. See Task format.
List tasksβ
curl 'https://{{platformHost}}/api/projects/<id>/tasks/?page=1&page_size=100' \
-H 'Authorization: Bearer <token>'
Export annotationsβ
# See available export formats for the project
curl https://{{platformHost}}/api/projects/<id>/export/formats \
-H 'Authorization: Bearer <token>'
# Export in JSON
curl 'https://{{platformHost}}/api/projects/<id>/export?exportType=JSON' \
-H 'Authorization: Bearer <token>' \
-o annotations.json
Supported formats include JSON (with full task + annotation structure), JSON_MIN, CSV (flat one-row-per-task), TSV, CoNLL2003, COCO, YOLO, Pascal VOC, brush masks as PNG, and ASR Manifest (for audio) β varies by task type.
Check a storage connection's filesβ
curl -X POST -H "Authorization: Bearer $TOKEN" \
"$HOST/api/storages/health/gcs/12"
gcs, s3 or azure, then the storage id. Returns the
file check:
status (good, warning, problem, unknown), a summary, the
findings with what to do about each, and the files sampled. Project
managers only.
Measure the media files of tasksβ
curl -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"selectedItems": {"all": true, "excluded": []}}' \
"$HOST/api/dm/actions?id=measure_files&project=42"
Fills meta.media (kind, size, duration, heavy) on each task and
answers with how many were measured and how many are left; call it
again until left is 0.
Where a bucket file isβ
A task's bucket file is fetched through
/tasks/<id>/resolve/?fileuri=<base64 of the address>, which redirects
to a signed address at the provider. Add &location=1 to get
{"url": "β¦"} instead of the redirect: use it when your request carries
an Authorization header, so that header is not sent on to the provider.
Rate limitsβ
- Authenticated endpoints: no explicit per-second cap; the
practical ceiling is your deployment's concurrency limit. If
you're bulk-loading thousands of tasks, batch into groups of
~500-1000 tasks per
POSTrather than one request per task. - Anonymous auth endpoints (
/api/auth/forgot-password,/api/auth/account-status,/api/auth/resend-verification): exponential per-(IP, email) backoff. First retry 15-60s, doubles per attempt, caps at 30 min.
Error formatβ
4xx and 5xx responses return JSON of the form:
{
"id": "5f2a9c1e-β¦",
"status_code": 400,
"version": "0.1.0",
"detail": "Human-readable detail",
"exc_info": null
}
Validation failures add a validation_errors object keyed by field
(with detail set to "Validation error"); some permission errors
add a machine-readable error_code. id is a unique exception ID
you can quote when reporting problems.
Exception: the anonymous auth endpoints' 429 responses use
{"error", "code": "rate_limited", "retry_after", "attempts"}
instead β see Rate limits.
What's nextβ
- Python SDK β β install + usage walkthrough
- Task format β β exact shape of the JSON tasks expect
- Cloud storage β β GCS / S3 / Azure as task sources instead of HTTP upload
- FAQ β API + integrations β token rotation, rate limits, common errors