---
title: "Update a keyword"
method: PATCH
path: "/v1/keywords/{id}"
tags: ["Keywords"]
---

# Update a keyword

`PATCH /v1/keywords/{id}`

## Request

- Base URL: `https://api.mentio.dev`
- URL: `https://api.mentio.dev/v1/keywords/{id}`
- Auth: HTTP bearer

Rename it (`term`: the new term is tracked from now on, with no look-back; the mentions the old term found stay), mute or unmute it, reclassify it (`kind`), change the platforms it is tracked on, its classifier `context`, its `matching` rules (each rule field optional; an empty list clears one), or its monthly mention `cap` (null removes it; a cap above this month's count resumes a capped keyword at once). Rules apply to new mentions from the next poll; stored mentions are untouched.

## Path parameters

- `id` string, required: Keyword id (kw_...).

## Request body

- object: Omitted fields are untouched.
  - `term` string: Renames the keyword: the new term is tracked from now on, with no look-back, and the keyword keeps its id, its settings and the mentions the old term found. Wrap it in double quotes for the exact phrase only. A 409 when its group already tracks the new term.
  - `kind` 'brand' | 'competitor' | 'topic': Reclassify it as brand, competitor or topic.
  - `muted` boolean: A muted keyword stops polling and matching; its mentions stay.
  - `platforms` string[], nullable: Replaces the platform list; null means every platform, [] none (reviews only, when the keyword has reviewSources).
  - `context` string, nullable: A sentence the classifier reads for this keyword only, on top of the company profile or the group's own description (at most 300 characters): what the term means here, what to ignore. "Arc is our browser; ignore the geometry word." Null clears it.
  - `matching` object: Omitted fields are untouched; an empty list clears one.
    - `requiredTerms` string[]: The post must ALSO contain these terms, any one of them or all of them per requiredMode. A `*` at the start or the end of an entry is a wildcard (alert* matches alerts and alerting). Empty: no requirement.
    - `requiredMode` 'any' | 'all': any: at least one required term must appear. all: every one must.
    - `requiredGroups` array[]: Combinations of terms, at most 50 of 10 terms each: the post must ALSO contain every term of at least one group (an OR of ANDs). For the keyword inreach, [["weather", "alert*"], ["tsunami"], ["flash flood"]] keeps a post about inReach weather alerts, tsunamis or flash floods, one keyword instead of three. Same phrase and wildcard rules as requiredTerms; when both are set a post must pass both. Empty: no combinations.
      - string[]
    - `excludedTerms` string[]: A post containing any of these is dropped. A `*` at the start or the end of an entry is a wildcard (beta.* matches beta.0.1; *bot matches nightlybot).
    - `excludedAuthors` string[]: Posts by these authors are dropped: profile or post links, @handles, u/names, Bluesky DIDs or display names, stored in canonical form like an alert's muted list.
    - `caseSensitive` boolean: true: the term must appear in the case it was typed (RAG, never rag). Default false.
    - `exactPhrase` boolean: true: only the exact phrase matches. false (default): a multi-word keyword also matches a post holding its words close together, in any order, plurals and spellings (non-profit, nonprofit) included; such a match is kept and billed only when the classifier scores it relevant. A term sent in double quotes sets this to true.
    - `repositories` object: GitHub only: each field is replaced when sent, kept when omitted; null clears a minimum.
      - `events` string[]: The repository events this keyword takes. traction: a repository created in the last 30 days reached 10 stars. stars: one with 100 stars or more passed a milestone (100, 250, 500, 1,000, 2,000, 5,000 ...). growth: it gained stars fast in a week. new: every repository created, even with no stars (off by default: nearly all of them are noise for a broad keyword). Default: traction, stars, growth. Empty: no repository events.
      - `minStars` integer, nullable: Only repositories with at least this many stars; null for no minimum.
      - `minWeeklyStars` integer, nullable: Only repositories that gained at least this many stars in the last 7 days (25 or more). It applies to every event, and one whose last week is not known yet does not pass. null: a growth event needs 100 stars and 20% of the repository's stars in a week, the other events need no growth.
    - `subreddits` object: Reddit only, for this keyword alone: each list is replaced when sent, kept when omitted.
      - `only` string[]: Replaces the keyword's allowlist; [] clears it.
      - `excluded` string[]: Replaces the keyword's deny list; [] clears it.
  - `cap` object, nullable: Replaces the mention cap and its period (per, default month); null removes it. A cap above the current period's count resumes a capped keyword at once, one at or under it pauses it.
    - `mentions` integer, required: Charged items allowed per period (see per): matched mentions plus the thread comments delivered under them. Every match counts, relevant or not, the look-back a new keyword gets included, because every match bills.
    - `per` 'day' | 'week' | 'month': The period the cap counts in, in UTC: day (from 00:00), week (from Monday 00:00) or month (the calendar month, the default). At the cap the keyword stops until the next period starts, so a daily cap keeps mentions coming every day with a fixed ceiling on spend.
  - `comments` object: Comments under this keyword's mentions; omitted fields are untouched (on create: off, 20 per post).
    - `enabled` boolean: Read the comments under this keyword's relevant mentions, from 30 minutes after each post, as long as its platform's conversations last (a day to a month). Each comment delivered costs $0.008 (comments of fewer than three words are dropped, never billed) (the comments line of the bill). Off by default.
    - `maxPerPost` integer: The newest comments of one thread you receive, 20 by default, 100 at most: the ceiling on what one mention's comments can cost. A comment that names the keyword is a mention too, but only among these newest ones, so it is never billed past the ceiling.
  - `groupId` string: Moves the keyword to this group (grp_...). A 409 when that group already tracks the term.
  - `reviewSources` object[]: Replaces the list of apps whose reviews this keyword collects; [] disconnects them all (their reviews stay). An app or country added here gets the free 30-day look-back; one already listed keeps its place.
    - `url` string: The review page's link: an App Store or Google Play app (https://apps.apple.com/us/app/notion/id1232780281, https://play.google.com/store/apps/details?id=notion.id), a Trustpilot page (https://www.trustpilot.com/review/notion.so) or a Google Maps place (its full link, or a maps.app.goo.gl share link). Or give platform and id.
    - `platform` 'appstore' | 'googleplay' | 'trustpilot' | 'googlemaps': appstore (Apple App Store), googleplay (Google Play), trustpilot (a company's Trustpilot page) or googlemaps (a place's Google reviews).
    - `id` string: The id on the platform: the digits after "id" on the App Store, the package name on Google Play, the company's domain on Trustpilot (notion.so), a Place ID (ChIJ...) on Google Maps.
    - `countries` string[]: App Store and Google Play only: storefronts to read, two-letter codes, at most 20. Default: the one in the link, else us. Each is one more poll a day; the same review seen in two storefronts is one mention. Trustpilot and Google Maps have one page for everyone and take none.
    - `language` string: Google Play only: the language of the reviews to read (en, es, de, pt-BR); Google Play answers one language at a time. Default: the link's hl, else en.
  - `feeds` object[]: Replaces the feeds this keyword reads; [] disconnects them all (their mentions stay). A feed added here is checked now and brings its newest 10 matching items of the last 30 days; one already listed keeps its place.
    - `url` string, required: A feed's address (https://forum.example.com/posts.rss), or a page's (https://forum.example.com): the feed the page advertises is used.
  - `readsWorkspaceFeeds` boolean: true: the keyword also reads the workspace's feeds (GET /v1/feeds), live from now on. false: its own feeds alone.

## Response `200`

The updated keyword

- Keyword
  - `id` string, required: Keyword id (kw_...).
  - `term` string, required
  - `kind` 'brand' | 'competitor' | 'topic', required
  - `muted` boolean, required: Not polled or matched. Paused by you, by the wallet (see pausedForBalance) or by the noise brake (see pausedForNoise). A keyword at its mention cap is not muted (see pausedForCap).
  - `pausedForBalance` boolean, required: Muted by the wallet for lack of balance; a top-up resumes it, unmuting by hand needs balance too.
  - `pausedForNoise` boolean, required: Muted by the noise brake: on a workspace running on its welcome credit, at least 20 of its matches were scored and under 30% were relevant. A change of its required or excluded terms, platforms or context resumes it (when the balance covers another day), and so does unmuting; a top-up does not.
  - `pausedForCap` boolean, required: At its monthly mention cap: not matched until the first of next month (UTC) or until the cap is raised. Not muted: it keeps its place and its daily keyword charge.
  - `cap` object, nullable, required: The mention cap and its period, or null for none.
    - `mentions` integer, required: Charged items allowed per period (see per): matched mentions plus the thread comments delivered under them. Every match counts, relevant or not, the look-back a new keyword gets included, because every match bills.
    - `per` 'day' | 'week' | 'month': The period the cap counts in, in UTC: day (from 00:00), week (from Monday 00:00) or month (the calendar month, the default). At the cap the keyword stops until the next period starts, so a daily cap keeps mentions coming every day with a fixed ceiling on spend.
    - `used` integer, required: Charged items (matches plus thread comments) counted in the current period: today, this week from Monday or this month, UTC. The keyword pauses when it reaches mentions.
    - `welcome` boolean, required: Set by Mentio, not you: a workspace on its welcome credit collects at most 200 mentions a keyword a month. The first top-up removes it.
    - `own` integer, nullable, required: Your own cap. With welcome true, the cap the keyword gets back at the first top-up (null for none); otherwise the same as mentions. Sending mentions: 200 back while welcome is true changes nothing.
    - `ownPer` 'day' | 'week' | 'month', nullable, required: The period of your own cap (null when own is null); with welcome false, the same as per.
    - `resumesAt` string, nullable, required: While paused for its cap (pausedForCap): when its next period starts and it matches again. Null otherwise.
  - `comments` object, required: Comments under this keyword's mentions: when enabled, the comments of every mention scored relevant are read from 30 minutes after the post, on a schedule per platform (for a day on Reddit, Hacker News and Bluesky, a week on GitHub, Stack Overflow and DEV, a month on YouTube) (new comments only, at most maxPerPost a thread, comments of fewer than three words dropped), on Hacker News, Bluesky, GitHub, Stack Overflow, DEV, YouTube and Reddit. List them with GET /v1/mentions/{id}/comments.
    - `enabled` boolean, required: Read the comments under this keyword's relevant mentions, from 30 minutes after each post, as long as its platform's conversations last (a day to a month). Each comment delivered costs $0.008 (comments of fewer than three words are dropped, never billed) (the comments line of the bill). Off by default.
    - `maxPerPost` integer, required: The newest comments of one thread you receive, 20 by default, 100 at most: the ceiling on what one mention's comments can cost. A comment that names the keyword is a mention too, but only among these newest ones, so it is never billed past the ceiling.
  - `group` GroupRef, required: The group the keyword belongs to.
    - `id` string, required: Group id (grp_...).
    - `name` string, required: The group's name.
    - `externalId` string, nullable, required: Your own id for the group, or null.
    - `isDefault` boolean, required: The workspace's default group, where a keyword lands when no group is named.
  - `platforms` string[], nullable, required: Platforms the term is searched on; null means every platform, [] none (the keyword only collects reviews).
  - `feeds` KeywordFeed[], required: RSS and Atom feeds this keyword reads; empty for none.
    - `url` string, required: The feed itself (after redirects): what is polled.
    - `title` string, nullable, required: The feed's own title, when it has one.
    - `connectedAt` string, required: When this keyword started reading the feed: nothing published before it is a live mention.
  - `readsWorkspaceFeeds` boolean, required: true (the default): the keyword also reads every feed of the workspace (GET /v1/feeds), on top of its own `feeds`. false: its own feeds alone.
  - `reviewSources` ReviewSource[], required: Where this keyword collects reviews from (App Store and Google Play apps, Trustpilot pages, Google Maps places); empty for none.
    - `platform` 'appstore' | 'googleplay' | 'trustpilot' | 'googlemaps', required: appstore (Apple App Store), googleplay (Google Play), trustpilot (a company's Trustpilot page) or googlemaps (a place's Google reviews).
    - `id` string, required: The id on the platform: app id, package name, Trustpilot domain, Google Place ID or cid.
    - `url` string, required: The review page: the store listing, the Trustpilot page or the Google Maps place.
    - `countries` string[], required: Storefronts read, lowercase two-letter codes; empty on Trustpilot and Google Maps.
    - `language` string, nullable, required: Google Play's review language; null elsewhere.
    - `connectedAt` string, required: When this keyword started collecting the page's reviews.
  - `context` string, nullable, required: A sentence the classifier reads for this keyword only, on top of the company profile or the group's own description (at most 300 characters): what the term means here, what to ignore. "Arc is our browser; ignore the geometry word." Null clears it.
  - `matching` object, required: Matching rules applied before a mention is stored; a rejected post is never billed.
    - `requiredTerms` string[], required: The post must ALSO contain these terms, any one of them or all of them per requiredMode. A `*` at the start or the end of an entry is a wildcard (alert* matches alerts and alerting). Empty: no requirement.
    - `requiredMode` 'any' | 'all', required: any: at least one required term must appear. all: every one must.
    - `requiredGroups` array[], required: Combinations of terms, at most 50 of 10 terms each: the post must ALSO contain every term of at least one group (an OR of ANDs). For the keyword inreach, [["weather", "alert*"], ["tsunami"], ["flash flood"]] keeps a post about inReach weather alerts, tsunamis or flash floods, one keyword instead of three. Same phrase and wildcard rules as requiredTerms; when both are set a post must pass both. Empty: no combinations.
      - string[]
    - `excludedTerms` string[], required: A post containing any of these is dropped. A `*` at the start or the end of an entry is a wildcard (beta.* matches beta.0.1; *bot matches nightlybot).
    - `excludedAuthors` string[], required: Posts by these authors are dropped: profile or post links, @handles, u/names, Bluesky DIDs or display names, stored in canonical form like an alert's muted list.
    - `caseSensitive` boolean, required: true: the term must appear in the case it was typed (RAG, never rag). Default false.
    - `exactPhrase` boolean, required: true: only the exact phrase matches. false (default): a multi-word keyword also matches a post holding its words close together, in any order, plurals and spellings (non-profit, nonprofit) included; such a match is kept and billed only when the classifier scores it relevant. A term sent in double quotes sets this to true.
    - `subreddits` object, required: Reddit only, for this keyword alone; the workspace filters' own subreddit lists (GET /v1/filters) still apply to every keyword, and a post must pass both.
      - `only` string[], required: When non-empty, this keyword takes Reddit posts from these subreddits ONLY and `excluded` is ignored. r/name or name, stored bare and lowercase.
      - `excluded` string[], required: Reddit posts from these subreddits are dropped for this keyword. r/name or name.
    - `repositories` object, required: GitHub only: which repository events naming this keyword become mentions. An event outside the rule is dropped before it is stored, so it is never billed.
      - `events` string[], required: The repository events this keyword takes. traction: a repository created in the last 30 days reached 10 stars. stars: one with 100 stars or more passed a milestone (100, 250, 500, 1,000, 2,000, 5,000 ...). growth: it gained stars fast in a week. new: every repository created, even with no stars (off by default: nearly all of them are noise for a broad keyword). Default: traction, stars, growth. Empty: no repository events.
      - `minStars` integer, nullable, required: Only repositories with at least this many stars; null for no minimum.
      - `minWeeklyStars` integer, nullable, required: Only repositories that gained at least this many stars in the last 7 days (25 or more). It applies to every event, and one whose last week is not known yet does not pass. null: a growth event needs 100 stars and 20% of the repository's stars in a week, the other events need no growth.
  - `stats` object, required: Computed over this workspace's matches.
    - `mentions` integer, required: Every match ever, relevant or not: the number billing counts.
    - `relevant` integer, required: Matches scored at or above the relevance threshold.
    - `last7d` integer, required: Matches published in the last 7 days.
    - `thisMonth` integer, required: Matches recorded this calendar month (UTC), plus the thread comments delivered under this keyword's mentions: the count a cap compares against.
    - `lastMentionAt` string, nullable, required: Newest matched post; null until the first one.
    - `feedback` object, required: Your verdicts on this keyword's mentions (PATCH /v1/mentions/{id} relevant).
      - `relevant` integer, required: Mentions a person marked relevant.
      - `notRelevant` integer, required: Mentions a person marked not relevant: the noise the classifier let through.
    - `noise` object, required: Relevance over the last 14 days of scored matches, so a keyword tightened today stops being flagged within two weeks.
      - `scored` integer, required: Matches of the last 14 days (by match time) the classifier has scored.
      - `relevant` integer, required: Of those, the ones scored relevant.
      - `noisy` boolean, required: At least 20 scored matches in the last 14 days and under 30% of them relevant: tighten the keyword with required terms, excluded terms or context. Every match bills, relevant or not.
    - `health` 'healthy' | 'noisy' | 'quiet' | 'capped' | 'paused' | 'new', required: The keyword's health over the same 14 days as `noise`, by the rule GET /v1/keywords/{id}/health applies to its own window: paused (muted), capped (at its mention cap), noisy (20 or more scored matches, under 30% relevant), new (under 7 days old and not noisy, or changed in the last 7 days with under 20 scored matches since), quiet (7 days or older, nothing relevant), else healthy. Judged on the matches since the keyword's last change to its matching rules, platforms or context (or its unmute) when that is inside the 14 days, so a keyword tightened today is not flagged on the noise the change removed; `noise` itself keeps the whole 14 days. Always 14 days, while the endpoint reads 30 by default, so the two can differ for the same keyword. The health endpoint says why and what to change.
    - `cost` object, required: What this keyword has cost this calendar month (UTC) at list price: exactly its row in GET /v1/usage/breakdown?month=<this month> (same tables, same rounding). The wallet's ledger, which settles once a day, is what can differ from these list-price numbers, and only by cumulative rounding.
      - `keywordDays` integer, required: Days this month the keyword was charged for: unmuted at the daily tick. A keyword created today reads 0 until tomorrow's tick.
      - `keywordCents` integer, required: Those days at the keyword rate ($5 a month, 500/30 cents a day), rounded once on the total.
      - `billableMentions` integer, required: Matches billed this month, counted when they were scored (the clock the ledger settles by), so it can trail thisMonth by the matches still being scored and never counts one that failed to score.
      - `mentionCents` integer, required: Those matches at $0.008 each, rounded once on the total.
      - `billableComments` integer, required: Comments billed this month under this keyword: comments delivered in its threads and comments it matched, each once per workspace.
      - `commentCents` integer, required: Those comments at $0.008 each, rounded once on the total.
      - `totalCents` integer, required: keywordCents plus mentionCents plus commentCents: what this keyword has cost this month, in USD cents.
  - `polling` object[], required: Poll health per platform polled on a schedule. Live feeds (Bluesky) have no entry.
    - `platform` 'bluesky' | 'hackernews' | 'github' | 'stackoverflow' | 'devto' | 'reddit' | 'x' | 'youtube' | 'news' | 'linkedin' | 'tiktok' | 'instagram' | 'appstore' | 'googleplay' | 'trustpilot' | 'googlemaps' | 'rss', required: Platform: bluesky, hackernews, github, stackoverflow, devto, reddit, x, youtube, news, linkedin, tiktok, instagram, appstore (App Store reviews), googleplay (Google Play reviews), trustpilot (Trustpilot reviews), googlemaps (Google reviews of a place), rss (RSS and Atom feeds a keyword reads).
    - `lastPolledAt` string, nullable, required: Newest poll of this platform for the term; null until the first one.
    - `emptyPolls` integer, required: Consecutive polls that found nothing new; the scheduler slows down as it grows.
  - `createdAt` string, required: ISO 8601 timestamp, UTC.

## Other responses

- `401`: Missing or invalid API key
- `402`: Unmuting needs balance for one more keyword-day (insufficient_balance) or would exceed the 500-keyword ceiling (keyword_limit_reached)
- `404`: Keyword not found
- `409`: Renamed to, or moved into a group with, a term that group already tracks

---

[API](https://skmtc.dev/mentio/apis/mentio-api.md) · [All operations](https://skmtc.dev/mentio/apis/mentio-api/llms.txt) · [OpenAPI document](https://skmtc.dev/mentio/apis/mentio-api/revisions/b16a21d0ed02?raw)
