Docs
The tagging API
One endpoint. Send content and a pool of tags, get back the tags that fit and a score for every tag you sent.
Authentication
Create a key in the dashboard and send it as a bearer token. Keys look like rst_sk_... and are shown once, at creation.
Authorization: Bearer rst_sk_...X-Api-Key works too, if a bearer header is awkward in your stack.
POST /api/v1/tag
The only endpoint you need. Every call is independent: there is no project to create and no state to keep.
curl -X POST https://rstags.com/api/v1/tag \
-H "Authorization: Bearer rst_sk_..." \
-H "Content-Type: application/json" \
-d '{
"content": "My card was declined twice renewing the Pro plan.",
"tags": ["billing", "bug", "churn-risk", "feature-request"]
}'Request fields
| Field | Type | Notes |
|---|---|---|
| content | string, required | The text to tag. |
| tags | string[], required | The pool to evaluate against. 1 to 500 tags. Duplicates collapse and are billed once. |
| rules | string, optional | One rule per line. A rule naming a tag is attached to that tag’s question. |
| threshold | number, optional | Score a tag must reach to be selected. 0 to 1. |
| max_tags | number, optional | Most tags to return. Defaults to 5. |
| fallback_tag | string, optional | Applied only when nothing clears the threshold. Never asked as a question, never billed. |
| scores | "all" | "selected" | "none" | How much of the score set to return. Defaults to all. |
| truncate | boolean, optional | Allow oversized content to be cut rather than rejected. Flagged in the response. |
| timeout_ms | number, optional | Per attempt timeout. Clamped to 30000. |
| metadata | object, optional | Echoed back untouched. Up to 1 KB. |
Response
{
"object": "tagging",
"tags": ["billing", "churn-risk"],
"scores": [
{ "tag": "billing", "score": 0.94 },
{ "tag": "churn-risk", "score": 0.78 },
{ "tag": "bug", "score": 0.11 },
{ "tag": "feature-request", "score": 0.03 }
],
"model": "typesafe-ai/jev",
"threshold": 0.7,
"usage": { "decisions": 4, "balance": 9996 }
}scores holds every tag that was asked about, best first, whether or not it was selected. Store them and you can raise or lower your threshold later without calling the API again.
What you are billed
One decision is one tag evaluated against one piece of content. A request with a 50 tag pool costs 50 decisions, because each tag is a separate question put to the model.
- The first 10,000 decisions are free on every account, then it is $50 per 100,000 tags.
- Duplicate tags collapse, so they are billed once.
- The
fallback_tagis never asked as a question and is never billed. - A failed request bills nothing. If the model is unavailable you get a 503 and your balance is untouched.
Every response carries usage.decisions and your remaining balance, so the charge is auditable from the response alone.
Limits
- Up to 500 tags in a pool.
- Up to 32,000 characters of content by default.
- Pool size multiplied by content length is also capped, because the model re-reads your content once per batch of 50 tags. A large pool over a long document is rejected with
request_too_largenaming both numbers. - Content is never truncated silently. Oversized input is rejected unless you pass
truncate: true, and then the response sayscontent_truncated: true.
Errors
Every error uses the same envelope. 4xx means the request needs changing, 5xx means we do.
{
"error": {
"type": "invalid_request_error",
"code": "pool_too_large",
"message": "A pool may contain at most 500 tags; this request sent 640.",
"param": "tags"
}
}| Status | Code | Meaning |
|---|---|---|
| 400 | invalid_request | The body failed validation. `param` names the field. |
| 401 | invalid_key | Missing or unknown API key. |
| 403 | key_revoked | The key was revoked or has expired. |
| 402 | insufficient_credits | Not enough balance for this pool. Nothing was evaluated. |
| 413 | pool_too_large | More than 500 tags. |
| 413 | content_too_large | Content over 32,000 characters. |
| 413 | request_too_large | Pool size multiplied by content length is over the limit. |
| 429 | rate_limited | Too many requests. Retry after the header says. |
| 503 | upstream_unavailable | The model is unavailable. Nothing was billed. |
Retries and idempotency
Send an Idempotency-Key header and a retried request is charged once. Without one, every call is billed on its own, because we cannot tell a retry from a second look at the same ticket.
The model
Requests run on typesafe-ai/jev, an evaluation model that answers a closed question with a calibrated probability instead of generating text. That is why a tag outside your pool cannot come back, and why there is no output to parse or repair.
Your content is sent to the model to be scored and is not stored.
Ready to try it
The first 10,000 tags are free. Create a key and post to https://rstags.com/api/v1/tag.