rstags

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.

Header
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.

Example request
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

FieldTypeNotes
contentstring, requiredThe text to tag.
tagsstring[], requiredThe pool to evaluate against. 1 to 500 tags. Duplicates collapse and are billed once.
rulesstring, optionalOne rule per line. A rule naming a tag is attached to that tag’s question.
thresholdnumber, optionalScore a tag must reach to be selected. 0 to 1.
max_tagsnumber, optionalMost tags to return. Defaults to 5.
fallback_tagstring, optionalApplied 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.
truncateboolean, optionalAllow oversized content to be cut rather than rejected. Flagged in the response.
timeout_msnumber, optionalPer attempt timeout. Clamped to 30000.
metadataobject, optionalEchoed back untouched. Up to 1 KB.

Response

200 OK
{
  "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_tag is 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_large naming both numbers.
  • Content is never truncated silently. Oversized input is rejected unless you pass truncate: true, and then the response says content_truncated: true.

Errors

Every error uses the same envelope. 4xx means the request needs changing, 5xx means we do.

Error
{
  "error": {
    "type": "invalid_request_error",
    "code": "pool_too_large",
    "message": "A pool may contain at most 500 tags; this request sent 640.",
    "param": "tags"
  }
}
StatusCodeMeaning
400invalid_requestThe body failed validation. `param` names the field.
401invalid_keyMissing or unknown API key.
403key_revokedThe key was revoked or has expired.
402insufficient_creditsNot enough balance for this pool. Nothing was evaluated.
413pool_too_largeMore than 500 tags.
413content_too_largeContent over 32,000 characters.
413request_too_largePool size multiplied by content length is over the limit.
429rate_limitedToo many requests. Retry after the header says.
503upstream_unavailableThe 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.

Get an API key