Docs
View as Markdown

Labels

include=labels and label= add small, structured judgments to every row a list endpoint returns: is this comment a question, is it a complaint, how positive is it, is the person asking to buy. You keep the endpoint's normal answer and get the judgments next to each row, in computed.labels.

Labels are signals, not verdicts. Each one is a level or a probability with a score, and you pick your own cut-off. Nothing else in https://api.onekey.dev is produced by a language model; this feature, relevance and themes are the only places one is involved, and the response says which method produced them.

When to use it#

  • You pull the comments or replies under a post and want to find the questions, the complaints and the buying intent without reading them all.
  • You want a sentiment number per row to sort or chart, and you are happy to choose the threshold yourself.
  • You can live with a judgment being missing on a row: a row that could not be judged says so with null and is never guessed.

Labels are extra work on top of the endpoint, so they are metered separately. See AI pricing.

The request#

Add include=labels to any endpoint that accepts it. This judges every comment under a Hacker News story with that endpoint's default presets:

curl "https://api.onekey.dev/v1/hackernews/item_comments?id=8863&include=labels" \
  -H "x-api-key: YOUR_API_KEY"

To choose the presets yourself, name them with label=. It implies include=labels, and fewer presets cost less:

shell
curl "https://api.onekey.dev/v1/hackernews/item_comments?id=8863&label=question,complaint" \
  -H "x-api-key: YOUR_API_KEY"
ParameterValuesWhat it does
include=labelsflagJudge every row with the endpoint's default presets (below).
label=sentiment,questioncomma list, at most 5Judge exactly these presets. Implies include=labels. An unknown name is a free 422 INVALID_LABEL.
strict=truetrue (default) or falseWhat happens when nothing at all could be judged. See When nothing could be judged.

These combine with the other enrichment parameters on the same call: relevance= and themes=.

The presets#

PresetValue per rowReads as
sentiment{level, score_0_1, confidence}level is 1 (very negative) to 5 (very positive); score_0_1 is the same on a 0 to 1 scale.
question{p}Probability the text is asking a question.
purchase_intent{p}Probability the person is looking to buy or asking where to buy.
complaint{p}Probability the text is a complaint.
issue{topic, confidence}The main problem raised. topic is one of shipping, pricing, billing, quality, support, usability, performance, content, account, other or none.

p and confidence are numbers from 0 to 1. There is never a bare is_complaint: true: you decide where the line is.

Defaults by row kind#

Without label=, the presets depend on what a row is:

Rows areDefault presets
Commentssentiment, question, complaint, issue
Postssentiment, question, purchase_intent
Reviewssentiment, complaint, issue, purchase_intent
Search resultssentiment, question

Which endpoints accept it#

31 list endpoints on 11 platforms accept enrichment today, plus multi-platform search (per platform) and batch items, which take the same parameters as a single call. The table is generated from each endpoint's own declaration, so it is always current; features.ai in GET /v1/platforms and x-ai in /openapi.json mark the same endpoints. The judged text is the field named in the third column: for a search result that is the title, for a post the title and body together.

EndpointRows areJudged textDefault presetsrelevance
bluesky.post_repliesCommentscontent.textsentiment, question, complaint, issueno
bluesky.postsPostscontent.textsentiment, question, purchase_intentno
bluesky.searchSearch resultscontent.textsentiment, questionyes
devto.articles_by_tagPostscontent.title + content.textsentiment, question, purchase_intentno
devto.commentsCommentscontent.textsentiment, question, complaint, issueno
devto.searchSearch resultscontent.title + content.textsentiment, questionyes
github.issue_commentsCommentscontent.textsentiment, question, complaint, issueno
github.issuesPostscontent.title + content.textsentiment, question, purchase_intentno
github.search_issuesPostscontent.title + content.textsentiment, question, purchase_intentno
gitlab.issuesPostscontent.title + content.textsentiment, question, purchase_intentno
gitlab.searchSearch resultscontent.title + content.textsentiment, questionyes
hackernews.author_commentsCommentscontent.textsentiment, question, complaint, issueno
hackernews.item_commentsCommentscontent.textsentiment, question, complaint, issueno
hackernews.searchSearch resultscontent.titlesentiment, questionyes
hackernews.search_commentsCommentscontent.textsentiment, question, complaint, issueno
hackernews.story_commentsCommentscontent.textsentiment, question, complaint, issueno
lemmy.commentsCommentscontent.textsentiment, question, complaint, issueno
lemmy.search_commentsCommentscontent.textsentiment, question, complaint, issueno
lemmy.search_postsPostscontent.title + content.textsentiment, question, purchase_intentyes
lobsters.story_commentsCommentscontent.textsentiment, question, complaint, issueno
mastodon.status_context_repliesCommentscontent.textsentiment, question, complaint, issueno
mastodon.timeline_tagPostscontent.textsentiment, question, purchase_intentno
reddit.post_commentsCommentscontent.textsentiment, question, complaint, issueno
reddit.search_postsPostscontent.title + content.textsentiment, question, purchase_intentyes
reddit.user_commentsCommentscontent.textsentiment, question, complaint, issueno
stackexchange.answersPostscontent.textsentiment, question, purchase_intentno
stackexchange.commentsCommentscontent.textsentiment, question, complaint, issueno
stackexchange.questionsPostscontent.title + content.textsentiment, question, purchase_intentno
stackexchange.searchSearch resultscontent.titlesentiment, questionyes
youtube.commentsCommentscontent.textsentiment, question, complaint, issueno
youtube.video_commentsCommentscontent.textsentiment, question, complaint, issueno

Any enrichment parameter on another endpoint is a free 422 INVALID_PARAMS, and so is one on /v1/creator/card. The last column says whether the endpoint is a search, where relevance= is also allowed.

The response#

Each row gets computed.labels, and one labels summary block sits at the top level next to data. This is a recorded call on the pre-release test provider (rows shortened; methodology_version carries the test provider's method id, which marks that provider, see below):

json
{
  "success": true,
  "data": [
    {
      "content": { "id": "8869", "text": "This has great potential! Only suggestion I would have is go slower on the demo…" },
      "author": { "username": "…" },
      "computed": {
        "labels": {
          "complaint": { "p": 0.05 },
          "issue": { "topic": "usability", "confidence": 0.5 },
          "question": { "p": 0.05 },
          "sentiment": { "level": 4, "score_0_1": 0.7, "confidence": 0.95 }
        }
      }
    }
  ],
  "labels": {
    "presets": ["complaint", "issue", "question", "sentiment"],
    "mode": "sync",
    "status": "complete",
    "rows": 32,
    "labelled": 32,
    "cached": 0,
    "skipped": 0,
    "unjudged": 0,
    "pending": 0,
    "dropped": 0,
    "extra_credits": 60.225,
    "methodology_version": "fake/1"
  },
  "metadata": { "platform": "hackernews", "endpoint": "item_comments", "cached": false, "credits_charged": 65.225, "extra_credits": 60.225, "…": "…" },
  "error": null
}
FieldMeaning
presetsThe presets that were judged, in the order returned.
modeAlways sync today: the call waits for the judgments.
statuscomplete (every row that could be judged was), partial (some were not) or unavailable (none were).
rowsHow many rows the endpoint returned.
labelledRows that have a judgment, whether it was fresh or reused.
cachedOf those, judgments reused from an earlier identical text. They cost nothing.
skippedRows with nothing to judge: no text, not text, or shorter than 3 characters. Their computed.labels is null.
unjudgedRows that had text but were not judged, for the reason given. Their presets are null.
pendingAlways 0. Reserved for a future asynchronous mode.
droppedAlways 0 for labels. Only relevance filtering drops rows.
extra_creditsWhat this step cost, on top of the endpoint's price. See AI pricing.
methodology_versionWhich method produced the judgments.
reasonOnly when status is not complete. One of request_cap, account_cap, global_cap, kill_switch, provider_off, provider_unavailable, invalid_output.

metadata.extra_credits is the sum of every enrichment step in the call, and metadata.credits_charged already includes it.

How to read it honestly#

  • Signals, not verdicts. A question.p of 0.7 is a probability from a model, not a fact about the comment. Choose the cut-off that fits your use, and spot-check rows near it.
  • null means not judged, never "no". A skipped row has computed.labels: null. A row that had text but was not judged has {preset: null} for each preset. Treat both as unknown, not as a negative. Nothing is guessed to fill a gap.
  • Count what was judged. rows, labelled, skipped and unjudged always add up the story. If labelled is smaller than rows, say so in whatever you build on top.
  • Same text, same judgment. Identical texts (ignoring case and extra whitespace) are judged once and share the result. Texts longer than 2,000 characters are clipped before judging.
  • methodology_version names the method. It is on every enrichment block. The value in the examples on these pages is the test provider's method id, the pre-release test provider: a deterministic keyword heuristic that exists so the feature can be built and tested end to end. It is not a measure of quality, and it is wrong on sarcasm, negation and non-English text. The value will change when a production method is in use, and judgments made under one version are never served as another.
  • Limits per request. At most 200 rows of one request are judged; the rest stay null with reason: request_cap and status: partial, and you are charged only for the rows judged. Per-account daily allowances also apply; see AI pricing.

When nothing could be judged#

strict decides what happens when not a single row could be judged, because enrichment is off for the account, the provider is unavailable or a daily allowance is used up.

  • strict=true (the default): the call fails, so you never mistake unjudged rows for judged ones. It is AI_UNAVAILABLE (503) or AI_BUDGET_EXCEEDED (429). When enrichment is not enabled at all, this is refused before the data is fetched, so it costs nothing.
  • strict=false: you get the rows back, computed.labels is null on each, labels.status is unavailable with a reason, and there is no extra charge. Use it when the rows matter more than the judgments.

A partial result is never an error, whichever you choose: you get what was judged, status: partial and the reason.

Batch, multi-platform search and streams#

  • Batch. Put the parameters in an item's params. The summary blocks sit on that item's row, not at the top of the response.
  • Multi-platform search. include=labels and label= on /v1/search/multi apply to every platform whose search endpoint accepts them. Each platform's row carries its own labels block and extra_credits, and data.labels adds them up (legs, the counters, and unsupported: the platforms whose search has no enrichment, whose rows come back untouched). data.labels.status is complete only when every platform is.
  • Streams. With Accept: text/event-stream the platform frames arrive first, without the enrichment lines in their credits_charged, then fused, then one enrich frame per step, then done with the full total. See Streaming.
  • Idempotency. A replay with the same Idempotency-Key returns the stored body, labels included, at 0 credits. Changing any enrichment parameter makes it a different request (IDEMPOTENCY_KEY_REUSED).

What it costs#

The endpoint's own price, plus a separate charge for each step that judged something. It depends on how many rows are judged and how many presets you ask for, and it is reserved before judging and settled to what was actually used. Cached judgments, skipped rows and unjudged rows are free. Details and the measured examples are on AI pricing.

Errors it can return#

StatusTypeWhen
422INVALID_LABELlabel= named a preset that does not exist. Free.
422INVALID_PARAMSAn enrichment parameter on an endpoint that does not accept it, or a malformed include. Free.
429AI_BUDGET_EXCEEDEDNothing could be judged because today's allowance is used up and strict=true. Wait Retry-After.
503AI_UNAVAILABLENothing could be judged because enrichment is off or unavailable and strict=true.
402INSUFFICIENT_CREDITS, KEY_BUDGET_EXCEEDEDThe reservation for the judgments cannot be covered.

See Errors for the full list.