Rubric Classify
curl --request POST \
--url https://api.trynawa.com/v1/rubric/classifyimport requests
url = "https://api.trynawa.com/v1/rubric/classify"
response = requests.post(url)
print(response.text)const options = {method: 'POST'};
fetch('https://api.trynawa.com/v1/rubric/classify', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));Endpoints
Rubric Classify
Classify text against your own categories. You define the rubric, the response returns your category names.
POST
/
v1
/
rubric
/
classify
Rubric Classify
curl --request POST \
--url https://api.trynawa.com/v1/rubric/classifyimport requests
url = "https://api.trynawa.com/v1/rubric/classify"
response = requests.post(url)
print(response.text)const options = {method: 'POST'};
fetch('https://api.trynawa.com/v1/rubric/classify', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));Classify text against a rubric you define. Unlike
/v1/classify, which returns NAWA’s fixed intent taxonomy, this endpoint returns your category names, exactly as you spelled them.
Cost: $0.003 per request (3 credits). Sandbox keys get 100 free requests per account, counted across every sandbox key you have ever created, including revoked and expired ones. This endpoint does not use the semantic cache, so every request is a live model call.
Request
Body parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
text | string | Yes | The text to classify. Must be non-empty. |
rubric | object | Yes | Your rubric definition. |
rubric.categories | object[] | Yes | Between 1 and 20 categories. Each is an object with a name, not a bare string. |
rubric.categories[].name | string | Yes | The category name. Returned verbatim, so use whatever spelling and language you want, including Arabic. |
rubric.categories[].description | string | No | What the category means. Improves accuracy noticeably on ambiguous categories. |
rubric.multi_label | boolean | No | true returns every matching category, false returns at most one, the best match. Default false. |
rubric.confidence_threshold | number | No | Categories scored below this are omitted. Clamped to 0–1. Default 0.5. |
Query parameters
| Parameter | Type | Description |
|---|---|---|
provider | string | Force a provider for A/B testing: claude or gemini. Default: chosen by language routing. |
Example request
curl -X POST https://api.trynawa.com/v1/rubric/classify \
-H "Authorization: Bearer nawa_test_sk_xxx" \
-H "Content-Type: application/json" \
-d '{
"text": "والله يا أخوي هذا أحسن فيديو شفته، بس متى ينزل الجزء الثاني؟",
"rubric": {
"categories": [
{ "name": "مدح", "description": "ثناء أو إعجاب بالمحتوى" },
{ "name": "سؤال", "description": "سؤال يحتاج إجابة من صاحب القناة" },
{ "name": "طلب محتوى", "description": "اقتراح أو طلب فيديو جديد" },
{ "name": "شكوى", "description": "اعتراض أو نقد سلبي" }
],
"multi_label": true,
"confidence_threshold": 0.5
}
}'
const { data, error } = await nawa.rubric.classify({
text: 'والله يا أخوي هذا أحسن فيديو شفته، بس متى ينزل الجزء الثاني؟',
rubric: {
categories: [
{ name: 'مدح', description: 'ثناء أو إعجاب بالمحتوى' },
{ name: 'سؤال', description: 'سؤال يحتاج إجابة من صاحب القناة' },
{ name: 'طلب محتوى', description: 'اقتراح أو طلب فيديو جديد' },
{ name: 'شكوى', description: 'اعتراض أو نقد سلبي' },
],
multi_label: true,
confidence_threshold: 0.5,
},
})
result = nawa.rubric.classify(
text="والله يا أخوي هذا أحسن فيديو شفته، بس متى ينزل الجزء الثاني؟",
rubric={
"categories": [
{"name": "مدح", "description": "ثناء أو إعجاب بالمحتوى"},
{"name": "سؤال", "description": "سؤال يحتاج إجابة من صاحب القناة"},
{"name": "طلب محتوى", "description": "اقتراح أو طلب فيديو جديد"},
{"name": "شكوى", "description": "اعتراض أو نقد سلبي"},
],
"multi_label": True,
"confidence_threshold": 0.5,
},
)
Response
Success response (200)
{
"result": {
"id": "rcl_nw_kqh91g635tr3",
"object": "rubric_classification",
"categories": [
{ "name": "مدح", "confidence": 0.95 },
{ "name": "سؤال", "confidence": 0.9 },
{ "name": "طلب محتوى", "confidence": 0.9 }
],
"multi_label": true,
"language": "ar",
"provider": "claude",
"model": "claude-sonnet-5",
"fallback_used": false,
"tokens_used": 564,
"cost_usd": 0.003,
"credits_used": 3
},
"success": true,
"errors": [],
"request_id": "req_nw_5pklglnkwwtc"
}
Result fields
| Field | Type | Description |
|---|---|---|
id | string | Classification ID in rcl_nw_xxx format |
object | string | Always "rubric_classification" |
categories | object[] | Matching categories, highest confidence first. Names are returned exactly as you supplied them. |
categories[].name | string | One of your category names, character for character |
categories[].confidence | number | 0–1, rounded to two decimals |
multi_label | boolean | Echoes the value you sent |
language | string | Detected language of the input: ar, en, or mixed |
provider | string | Provider that produced this result: claude or gemini |
model | string | The model that actually ran, e.g. claude-sonnet-5 |
fallback_used | boolean | true if the primary provider failed and a fallback answered |
tokens_used | number | Input plus output tokens for the call |
cost_usd | number | Always 0.003 for this endpoint |
credits_used | integer | Always 3 for this endpoint |
An empty
categories array is a valid answer. It means nothing in your rubric scored above the threshold. The endpoint will never substitute a generic category such as other to avoid returning an empty list, and it will never return a name that was not in your rubric.Response headers
| Header | Description |
|---|---|
X-Request-Id | Unique request identifier. Quote this to support when reporting an issue. |
X-NAWA-Provider | Which provider produced the response. |
X-NAWA-Latency | Server-side processing time in milliseconds. |
Errors
| Status | Code | Meaning |
|---|---|---|
| 400 | missing_field | text or rubric absent, or rubric.categories empty |
| 400 | invalid_field | A category has no non-empty name, more than 20 categories were sent, or provider is not claude or gemini |
| 402 | insufficient_credits | Live keys only. Sandbox keys use the 100-request account cap instead. |
| 502 | rubric_unparseable | The model did not return a usable classification, including after one automatic retry. No credits are charged. Retry, and if it persists, shorten your category descriptions. |
Categories must be objects.
"categories": ["praise", "question"] returns a 400: each entry needs a name field. This was documented incorrectly before 2026-09-13.Was this page helpful?