# Labels (/docs/labels) SocialCrawl labels on comments, posts, reviews and search results. On by default, free, and switched off with one parameter. Comment, post, review and search lists come back with SocialCrawl labels attached: how a comment feels, whether a post is a paid promotion, which problem a review reports, whether a search result is about what you searched for. They are on by default, they add no credits, and every field you already read stays exactly as it was. ```bash title="cURL" curl "https://www.socialcrawl.dev/v1/tiktok/post/comments?url=https://www.tiktok.com/@stoolpresidente/video/7623818255903329566" \ -H "x-api-key: YOUR_API_KEY" ``` ```json title="Response" { "success": true, "platform": "tiktok", "endpoint": "/v1/tiktok/post/comments", "data": { "items": [ { "comment": { "text": "Ordering it for my 13 year old ❤️" }, "computed": { "language": "en", "labels": { "sentiment": { "level": 4, "score_0_1": 0.91, "confidence": 0.71 }, "question": { "p": 0.03 }, "purchase_intent": { "p": 0.98 }, "complaint": { "p": 0.02 } } } } ], "labels": { "presets": ["sentiment", "question", "purchase_intent", "complaint"], "mode": "default", "status": "complete", "rows": 50, "labelled": 50, "cached": 0, "skipped": 0, "unjudged": 0, "pending": 0, "pending_ids": [], "dropped": 0, "dropped_ids": [], "extra_credits": 0, "methodology_version": "labels/1" } }, "credits_used": 1, "cached": false } ``` The response is trimmed to one comment, and the labels shown are the ones that comment received on a recorded run. The page cost 1 credit, the same as it did before labels existed. Each comment gained `computed.labels`, and `data.labels` reports how many rows were judged, how many are still waiting, and what the labels cost you (nothing: `extra_credits: 0`). A judgment is a model's answer to one narrow question about one row ("is this comment asking a question?"), published as a probability from 0 to 1 or as a level, so you choose the cut-off. Code does everything else: which rows are worth sending, the counting, the drops you ask for, and the credits. Labels are signals to read, never a verdict on a post, a review or a person. ## What each list gets by default | List | Default labels, free | Where they land | | ------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ | | Comment lists | `sentiment`, `question`, `purchase_intent`, `complaint` | `items[].computed.labels`, reported in `data.labels` | | Post lists | `sponsored`, `intent` (without `fits_offer`), `niche` | `items[].computed.labels`, reported in `data.labels` | | Post search lists | relevance against your `query`, as a score: nothing is dropped or reordered | `items[].computed.relevance`, reported in `data.relevance` | | Review lists | `sentiment` (with `rating_mismatch`), `issue` | `items[].computed.labels`, reported in `data.labels` | | [`/v1/search/everywhere`](/docs/search/everywhere.md) | stance toward your query, and relevance from the ranking step | `items[].computed.stance`, `items[].computed.relevance`, `data.stance_split`, `data.relevance` | | [`/v1/search/forums`](/docs/search.md#forums-and-creators) | relevance of each thread to your query | `items[].computed.relevance`, `data.relevance` | | [`/v1/search/news`](/docs/search/news.md) | articles about the same event grouped into stories | `data.stories`, `data.story_grouping` | | [`/v1/search/creators`](/docs/search.md#forums-and-creators) | what kind of account each creator is, for the first 40 | `data.creators[].account`, `data.account_kinds` | Prism composites already judged their rows before this change; see [Prism](/docs/prism.md). **Which endpoints are comment, post, search and review lists** **Comment lists:** `/v1/tiktok/post/comments`, `/v1/tiktok/video/comment/replies`, `/v1/instagram/post/comments`, `/v1/instagram/post/comment/replies`, `/v1/youtube/video/comments`, `/v1/youtube/video/comment/replies`, `/v1/reddit/post/comments`, `/v1/facebook/post/comments`, `/v1/facebook/post/comment/replies`, `/v1/twitter/tweet/replies`. **Post lists:** `/v1/instagram/profile/posts`, `/v1/instagram/profile/reels`, `/v1/instagram/search/reels`, `/v1/tiktok/profile/videos`, `/v1/tiktok/search/hashtag`, `/v1/twitter/user/tweets`, `/v1/twitter/search/tweets`, `/v1/reddit/search`, `/v1/reddit/subreddit`, `/v1/facebook/search/posts`, `/v1/hackernews/search`, and the four search lists below that walk several pages (`/v1/tiktok/search`, `/v1/youtube/search`, `/v1/threads/search`, `/v1/linkedin/search/posts`). **Post search lists (relevance):** `/v1/instagram/search/reels`, `/v1/twitter/search/tweets`, `/v1/hackernews/search`, `/v1/facebook/search/posts`, `/v1/reddit/search`, `/v1/tiktok/search`, `/v1/youtube/search`, `/v1/threads/search`, `/v1/linkedin/search/posts`. **Review lists:** `/v1/amazon/reviews`, `/v1/walmart/reviews`, `/v1/target/reviews`, `/v1/wayfair/reviews`, `/v1/home_depot/reviews`, `/v1/sephora/reviews`, `/v1/aliexpress/reviews`, `/v1/kohls/reviews`, `/v1/klarna/reviews`, `/v1/klarna/reviews/pro`, `/v1/google_shopping/reviews`, `/v1/tiktokshop/product/reviews`, `/v1/trustpilot/reviews`, `/v1/g2/reviews`, `/v1/yelp/business/reviews`, `/v1/google/business/extended-reviews`, `/v1/tripadvisor/reviews`, `/v1/tripadvisor/restaurant/reviews`, `/v1/tripadvisor/attraction/reviews`, `/v1/tripadvisor/cruise/reviews`, `/v1/google_play/app-reviews`, `/v1/app_store/app-reviews`. Every other endpoint answers exactly as before and has no `judgments` parameter. ## Field shapes Every label is a key under `computed.labels`, named after its preset. A preset key that is missing means that preset has not judged the row yet. ### Comments | Label | Value | Meaning | | ----------------- | -------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ | | `sentiment` | `{ level, score_0_1, confidence }` | How the comment feels, on five levels from 0 (very negative) to 4 (very positive); `score_0_1` is the same on a 0 to 1 scale | | `question` | `{ p }` | A real question. A rhetorical one that complains or mocks is not | | `purchase_intent` | `{ p }` | Bought it, ordered it, is switching to it, or asks for a link or a price. "Is it worth it?" is not | | `complaint` | `{ p }` | Something that happened to the author. Criticism of a price or a spec is not | ### Posts | Label | Value | Meaning | | ----------- | --------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `sponsored` | `{ p, disclosed, undisclosed, brand }` | Is it a paid or gifted promotion (`p`), did it carry a marker such as #ad or 광고 (`disclosed`), is it likely paid with no marker (`undisclosed`), and which mentioned account it promotes (`brand`, or `null`) | | `intent` | `{ label, confidence, buyer, seller, urgency, fits_offer }` | What the author is mainly doing (`asking_for_recommendation`, `comparing_options`, `switching_away`, `complaining`, `promoting`, `news_or_discussion`, `other`, or `null` when unsure), whether they read as a buyer, how pressing the need is (0 to 3). `fits_offer` is `null` unless you pass `offer=` | | `niche` | `{ label, confidence, taxonomy }` | Which niche of the published `sc-niche-v1` taxonomy the caption belongs to, or `personal_no_niche` or `other`. `label` is `null` when the caption is too thin to tell. It reads the caption only, not the video | ### Post search relevance `items[].computed.relevance` is `{ p, sense, depth, spam }`. `p` is the probability that the row is about what your query means. `sense` says which thing the query's key word refers to in the row: `target` (what you meant), `other_named_thing` (a different brand, person or place with the same name), `generic_word`, or `not_present`. `depth` runs from 0 (not about it) to 3 (mainly about it), and `spam` is the probability that the row is a mass-posted template. A row with too little text to judge carries `relevance: null`. ### Reviews | Label | Value | Meaning | | ----------- | ---------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `sentiment` | `{ level, score_0_1, confidence, rating_mismatch }` | How the reviewer feels, read from the title and text only, never the stars. `rating_mismatch` is `true` when the words clearly contradict the star rating, and `null` when the review has no rating | | `issue` | `{ label, confidence }` | The main problem the review reports: `product_defect`, `sizing_or_fit`, `shipping_or_delivery`, `customer_service`, `price_or_value`, `missing_feature`, `other`, or `none`. `label` is `null` when unsure | ### Search endpoints | Endpoint | Per row | Page report | | ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | | `search/everywhere` | `computed.stance`: `{ label, probabilities, confidence }`, with `label` one of `positive`, `negative`, `neutral`, `no_opinion`, or `null` under the confidence floor. `computed.relevance`: `{ score, on_topic, entity }` | `data.stance_split` counts stances overall and per platform with the ids behind every count; `data.relevance` counts off-topic results (`off_topic`, `off_topic_ids`) | | `search/forums` | `computed.relevance`: `{ p, sense, on_topic }` | `data.relevance` with `mode: "default"` and `reordered: false` | | `search/news` | none: `items[]` is unchanged | `data.stories[]`: `{ id, head_id, member_ids, size, outlets, outlet_count, countries, languages, first_seen, last_seen, judged }`, and `data.story_grouping` | | `search/creators` | `account`: `{ account_kind, account_kind_confidence, named_person }`, with `account_kind` one of `individual_creator`, `brand_or_business`, `media_or_publisher`, `fan_or_repost_page`, `cannot_tell`, or `null` when unsure | `data.account_kinds`: `{ status, judged, unjudged, methodology_version }` | On `search/everywhere`, `on_topic: false` marks exactly the results `relevance=filter` would drop. On `search/creators`, `individual_creator` is published only when the name, handle or bio shows one particular person; otherwise the creator reads `cannot_tell`. ## `null` and `pending` A row whose labels are `null` was not judged in this response. That is different from a low probability: `{ "p": 0.02 }` means "judged, and no", `null` means "not judged yet". A label that could not be judged never drops a row and is never billed. Default labels are judged under a short time budget so they never slow your page down. On list endpoints, rows not finished in time ship with `labels: null` (or `relevance: null`), are counted in `data.labels.pending` and listed in `pending_ids`, and keep being judged after the response is sent. The result goes into a shared per-row store, so your next call for the same page, including a free cache hit, carries them. In practice a cold comment page usually arrives fully labelled; a cold post or review page often arrives mostly pending and is complete from the next call. At most 60 cold rows are started per request, so a longer page can take more than one call to fill; rows past that count are `unjudged`, not `pending`. `data.labels.status` sums it up: | `status` | Meaning | | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `complete` | Every row that can be judged carries its labels. | | `partial` | Some rows are `null`: still being judged (`pending`) or not judged this time (`unjudged`). | | `skipped` | Judging was switched off for this request because capacity was short. No row is touched, not even with labels judged earlier, so the page is exactly the `judgments=off` page plus the report. | Need every row labelled in this response? Ask for the labels by name: `label=sentiment,question,purchase_intent,complaint` is still free, and a label you ask for is judged in the foreground under a longer time budget. `relevance=score` does the same for search relevance. The four search endpoints judge inside the request and keep no per-row store. A result not judged in time stays `null` for the life of that cached response (2 minutes, 5 minutes on `search/news`). There, a late or failed judgment is also named in `data._warnings`: `stance_partial`, `stance_unavailable` or `stance_skipped` on `search/everywhere`, `stories_pending` or `stories_unavailable` on `search/news`. `search/forums` and `search/creators` report it in `status` only. ## The report `data.labels` describes the labels on this page: | Field | Meaning | | ------------------------------ | ----------------------------------------------------------------------------------------------------------------------- | | `presets` | The labels this report covers. | | `mode` | `default` when you passed no `label=`, `requested` when you did. | | `status` | `complete`, `partial` or `skipped`, as above. | | `rows` | Rows on the page before any drop. | | `labelled`, `cached` | Rows that carry every label, and how many of those came from the shared per-row store. | | `pending`, `pending_ids` | Rows still being judged when the page was sent. | | `unjudged`, `skipped` | Rows not judged this time, and deleted or empty rows that are never sent or billed. | | `dropped`, `dropped_ids` | Rows removed by an `exclude=` you asked for. Default labels never drop a row. | | `extra_credits` | What labels added to this call. Always `0` for default labels. | | `methodology_version` | The version of the label questions, so you can tell when they change. | | `default` | Present when you asked for some labels by name: the report of the default labels you did not name, with its own `mode`, `status`, `pending` and `pending_ids`. Absent when you named every default. | `data.relevance` on the post search lists has the same `status`, `pending` and `pending_ids`, plus `origin` (`default` or `requested`), because its `mode` field already says `score` or `filter`. ## Turn them off ```bash title="cURL" curl "https://www.socialcrawl.dev/v1/tiktok/post/comments?url=https://www.tiktok.com/@stoolpresidente/video/7623818255903329566&judgments=off" \ -H "x-api-key: YOUR_API_KEY" ``` `judgments=off` returns the page exactly as it was before default labels: no `computed.labels`, no `computed.relevance` or `computed.stance`, no `data.labels`, `data.relevance`, `data.stance_split`, `data.stories` or `account` blocks. `label=none` does the same on list endpoints, also inside a list such as `label=mention,none`. It is part of the cache key, so an `off` request never receives a labelled cached page. It does not cancel a `label=` or `relevance=` you pass in the same request: those still run. ## Ask for more: `label=` adds to the defaults Naming labels adds them to the defaults rather than replacing them: ```bash title="cURL" curl "https://www.socialcrawl.dev/v1/instagram/search/reels?query=buldak+noodles&label=mention&brand=Buldak" \ -H "x-api-key: YOUR_API_KEY" ``` Each reel keeps `sponsored`, `intent`, `niche` and its relevance score, and gains `mention`: is the post about Buldak rather than something that shares the name, how it feels about the brand, which aspect it talks about, and whether the author bought or used it. `data.labels` reports the labels you named (`mode: "requested"`), and `data.labels.default` reports the default ones. A row can carry the labels you named now and the default ones on the next call. ## What stays opt-in and metered Labels that need your own input, and labels without a blind measurement yet, run only when you ask for them. | Ask with | On | Adds | | ------------------------------------------- | ------------------------- | -------------------------------------------------------------------------------------------------------- | | `label=mention&brand=` | post lists | `mention`: about the brand or not, sentiment toward it, sarcasm, aspect, first-hand use | | `label=intent&offer=` | post lists | `intent.fits_offer`: would your offer plausibly help this author | | `label=spam`, `label=toxic`, `label=low_quality` | comment lists | `{ p }` each (`low_quality` also `generic` and `bait`); `exclude=spam` or `exclude=low_quality` drops matches at 0.8 or higher | | `label=ai_style` | post lists | Named writing patterns common in AI-generated text, each on its own, plus `pattern_count`: see [Writing-pattern labels](#writing-pattern-labels-ai_style). `exclude=ai_style` drops posts with 2 or more | | `label=ai_written` | post lists | How likely the post was written by an AI model: `{ p, level, model }`, see [AI-written detection](#ai-written-detection-ai_written). `exclude=ai_written` drops `likely` posts | | `label=reports&reports=` | review lists | `reports.p`: does the review say the thing you describe, in any wording or language | | `label=incentivized` | review lists | `{ p, disclosed }`: does the reviewer say they got the product free, discounted or rewarded | | `relevance=score` or `filter` with `relevant_to=` | post search lists | relevance against a topic you write, instead of the query | | `brief=` | `search/creators` | `brief_match` per creator and a re-ranked list: see [Universal Search](/docs/search.md#forums-and-creators) | On the lists, a metered label holds a few extra credits up front (4 on most lists, 5 on `tiktok/search`, 8 on `linkedin/search/posts`) and refunds down to **1 credit per started 25 rows newly judged** on this request. Rows already judged for anyone are free, a page where nothing could be judged refunds the whole hold, and a cached page is free. `brief=` on `search/creators` adds 2 credits. Asking by name for a label that is also a default one, such as `label=sponsored`, stays free. ## Writing-pattern labels: `ai_style` [#writing-pattern-labels-ai_style] `label=ai_style` flags writing patterns that are common in AI-generated text, such as "This is not X. It's Y." and "Here's what nobody tells you". It reads the text of each post and nothing else. It does not say who wrote the post, and a person who writes in this style is flagged too. Read it as a list of patterns you can check in the text, never as a verdict on an author. ```bash title="cURL" curl "https://www.socialcrawl.dev/v1/twitter/search/tweets?query=customer+feedback&label=ai_style&exclude=ai_style" \ -H "x-api-key: YOUR_API_KEY" ``` `exclude=ai_style` only works together with `label=ai_style` in the same request. Sent alone it returns a 400 and costs nothing. Dropped posts are listed in `data.labels.dropped_ids`, and a post that could not be judged is never dropped. Each post carries `computed.labels.ai_style`: | Field | Type | Meaning | | --------------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | | `binary_contrast` | number | 0 to 1. A denial that sets up the point: "This is not X. It's Y." | | `faux_insight` | number | 0 to 1. A setup that announces a hidden truth: "Here's what nobody tells you", "The uncomfortable truth is" | | `colon_reveal` | number | 0 to 1. A short label, a colon, then the point as a reveal: "The best part: it learns." | | `profound_kicker` | number | 0 to 1. The last line restates the point as a short maxim: "Rejection is redirection." | | `puffery` | number | 0 to 1. Importance claimed instead of shown: "marks a pivotal moment", "highlighting the importance of" | | `rhetorical_setup` | number | 0 to 1. A staged setup: "What if I told you", a question the post answers itself, "Not a X. Not a Y. A Z." | | `slop_words` | integer | Count of overused words and empty phrases, such as delve, leverage, robust, game changer and "at the end of the day". Counted in code | | `em_dashes` | integer | Count of em dashes, and of en dashes with a space on both sides. Counted in code | | `emoji_bullets` | integer | Count of lines that start with an emoji or a symbol bullet and then text. Counted in code | | `one_line_paragraphs` | integer | Count of paragraphs that are one line of fewer than 12 words. A post with fewer than 2 paragraphs scores 0. Counted in code | | `pattern_count` | integer | How many of the 10 patterns are present | | `markers` | string | The names of the present patterns, sorted and comma-separated, or an empty string when there are none | A pattern counts as present when: - a judged pattern (the first six) is **0.8 or higher**; - `slop_words` is 1 or more; - `em_dashes` is 2 or more; - `emoji_bullets` is 2 or more; - `one_line_paragraphs` is 5 or more. `exclude=ai_style` drops a post with a `pattern_count` of **2 or more**. The thresholds are published so you can recompute `pattern_count` from the fields beside it, or apply your own cut-off to the raw numbers and skip `exclude=` altogether. A trimmed example row, illustrative and not a recorded response: ```json title="Response" { "post": { "id": "1234567890", "content": { "text": "Listening to customers is smart.\n\nObeying every customer request isn't.\n\n..." } }, "computed": { "labels": { "ai_style": { "binary_contrast": 0.91, "faux_insight": 0.06, "colon_reveal": 0.03, "profound_kicker": 0.12, "puffery": 0.02, "rhetorical_setup": 0.18, "slop_words": 0, "em_dashes": 0, "emoji_bullets": 0, "one_line_paragraphs": 7, "pattern_count": 2, "markers": "binary_contrast,one_line_paragraphs" } } } } ``` What to know before you rely on it: - **Short posts are counted, not judged.** A post with fewer than about eight words of prose (links, hashtags and mentions do not count) is never sent to the model. Its six judged fields are 0, and the four counted fields are still filled in, so it can still reach a `pattern_count` of 2. - **A long post is read at both ends.** The judged patterns read the first 400 characters of the post and its last 150, so a closing line is judged too, and the counts read the whole post. A pattern that sits only in the middle of a long post is judged from the counts alone. - **Paragraph layout matters.** `one_line_paragraphs` splits at blank lines. A post that breaks its lines with single newlines scores 0 there, and a list with blank lines between its items can score high. - **`fields=` removes the labels.** A `fields=` projection keeps only the paths you name on each row, and `computed.labels.ai_style` is not one of them unless you add it. The drop and `data.labels.dropped_ids` still apply, so rows disappear without a reason. Name the label to keep it: add `fields=post.url,post.published_at,post.content.text,computed.labels.ai_style` to the call above. The path is `computed.labels.ai_style`, not `post.computed.labels.ai_style`: on a row, `computed` sits beside `post`, not inside it. A path that matches nothing is named in `data._warnings`, with the path it suggests. **Where it works.** Every post list that takes `label=`, including `/v1/twitter/search/tweets`, `/v1/twitter/user/tweets` and `/v1/linkedin/search/posts`. It is not a default label. **What it costs.** It is metered like the other opt-in post labels: the call holds a few extra credits up front and settles at **1 credit per started 25 posts newly judged** on that request. Posts already labelled for anyone, and a cached page, cost nothing extra. `data.labels.extra_credits` reports what the label added, and `dry_run=1` shows the ceiling first. ## AI-written detection: `ai_written` [#ai-written-detection-ai_written] `label=ai_written` scores how likely each post was written by an AI model. It comes from SocialCrawl's own text classifier, trained for this one question, not from a general-purpose language model. Where `ai_style` names patterns you can check in the text, `ai_written` is the probability those patterns only hint at. ```bash title="cURL" curl "https://www.socialcrawl.dev/v1/linkedin/search/posts?query=product+feedback&label=ai_written&exclude=ai_written" \ -H "x-api-key: YOUR_API_KEY" ``` Each post carries `computed.labels.ai_written`: | Field | Type | Meaning | | ------- | -------------- | ----------------------------------------------------------------------------------------------------------- | | `p` | number or null | 0 to 1. The calibrated probability that an AI model wrote the post. `null` when the post is too short to read | | `level` | string | `likely` (p 0.95 or higher), `possible` (0.71 or higher), `unlikely`, or `too_short` (under 8 words) | | `model` | string | The model that scored it, `sc-ai-written-1` today. A new name means a new scale: re-check your own cut-offs | `exclude=ai_written` drops `likely` posts. To drop more, add `ai_written_threshold=` with the `p` you want to drop at, for example `ai_written_threshold=0.71` to drop `possible` posts too. A post too short to read, or one that could not be scored, is never dropped. Like every `exclude=`, it works only together with `label=ai_written` in the same request. To check one text you already have, without a list, call [`/v1/utility/ai-detection?text=`](/docs/utility.md). It returns the same judgment for the same text, for 1 credit. **How accurate it is.** Measured on 467 posts the model never saw: 233 written by people before ChatGPT existed (X and Reddit, up to November 2022), and 234 written by 8 current models (GPT-5.5, GPT-5.4 mini, GPT-4o mini, gpt-oss, Claude Sonnet 4.5, Claude Haiku 4.5, Gemini 3 Flash, Grok 4.3), a third of them prompted to sound like a person. | Cut-off | People's posts flagged | AI posts caught | | -------------------------- | ---------------------- | ------------------------------------------------- | | `likely` (the default drop) | 1 of 233 | 77% overall; 91% of posts of 40 words or more | | `possible` and above | 12 of 233 | 95% overall; 99% of posts of 40 words or more | What to know before you rely on it: - **It is a probability, not proof.** A person can be flagged and an AI-written post can pass. Use it to filter or sort a feed, not to accuse anyone of anything. - **Short posts are the hardest.** Under about 40 words, `likely` catches about 2 in 5 AI posts. Under 8 words the model does not guess: `level` is `too_short`. - **Some models are harder to catch than others.** Of the 8 tested, Gemini 3 Flash was the hardest: 28% `likely`, 79% `possible` or above. - **Edited text sits in between.** A draft a person rewrote, or a person's post polished by AI, can land anywhere on the scale. - **It reads the opening of each post**, about the first 1,000 characters, which on social posts is nearly always the whole post. - **English is what it was measured on.** It returns a score for any language, but other languages are unmeasured. **What it costs.** Metered like the other opt-in post labels: 1 credit per started 25 posts newly judged on that request, so a 20-post page adds 1 credit. Posts already scored for anyone, and a cached page, cost nothing extra. ## Filtering is free `relevance=filter` drops the rows that are not about your query and lists their ids in `data.relevance.dropped_ids`. It is free with your query as the topic. ```bash title="cURL" curl "https://www.socialcrawl.dev/v1/instagram/search/reels?query=mustang+horse&relevance=filter" \ -H "x-api-key: YOUR_API_KEY" ``` - **Post search lists:** a row is kept at `p` of 0.5 or more, or the `relevance_threshold` you set. Pagination is unchanged, so a filtered page can hold fewer rows. - **`search/everywhere`:** drops the ranked results the ranking step already judged off-topic, inside the flat 20 credits. `items_by_source` is not filtered. - **`search/forums`:** `relevance=score` reorders threads by relevance blended with their rank, and `filter` also drops the off-topic ones, inside the flat 10 credits. - **`search/creators`:** `relevance=filter` works only with `brief=` and drops creators that do not fit it. A row that could not be judged is never dropped. ## Credits and caching Default labels never add credits, holds or refunds: a call costs exactly what the same call costs with `judgments=off`. A response cache hit is still 0 credits and still labelled, because labels are read from a shared per-row store that lasts 30 days and is keyed by the row's text, so an edited comment is judged again. See [Credits](/docs/credits.md#labels-add-no-credits) and [Caching](/docs/caching.md#labels-on-a-cache-hit). ## How we measure [#how-we-measure] A label is on by default only when it has a blind measurement: rows labelled by hand before any judgment ran on them, on queries and sources never used to write or tune the questions. Every number below comes from one labeller, not a panel, so read it as a strong signal rather than a guarantee, and keep a person in the loop for decisions that matter. The numbers were measured again on 16/10/2026, when the judging model was updated. The `search/everywhere` stance, `search/creators` account kind and `prism/comment-leads` rows are from the earlier model; their blind sets have not been run again yet. | Judgment | Blind set | Result | | ------------------------------------------ | ------------------------------------------------ | ------------------------------------------------------------------------------------------------ | | Post `sponsored` | 39 posts, 15 Korean | precision 1.000, recall 1.000 | | Post `intent`, the `buyer` flag | two sets, 87 and 108 posts | precision 0.912 and 0.868, recall 1.000 and 0.885. Korean on the second set: precision 0.700, recall 0.778. Treat Korean `buyer` as a hint | | Post `niche` | 80 posts in English, Korean and German | 77 of 77 published labels right, 4% left `null` | | Review `sentiment` | 101 reviews from 8 sources, 38 Korean | 0.960 agreement, against 0.930 for the star rating | | Review `issue` | the same 101 reviews | 0.965 on the 86 reviews it labels | | Post search relevance, `relevance=filter` | 233 rows from 15 queries, 42 Korean | precision 0.963 (0.824 unfiltered), recall 0.953 | | `search/everywhere` stance | 64 posts, English and Korean | 0.826 agreement on the posts it labels; 72% of posts get a label | | `search/forums` relevance, `relevance=filter` | 168 threads, 140 Korean | Korean precision 0.948 (0.921 unfiltered) at recall 0.992; English 0.917 (0.630 unfiltered) at recall 0.647 | | `search/news` stories | 100 articles, 1,047 article pairs | pairwise precision 0.644, recall 0.887: the weakest default, which tends to join an event's preview with its outcome | | `search/creators` account kind | 240 creators | 0.843 accuracy; published `individual_creator` precision 0.917 | | Post `ai_written`, `level: likely` | 467 posts: 233 by people before 30/11/2022, 234 by 8 current models | 1 of 233 people's posts flagged; 77% of AI posts caught (91% of 40 words or more). See [AI-written detection](#ai-written-detection-ai_written) | Two Prism composites that judge rows on request were measured the same way: | Judgment | Blind set | Result | | ------------------------------------------ | ------------------------------------------------ | ------------------------------------------------------------------------------------------------ | | `prism/adverse-screen`, any flag | 166 posts, 55 Korean, 69 of them near-misses such as news and scam warnings | precision 0.827, recall 0.927; 6 of 69 near-misses flagged (Korean 4 of 31). `sanctions_or_terror_links` and `sexual_misconduct` had no positives and are unmeasured | | `prism/comment-leads`, the lead rule | 236 flagged comments | 0.962 show buyer intent; 0.792 show buyer intent for the offer's category | The default comment labels were measured on held-out rows of their tuning set rather than on a separate blind set, so their numbers are not quoted here. `spam`, `toxic` and `incentivized` have too few real positives in any set to measure, which is why they stay opt-in. ## Next steps - [Computed fields](/docs/computed-fields.md): The code-derived fields that sit next to the labels. - [Response schema](/docs/response-schema.md#data-level-reports): Where data.labels, data.relevance and the other reports sit. - [Sentiment analysis recipe](/docs/recipes/sentiment-analysis.md): Comment sentiment across platforms, with and without your own model. - [Universal Search](/docs/search.md): Stance, forum relevance, news stories and creator account kinds.