Skills
Create your own skill from labelled examples and require raters to pass it before their votes count.
A skill is a set of questions with known answers. A job or stream that requires a skill gives each rater a short assessment from that set first, and only counts votes from raters whose score is above your threshold.
You can require a shared skill, or create your own from examples you have labelled yourself. Your own skills are private to your account. For how modes, scores, and qualification behave, see Skills and qualification.
Every endpoint below is under https://api.trydatapoint.com/data-labelling/v1, takes your API key in the X-API-Key header and counts against your key's rate limit.
Create a skill in three calls
POST /skillsto name the skill. You get back itsskillidentifier, which looks liketenant:<account id>:<name>.POST /mediato upload any images, videos or audio the examples use.POST /skills/{skill}/calibration-setswith 20 to 500 labelled examples.
The set is checked automatically and a draft skill becomes active in the same call. There is no manual review. You can then name the skill in skill_requirements on a job or a stream.
import requests
API = "https://api.trydatapoint.com/data-labelling/v1"
HEADERS = {"X-API-Key": "dp_..."}
skill = requests.post(
f"{API}/skills",
headers=HEADERS,
json={"name": "Artifact spotting", "description": "Finds the image with a rendering artifact."},
).json()["skill"]
# labelled: [(first_ref, second_ref, "image_1" or "image_2"), ...], refs from POST /media.
examples = [
{
"media": {
"candidates": [
{"url": first, "type": "image"},
{"url": second, "type": "image"},
]
},
"gold_submission": {"choice": answer},
"provenance": {"source": "Labelled in house", "license": "client-owned"},
}
for first, second, answer in labelled
]
created = requests.post(
f"{API}/skills/{skill}/calibration-sets",
headers=HEADERS,
json={
"name": "Artifacts v1",
"task_type": "comparison",
"instruction": "Which image has a visible artifact?",
"examples": examples,
},
)
created.raise_for_status()Create a skill
Request body
| Field | Type | Description |
|---|---|---|
name* | string (1–120) | Display name. Must be unique among your skills; a duplicate is a `409`. |
description | string (up to 2,000) | What the skill checks. |
Response (201 Created)
The skill object, with status: "draft". A draft skill can't be required yet; it becomes active when its first set is accepted.
Add labelled examples
Adds a set of examples to one of your skills. You can add more sets later; raters are assessed on examples drawn from all of the skill's active sets that can serve in the job's environment. Each rater sees each example at most once, so larger sets support more re-checks.
Request body
| Field | Type | Description |
|---|---|---|
name* | string (1–120) | Name of this set, unique among the skill's sets. |
task_type* | "comparison" | "multiple_choice" | How each example is asked. Other task types aren't supported for skills. |
instruction* | string (1–5,000) | The question raters see on every example. |
response_options | object | As on `POST /jobs`. Required for `multiple_choice` (`mode`). |
serving_environment | "all" | "sandbox" | "prod" | Where the examples can be served. Defaults to the environment of the API you call (`prod` on the production API). A `prod` job can use `prod` and `all` sets, a `sandbox` job `sandbox` and `all` sets, and an `all` job only `all` sets. Use `all` to try the skill in sandbox before going live. |
examples* | array (20–500 items) | The labelled examples. See below. |
examples[]
| Field | Type | Description |
|---|---|---|
media* | object | The same shape as a datapoint's `media` for the task type: `candidates` for comparison, `options` and optional `subjects` for multiple choice. |
context | string (up to 20,000) | Required for `multiple_choice`, where it is the question. For `comparison`, per-example text shown where the instruction has `{context}`. |
gold_submission* | object | The correct answer. See [Gold answers](#gold-answers). |
provenance* | object | Where the example came from: `source` (required), `license` (required) and an optional `uri`. For your own data, `"client-owned"` is a fine `license`. |
The media shapes are on the comparison and multiple choice pages.
Gold answers
| Task type | gold_submission | Notes |
|---|---|---|
comparison | {"choice": "image_1"} | The first entry in candidates is image_1, video_1 or audio_1 by its uploaded media type; the second ends in _2. Raters may see them in either order. Any id or type you set on a candidate is ignored. |
multiple_choice, mode: "single" | {"choice": "<option id>"} | An id from media.options. |
multiple_choice, mode: "multi" | {"choices": ["<option id>", ...]} | Every correct option. |
For multiple choice, vary which option is correct across the set: if one option is right for most examples, a rater who always picks it scores well without the skill. Each example's media and context must be distinct from every other example's.
A multiple-choice example with an image:
{
"context": "What is wrong with this image?",
"media": {
"subjects": [{ "url": "dp://b6a1cd3f1234/sample_017.png", "type": "image" }],
"options": [
{ "id": "hands", "text": "Malformed hands" },
{ "id": "text", "text": "Garbled text" },
{ "id": "none", "text": "Nothing" }
]
},
"gold_submission": { "choice": "hands" },
"provenance": { "source": "Labelled in house", "license": "client-owned" }
}Response (201 Created)
{
"set_id": "0b9f6c0e-3a7d-4f0e-9d1b-2f5f7f0c8a11",
"skill": "tenant:7c1e0d52-9b3a-4c1f-8e21-5a4d1f0b6c33:artifact-spotting",
"name": "Artifacts v1",
"status": "passed",
"active": true,
"item_count": 20,
"submitted_for_review": false
}Error cases
| Status | When |
|---|---|
400 | The skill isn't one of yours or is retired; the set's name is already used on the skill; an example fails the task type's validation, its answer isn't one of its candidates or options, or its media isn't an uploaded reference that passed content review; or examples repeat. The detail says which. |
422 | The body doesn't match the schema, such as fewer than 20 examples or an unknown field, or its text didn't pass content review (detail.code is content_blocked). |
503 | Skill checks or content review are temporarily unavailable. Nothing was stored; retry later, after Retry-After when it is sent. |
List skills
Returns { "skills": [...] }: every shared skill plus the ones your account created.
The skill object
{
"skill": "tenant:7c1e0d52-9b3a-4c1f-8e21-5a4d1f0b6c33:artifact-spotting",
"name": "Artifact spotting",
"description": "Finds the image with a rendering artifact.",
"scope": "client",
"status": "active",
"calibration_sets": 1,
"validated_sets": 1,
"active_items": 20,
"created_at": "2026-11-12T10:15:00+00:00"
}| Field | Type | Description |
|---|---|---|
skill | string | The identifier to pass in `skill_requirements`. It never changes, even if you rename the skill. |
scope | "platform" | "client" | `platform` is a shared skill, free to require. `client` is one your account created. |
status | "draft" | "active" | "paused" | "retired" | Only an `active` skill can be required. |
active_items | int | Examples raters can currently be assessed on. |
Update, pause or retire a skill
Takes any of name, description and status ("active", "paused" or "retired"), and returns the skill object. Setting active needs at least one accepted set, otherwise it is a 400. A paused skill stays paused when you add sets to it. A retired skill takes no new sets. A skill that isn't yours is a 404.
Pausing or retiring a skill stops every job and stream that requires it from serving new work, including to raters who already qualified, until you set it back to active. Those jobs stay active and report no error.
Require a skill on a job
Add these to the POST /jobs body:
| Field | Type | Description |
|---|---|---|
skill_requirements | array (1–10 items) | Each item has a `skill` identifier and `reject_at_or_below`, from 0 up to but not including 1. A vote counts only when the rater's score on every listed skill is strictly above its threshold. |
skill_gate_mode | "adaptive" | "enforced" | `adaptive` (the default) reuses a rater's existing score and rechecks it from time to time. `enforced` assesses every rater on each required skill for this job. |
skill_budget_credits | int | The most the job may spend on assessments of your own skills, reserved on top of the normal responses. Required when a listed skill is one you created and the job is charged, and must cover at least one assessment per such skill. Omit it, or send `0`, for shared skills and sandbox jobs; a non-zero budget there is a `400`. |
{
"name": "artifact-eval-run-3",
"task_type": "comparison",
"instruction": "Which image looks more realistic?",
"datapoints": [...],
"skill_requirements": [
{ "skill": "tenant:7c1e0d52-9b3a-4c1f-8e21-5a4d1f0b6c33:artifact-spotting", "reject_at_or_below": 0.6 }
],
"skill_budget_credits": 5000
}A rater's score on a skill runs from 0 to 1, reflects their recent graded answers on it, and is shared by every job that requires the skill. In adaptive mode, a rater who falls below your threshold can be assessed again later. In enforced mode, each rater is assessed on every required skill for the job, and a rater who doesn't qualify isn't assessed again for that job.
Votes from raters who don't pass are left out of results and billing. A datapoint keeps being served until enough votes count, up to a serving limit, so if many raters fail it can close with fewer than max_responses_per_datapoint. An unknown, inactive or empty skill is a 400 naming it, and while skill checks are temporarily unavailable a job with skills is a 503.
To raise the assessment budget on a running job, POST /jobs/{job_id}/skill-budget with { "skill_budget_credits": <new total> }. It can only go up; a 402 means your balance doesn't cover the increase. See Billing.
Returns job_id, enabled, mode, and a requirements array. Each requirement has the skill, its reject_at_or_below, and counts of completed_assessments, billed_assessments, passed_assessments and failed_assessments, plus skill_cost_credits. The passed and failed counts record enforced-mode threshold checks, not answer accuracy, so they stay at 0 on adaptive jobs; use the performance report below for accuracy.
What assessments cost
- Shared skills are free.
- Each answered assessment of a skill you created is charged once at your account's assessment rate, whether the rater passes or not. The rate is fixed when the job or stream is created;
GET /billing/pricingreturns your current rate. - In
adaptivemode, a rater who already has a passing score usually starts real work without a new assessment, but is re-checked from time to time, and each re-check is charged. Inenforcedmode, every rater is assessed on every job. - Unanswered assessments are free, and so are answers that arrive after the assessment expires.
- Sandbox jobs have a rate of 0.
- When the job settles, unused assessment budget is returned.
Billing covers budgets and spend reports.
Check how a skill is doing
Returns the skill's status and counts, and a validation array listing each set with its set_id, status, whether it is active, its item_count and active_items, and a summary of the automated checks. Works for shared skills too.
Reports how raters did on the skill across your jobs: assessments assigned and scored, accuracy, qualification pass rate and credits charged, in total and per set or per question.
Query parameters
| Field | Type | Description |
|---|---|---|
window_days | int (1–90) | Days to report on, ending at `end_at`. Default 30. |
end_at | datetime | End of the window, with a timezone. Defaults to now. |
job_id | string | Only assessments served for this job. |
set_id | uuid | Only this set. |
mode | "adaptive" | "enforced" | Only jobs in this gate mode. |
serving_environment | "all" | "sandbox" | "prod" | Only jobs with this `serving_environment` setting. |
group_by | "set" | "question" | Breakdown level. Default `set`. Use `question` to find examples most raters get wrong, which often means the label or the question is unclear. |
page, page_size | int | Paging over the breakdown; `page_size` up to 100, default 25. |
Comparison and multiple-choice answers are graded right or wrong, so accuracy is the share answered correctly. The qualification pass rate counts enforced-mode threshold checks only, and is null without them; null never means 0%.
An unknown skill, job or set is a 404, an invalid filter or a future end_at is a 422, and a report that takes too long is a 503; retry with a shorter window or a job_id or set_id filter.