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

# Update a keyword

`PATCH /v1/keywords/{id}`

## Request

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

Renames the keyword (`term`: the new term is tracked from now on, with no look-back, and the mentions the old term found stay), mutes or unmutes it, or changes its `kind`, platforms, classifier `context`, `matching` rules or monthly mention `cap`. Each rule field is optional and an empty list clears it. A null cap removes the cap, and a cap above this month's count resumes a capped keyword right away. New rules apply to mentions from the next poll on. Stored mentions stay as they are.

## Path parameters

- `id` string, required: The keyword's id (kw_...).

## Request body

- object: Fields you leave out stay as they are.
  - `term` string: A new term for the keyword. It keeps its id, settings and the mentions the old term found, and the new term is tracked from now on, with no look-back. A 409 when its group already tracks the new term.
  - `kind` 'brand' | 'competitor' | 'topic': New kind (brand, competitor or topic).
  - `muted` boolean: Muting stops polling and matching. Existing mentions are kept.
  - `platforms` string[], nullable: Sets a new platform list. Null means all platforms. An empty list means none, which works when the keyword has reviewSources and only collects reviews.
  - `feeds` object[]: RSS or Atom feeds this keyword reads, at most 20, each { url }: a feed's address, or a page's (a forum, a community, a blog), whose advertised feed is used, else a usual address such as /feed or /rss. Every feed is checked when saved, and a 400 names each one that cannot be read and why. Feeds are read every hour; an item is a mention of this keyword when it holds the term (with its matching rules), and only of keywords that named the feed. A newly connected feed brings its newest 10 matching items of the last 30 days, billed like any mention. On PATCH the list replaces the old one, and [] disconnects them all (their mentions stay).
    - `url` string, required: A feed's address (https://forum.example.com/posts.rss), or a page's (https://forum.example.com), whose advertised feed is used.
  - `context` string, nullable: Up to 300 characters the classifier reads for this keyword only, in addition to the company profile or the group's description. Say what the term means for you and what to ignore, for example "Basil is our deploy tool, not the herb." Null clears it.
  - `matching` object: Fields you leave out stay as they are. An empty list clears a field.
    - `requiredTerms` string[]: Terms the post must contain as well. requiredMode says whether one or all of them must appear. An empty list sets no requirement.
    - `requiredMode` 'any' | 'all': any needs one of the required terms in the post. all needs every one.
    - `excludedTerms` string[]: Posts that contain one of these terms are dropped. Put `*` at the start or end of a term as a wildcard, so deploy* matches deployment and *bot matches nightlybot.
    - `excludedAuthors` string[]: Authors whose posts this keyword ignores. Each entry is a profile or post link, @handle, u/name, Bluesky DID or display name. Entries are normalized the same way as an alert's muted authors.
    - `caseSensitive` boolean: When true, the term only matches in the exact case you typed, so RAG never matches rag. Defaults to false.
  - `cap` object, nullable: Sets a new monthly mention cap, or null for no cap. A cap above this month's count resumes a capped keyword right away. A cap at or below the count pauses it.
    - `mentions` integer, required: How many matched mentions the keyword may collect per calendar month (UTC). Every match counts, relevant or not, including a new keyword's look-back, since every match is billed.
  - `comments` object: Comments under this keyword's mentions; omitted fields stay as they are (on create: off, 20 per mention).
    - `enabled` boolean: Read the comments under this keyword's relevant mentions, from 30 minutes after each post, while its platform's conversations last. Each comment delivered costs $0.008 on the comments line of the bill (comments of fewer than three words are dropped, never billed). Off by default.
    - `maxPerPost` integer: How many of a thread's newest comments you receive, 20 by default and 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.
  - `groupId` string: Target group (grp_...). 409 if it already has this term.
  - `reviewSources` object[]: Sets a new list of review pages for this keyword. An empty list disconnects them all, and their reviews are kept. A page or country you add gets the free 30-day look-back. Pages already listed are unchanged.
    - `url` string: Review page link, or send platform and id instead. Accepts App Store and Google Play app links (https://apps.apple.com/us/app/slack/id618783545, https://play.google.com/store/apps/details?id=com.Slack), Trustpilot pages (https://www.trustpilot.com/review/slack.com) and Google Maps places (full or maps.app.goo.gl link).
    - `platform` 'appstore' | 'googleplay' | 'trustpilot' | 'googlemaps': Where the reviews come from. appstore is the Apple App Store, googleplay is Google Play, trustpilot is a company's Trustpilot page and googlemaps is the Google reviews of a place.
    - `id` string: The platform's own id. On the App Store it is the number after "id" in the link, on Google Play the package name, on Trustpilot the company's domain (slack.com) and on Google Maps a Place ID (ChIJ...).
    - `countries` string[]: For App Store and Google Play. Up to 20 storefronts to read, as two-letter codes. Without it, the storefront in the link is used, or us. Each storefront adds one poll a day, and a review found in two storefronts is still one mention. Trustpilot and Google Maps have a single page and take no countries.
    - `language` string: For Google Play. The review language to read, such as en, es, de or pt-BR, since Google Play returns one language at a time. Without it, the link's hl is used, or en.

## Response `200`

The keyword after the change

- Keyword
  - `id` string, required: The keyword's id (kw_...).
  - `term` string, required
  - `kind` 'brand' | 'competitor' | 'topic', required
  - `muted` boolean, required: True when the keyword is neither polled nor matched. You, the wallet (pausedForBalance) or the noise brake (pausedForNoise) can mute it. Reaching the mention cap does not mute it (pausedForCap).
  - `pausedForBalance` boolean, required: Muted because the balance ran out. A top-up resumes it. Unmuting it yourself also needs balance.
  - `pausedForNoise` boolean, required: Set when the noise brake muted the keyword. That happens on welcome credit once 20 or more matches are scored and under 30% are relevant. Unmuting it, or changing its required or excluded terms, platforms or context, resumes it when the balance covers another day. A top-up alone does not.
  - `pausedForCap` boolean, required: Hit its monthly mention cap. Matching resumes on the 1st (UTC) or when you raise the cap. The keyword isn't muted and is still charged daily.
  - `cap` object, nullable, required: Monthly limit on matched mentions. Null means none.
    - `mentions` integer, required: How many matched mentions the keyword may collect per calendar month (UTC). Every match counts, relevant or not, including a new keyword's look-back, since every match is billed.
    - `welcome` boolean, required: Rumoro sets this while the workspace uses its welcome credit. It limits each keyword to 200 mentions a month until the first top-up.
    - `own` integer, nullable, required: The cap you chose. While welcome is true it is the cap restored at the first top-up (null for none), and sending `mentions: 200` changes nothing. Otherwise it equals mentions.
  - `comments` object, required: Comments under this keyword's mentions. When enabled, the thread of every new mention scored relevant is read from 30 minutes after the post, several times while its platform's conversations last (a day on Reddit, Hacker News and Bluesky, a week on GitHub, Stack Overflow and DEV, a month on YouTube): new comments only, newest first, at most maxPerPost per mention, and comments of fewer than three words are dropped. 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, while its platform's conversations last. Each comment delivered costs $0.008 on the comments line of the bill (comments of fewer than three words are dropped, never billed). Off by default.
    - `maxPerPost` integer, required: How many of a thread's newest comments you receive, 20 by default and 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.
  - `group` GroupRef, required: The keyword's group.
    - `id` string, required: The group's id (grp_...).
    - `name` string, required: Name of the group.
    - `externalId` string, nullable, required: An id from your own system, or null.
    - `isDefault` boolean, required: True for the workspace's default group. New keywords go there when you don't name a group.
  - `platforms` string[], nullable, required: The platforms searched for the term. Null means all of them. An empty list means none, and the keyword only collects reviews.
  - `feeds` KeywordFeed[], required: The RSS or Atom feeds this keyword reads, in the order given.
    - `url` string, required: The feed itself, after redirects: the address that is read.
    - `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.
  - `reviewSources` ReviewSource[], required: The review pages the keyword reads, such as App Store and Google Play apps, Trustpilot pages and Google Maps places. Empty when there are none.
    - `platform` 'appstore' | 'googleplay' | 'trustpilot' | 'googlemaps', required: Where the reviews come from. appstore is the Apple App Store, googleplay is Google Play, trustpilot is a company's Trustpilot page and googlemaps is the Google reviews of a place.
    - `id` string, required: The platform's own id. An App Store app id, a Google Play package name, a Trustpilot domain, or a Google Place ID or cid.
    - `url` string, required: URL of the review page, such as an app's store listing, a company's Trustpilot page or a place on Google Maps.
    - `countries` string[], required: Two-letter storefront codes, lowercase. Empty for Trustpilot and Google Maps.
    - `language` string, nullable, required: The review language on Google Play. Null on other platforms.
    - `connectedAt` string, required: When the keyword began collecting reviews from this page.
  - `context` string, nullable, required: Up to 300 characters the classifier reads for this keyword only, in addition to the company profile or the group's description. Say what the term means for you and what to ignore, for example "Basil is our deploy tool, not the herb." Null clears it.
  - `matching` object, required: Rules a post must pass before it is stored as a mention. A post they reject is never billed.
    - `requiredTerms` string[], required: Terms the post must contain as well. requiredMode says whether one or all of them must appear. An empty list sets no requirement.
    - `requiredMode` 'any' | 'all', required: any needs one of the required terms in the post. all needs every one.
    - `excludedTerms` string[], required: Posts that contain one of these terms are dropped. Put `*` at the start or end of a term as a wildcard, so deploy* matches deployment and *bot matches nightlybot.
    - `excludedAuthors` string[], required: Authors whose posts this keyword ignores. Each entry is a profile or post link, @handle, u/name, Bluesky DID or display name. Entries are normalized the same way as an alert's muted authors.
    - `caseSensitive` boolean, required: When true, the term only matches in the exact case you typed, so RAG never matches rag. Defaults to false.
  - `stats` object, required: Counted from this workspace's matches only.
    - `mentions` integer, required: All matches so far, relevant or not. Billing counts this number.
    - `relevant` integer, required: Matches whose relevance score reached the threshold.
    - `last7d` integer, required: Matches with a post date in the past 7 days.
    - `thisMonth` integer, required: Matches recorded in the current calendar month (UTC). The cap is checked against this count.
    - `lastMentionAt` string, nullable, required: Time of the latest matched post. Null before the first match.
    - `feedback` object, required: Your team's relevance marks, set with PATCH /v1/mentions/{id}.
      - `relevant` integer, required: Marked relevant by your team.
      - `notRelevant` integer, required: Mentions someone on your team marked not relevant. This is noise the classifier missed.
    - `noise` object, required: Relevance of the scored matches from the past 14 days. A keyword you narrow today drops the flag within two weeks.
      - `scored` integer, required: How many of the last 14 days' matches are scored.
      - `relevant` integer, required: How many of them were scored relevant.
      - `noisy` boolean, required: True when the past 14 days have 20 or more scored matches and fewer than 30% are relevant. Narrow the keyword with required terms, excluded terms or context, because every match is billed, relevant or not.
    - `health` 'healthy' | 'noisy' | 'quiet' | 'capped' | 'paused' | 'new', required: Health over the 14 days `noise` covers, by the same rule as GET /v1/keywords/{id}/health. The first that applies wins. paused (muted), capped (at its cap), noisy (20+ scored matches, under 30% relevant), new (under 7 days old, or changed in the last 7 days with under 20 scored since), quiet (7+ days, nothing relevant), otherwise healthy. After a change to matching rules, platforms or context, or an unmute, within the 14 days, only later matches count, while `noise` keeps all 14 days. The endpoint defaults to 30 days, so the two can differ, and it explains why and what to change.
    - `cost` object, required: List-price cost this calendar month (UTC), equal to this keyword's row in GET /v1/usage/breakdown?month=<this month>. The ledger settles daily and can differ only by cumulative rounding.
      - `keywordDays` integer, required: Charged days this month. A day is charged when the keyword is unmuted at the daily run, so a new keyword shows 0 until tomorrow.
      - `keywordCents` integer, required: keywordDays at 500/30 cents each, which is $5 a month. Only the total is rounded.
      - `billableMentions` integer, required: Matches billed this month. A match counts when it is scored, which is the time the ledger settles by. So this can lag behind thisMonth while matches are still being scored, and a match that failed to score never counts.
      - `mentionCents` integer, required: Those matches priced at $0.008 each, with the total rounded once.
      - `billableComments` integer, required: Comments billed on the comments line: comments delivered under mentions and matched replies, 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.
  - `polling` object[], required: Polling status for each platform that is polled on a schedule. Live feeds such as Bluesky are not listed.
    - `platform` 'bluesky' | 'hackernews' | 'github' | 'stackoverflow' | 'devto' | 'reddit' | 'x' | 'youtube' | 'news' | 'linkedin' | 'tiktok' | 'instagram' | 'appstore' | 'googleplay' | 'trustpilot' | 'googlemaps' | 'rss', required: The platform. Review platforms are appstore, googleplay, trustpilot and googlemaps (a place's Google reviews).
    - `lastPolledAt` string, nullable, required: When the term was last polled on this platform. Null before the first poll.
    - `emptyPolls` integer, required: How many polls in a row found nothing new. The higher it gets, the less often the term is polled.
  - `createdAt` string, required: UTC time in ISO 8601.

## Other responses

- `401`: The API key is missing or not valid
- `402`: The balance can't cover another keyword-day (insufficient_balance), or the workspace already runs 500 keywords (keyword_limit_reached)
- `404`: No keyword with this id
- `409`: Renamed to, or moved into a group with, a term that group already tracks

---

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