Targeting
Restrict a job to annotators matching demographic and geographic criteria.
annotator_filter is an optional field on the POST /jobs request body (and on any survey plan). It gates which annotators are eligible to receive the job's tasks, based on demographic and geographic attributes recorded for each annotator.
Leave it out and the job serves to the whole eligible pool. Set it and only annotators who match every criterion you specify will see the tasks.
How matching works
annotator_filter is a map of dimension → allowed value(s):
{
"annotator_filter": {
"country": ["US", "CA"],
"gender": ["female"],
"age_range": ["25-34", "35-44"]
}
}- Different keys are AND-ed. The example above serves only to annotators who are in the US or Canada, and female, and aged 25–44.
- Multiple values within one key are OR-ed.
"country": ["US", "CA"]matches annotators in either country. - Matching is exact. Values are compared literally — there is no fuzzy matching, aliasing, or hierarchy expansion. Use the discovery endpoints to find the exact strings the pool uses.
- Annotators without a profiled value are excluded. If a required dimension isn't known for an annotator, they don't match — a filter never admits an unknown.
Dimensions
Only the dimensions below are accepted. Geographic values (country, region, city, postal, income) come from an approximate, IP-derived location; demographic values (gender, age) come from what the annotator has told us.
Geography
| Field | Type | Description |
|---|---|---|
country | string[] | ISO 3166-1 alpha-2 code. Exact and case-sensitive (uppercase), e.g. ["US", "GB"]. This is the preferred way to target a country. |
country_name | string[] | Full country display name, matched exactly (e.g. ["United States"]). Prefer `country` (the code) — it avoids naming ambiguity. |
region | string[] | First-level subdivision label (state / province / region), matched exactly. There is no hierarchy or alias resolution — pass the exact label the discovery endpoint returns. |
city | string[] | Exact city / locality label. Note: residents of a large metro are often labelled by their neighbourhood or suburb rather than the metro name, so a bare city filter can silently miss part of a metro-wide audience. For metro-wide reach, prefer `region` or `postal`. |
postal | string[] | Postal / ZIP area, matched exactly. This is an approximate area inference from the annotator's connection — not a verified home address. |
median_household_income | range object | US-only. Filters on the median household income of the annotator's postal AREA — an area statistic (USD), not the person's own income. Takes a range object, e.g. {"gte": 75000} or {"gte": 50000, "lte": 150000}. Pair it with country: ["US"]. |
median_household_income accepts the operators gte, lte, gt, and lt (integers). All other geography dimensions take a non-empty list of strings.
Demographics
| Field | Type | Description |
|---|---|---|
gender | string[] | One or more of: "male", "female", "non_binary", "prefer_not_to_say". Exact match. |
age_range | string[] | One or more of: "18-24", "25-34", "35-44", "45-54", "55-64", "65+". Derived from the annotator's self-reported age with an 18+ floor — under-18 profiles never match any band. |
The platform may expose additional custom demographic dimensions beyond the built-ins. When present, these are filterable by their dimension key just like the built-ins; GET /annotators/filters is the authoritative list of what is currently available.
Example
{
"name": "us-parents-preference",
"task_type": "comparison",
"instruction": "Which packaging looks more trustworthy?",
"datapoints": [
{
"media": {
"candidates": [
{ "url": "dp://1fae39d2c7b5/pack_a.png", "type": "image" },
{ "url": "dp://84b1f6ea3d02/pack_b.png", "type": "image" }
]
}
}
],
"annotator_filter": {
"country": ["US"],
"age_range": ["25-34", "35-44"],
"median_household_income": { "gte": 60000 }
}
}Discovering valid values
Because matching is exact, guessing at labels is fragile. Three read-only endpoints let you enumerate exactly what the pool supports. All are prefixed with https://api.trydatapoint.com/data-labelling/v1 and require an X-API-Key header.
These endpoints report which values exist. They never return pool counts — the optional min_count parameter lets you suppress sparsely-populated values, but no threshold is applied by default.
List available dimensions
Returns the catalog of dimensions the pool is indexed on and how to query each one; the Dimensions table above is authoritative for what annotator_filter accepts. Hierarchical dimensions advertise their levels; enum dimensions advertise their inline values.
Response (200 OK, trimmed for brevity)
{
"filters": [
{
"id": "geography",
"label": "Location",
"type": "hierarchical",
"supports_search": true,
"levels": [
{ "key": "country", "label": "Country" },
{ "key": "region", "label": "State / Region" },
{ "key": "city", "label": "City" },
{ "key": "postal", "label": "Postal code" }
]
},
{
"id": "gender",
"label": "Gender",
"type": "enum",
"supports_search": false,
"values": [
{ "value": "male", "label": "Male" },
{ "value": "female", "label": "Female" },
{ "value": "non_binary", "label": "Non-binary" },
{ "value": "prefer_not_to_say", "label": "Prefer not to say" }
]
},
{
"id": "age_range",
"label": "Age range",
"type": "enum_ordered",
"supports_search": false,
"values": [
{ "value": "18-24", "label": null },
{ "value": "25-34", "label": null },
{ "value": "35-44", "label": null },
{ "value": "45-54", "label": null },
{ "value": "55-64", "label": null },
{ "value": "65+", "label": null }
]
}
]
}The location levels — country, region, city, postal — map one-to-one onto geography keys of the same name in annotator_filter. country_name (matched against the country level's display label) and median_household_income (a numeric range filter) are not enumerated here.
Browse a dimension's values
Lists the distinct values at one level of a dimension. For the hierarchical geography dimension, parent_path selects the level: absent → countries, US → that country's regions, US/California → its cities, and so on. Segments are slash-separated and URL-encoded.
Query parameters
| Field | Type | Description |
|---|---|---|
parent_path | string | Hierarchical filters only. Slash-separated path selecting the level to list (e.g. "US" for US regions). Omit for the top level. |
min_count | integerdefault: 0 | Omit values whose annotator population falls below this threshold. Raise it to skip sparsely-populated values you'd never want to target on their own. |
offset | integerdefault: 0 | Pagination offset. |
limit | integerdefault: 100 | Page size (1–1000). |
Response (200 OK)
{
"filter_id": "geography",
"level": "region",
"parent_path": "US",
"values": [
{ "value": "California", "label": "California", "has_children": true },
{ "value": "New York", "label": "New York", "has_children": true }
],
"has_more": false
}Each row's value is the exact string to pass in annotator_filter (for the region level, into the region key). has_children is true only when at least one descendant also clears min_count.
Search across a dimension
Typeahead across every level of a hierarchical dimension — useful for resolving "what's the exact label for Toronto?" without walking the tree.
Query parameters
| Field | Type | Description |
|---|---|---|
q* | string | Search text (1–100 chars). |
min_count | integerdefault: 0 | Omit matches whose annotator population falls below this threshold. |
limit | integerdefault: 10 | Maximum matches (1–50). |
Response (200 OK)
{
"filter_id": "geography",
"q": "toron",
"matches": [
{
"level": "city",
"value": "Toronto",
"label": "Toronto",
"path": [
{ "key": "country", "value": "CA", "label": "Canada" },
{ "key": "region", "value": "Ontario", "label": "Ontario" },
{ "key": "city", "value": "Toronto", "label": "Toronto" }
],
"display_path": "Toronto, Ontario, Canada"
}
]
}Each match carries its full path back to the root level, so you can see which country and region a city belongs to before targeting it. display_path is that same path rendered deepest-first — the inverse of path.
Error cases
| Status | Cause |
|---|---|
400 | Unknown filter_id; a malformed parent_path (e.g. empty path segments); a parent_path passed to a non-hierarchical dimension; a parent_path deeper than the dimension's levels; or a search on a dimension that doesn't support search. |
422 | A query parameter outside its accepted range. |
Results breakdowns
Demographic breakdowns (gender, age) are available only for dimensions the job targeted. Country is always included. Add a demographic dimension (gender, age_range, or a custom dimension) to annotator_filter at creation time to make per-segment breakdowns available for that job's results afterwards. Geographic dimensions other than country do not produce breakdowns.
Next
- Jobs: where
annotator_filteris submitted - Billing: targeting surcharges
- Survey Planner: natural-language job generation