Properties
| Name | Type | Description |
|---|---|---|
| keyword | object | The keyword this report covers. |
| window | object | The days the report covers, in UTC and by match time. |
| status | string | The first that applies wins. paused when you, the wallet or the noise brake muted it. capped at the monthly mention cap. noisy with 20+ scored matches in the window and under 30% relevant. new when un |
| reasons | array | Plain-word explanation, status first. |
| stats | object | Based on this keyword's matches in the window. |
| sample | object | The posts used for noiseTerms, noiseAuthors and the effects. Only posts the keyword's current rules accept count, not ones an older rule let in. Review platforms match by app rather than text and are |
| noiseTerms | array | Up to 10 words or phrases that show up far more in noise than in relevant posts, strongest first. Words of the keyword itself are skipped. |
| noiseAuthors | array | Authors in the sample with no relevant post and 3 or more noise posts. |
| suggestions | array | Suggested noise fixes, each a body for PATCH /v1/keywords/{id}. The list is empty if no fix helps, or if fewer than 20 matches were scored after the last change. A reason explains which. |
| ai | object | The optional part of the report written by a language model. |
| generatedAt | string | When the report was built. Reports are cached for 5 minutes, and changing the keyword builds a new one. |
JSON Schema
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://artifacts.apis.io/rumoro-dev/json-schema/rumoro-dev-keyword-health-schema.json",
"title": "KeywordHealth",
"x-generated": "2026-10-31",
"x-method": "derived",
"x-generator": "derive-json-schema.py",
"x-source": "openapi/rumoro-dev-openapi.json#/components/schemas/KeywordHealth",
"type": "object",
"properties": {
"keyword": {
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "The keyword's id (kw_...)."
},
"term": {
"type": "string",
"description": "The term exactly as entered."
},
"group": {
"$ref": "#/$defs/GroupRef"
}
},
"required": [
"id",
"term",
"group"
],
"description": "The keyword this report covers."
},
"window": {
"type": "object",
"properties": {
"from": {
"type": "string",
"description": "Start date (UTC, YYYY-MM-DD), included."
},
"to": {
"type": "string",
"description": "The last day included, which is today."
},
"days": {
"type": "integer",
"minimum": 0,
"description": "How many days the window spans."
}
},
"required": [
"from",
"to",
"days"
],
"description": "The days the report covers, in UTC and by match time."
},
"status": {
"type": "string",
"enum": [
"healthy",
"noisy",
"quiet",
"capped",
"paused",
"new"
],
"description": "The first that applies wins. paused when you, the wallet or the noise brake muted it. capped at the monthly mention cap. noisy with 20+ scored matches in the window and under 30% relevant. new when under 7 days old, or changed in the last 7 days with under 20 scored matches since. quiet when 7+ days old with nothing relevant in the window. Otherwise healthy. After a rules, platforms or context change, or an unmute, inside the window, only later matches count (stats.judgedSince)."
},
"reasons": {
"type": "array",
"items": {
"type": "string"
},
"description": "Plain-word explanation, status first."
},
"stats": {
"type": "object",
"properties": {
"matches": {
"type": "integer",
"minimum": 0,
"description": "Match count for the window, relevant or not."
},
"relevant": {
"type": "integer",
"minimum": 0,
"description": "Matches with a relevance score of 40 or more."
},
"filtered": {
"type": "integer",
"minimum": 0,
"description": "Matches scored below 40, the noise. They are billed like any other match."
},
"unscored": {
"type": "integer",
"minimum": 0,
"description": "Matches not scored yet, or whose scoring failed. A failed one is never billed."
},
"noiseShare": {
"type": [
"number",
"null"
],
"minimum": 0,
"maximum": 1,
"description": "Share of scored matches that were filtered. Null if none were scored."
},
"workspaceShare": {
"type": [
"number",
"null"
],
"minimum": 0,
"maximum": 1,
"description": "The part of the workspace's matches in the window that came from this keyword. Null when the workspace had no matches."
},
"judgedSince": {
"type": [
"string",
"null"
],
"description": "The last change to the keyword's matching rules, platforms or context, or its last unmute, if that falls inside the window. The status, reasons, noiseTerms, noiseAuthors and suggestions then only look at matches since this time. These numbers still cover the whole window. Null means everything covers the whole window."
},
"byPlatform": {
"type": "array",
"items": {
"type": "object",
"properties": {
"platform": {
"type": "string",
"enum": [
"bluesky",
"hackernews",
"github",
"stackoverflow",
"devto",
"reddit",
"x",
"youtube",
"news",
"linkedin",
"tiktok",
"instagram",
"appstore",
"googleplay",
"trustpilot",
"googlemaps",
"rss"
],
"description": "The platform. Review platforms are appstore, googleplay, trustpilot and googlemaps (a place's Google reviews)."
},
"matches": {
"type": "integer",
"minimum": 0,
"description": "This platform's match count for the window."
},
"relevant": {
"type": "integer",
"minimum": 0,
"description": "How many of them scored 40 or more."
},
"filtered": {
"type": "integer",
"minimum": 0,
"description": "How many of them scored below 40, the noise."
},
"noiseShare": {
"type": [
"number",
"null"
],
"minimum": 0,
"maximum": 1,
"description": "Share of scored matches that were filtered. Null if none were scored."
}
},
"required": [
"platform",
"matches",
"relevant",
"filtered",
"noiseShare"
]
},
"description": "A row for each platform with matches, the busiest first."
},
"weekly": {
"type": "array",
"items": {
"type": "object",
"properties": {
"from": {
"type": "string",
"description": "Week start as YYYY-MM-DD. Weeks run in 7-day steps from the start of the window."
},
"matches": {
"type": "integer",
"minimum": 0,
"description": "Matches during these 7 days."
},
"relevant": {
"type": "integer",
"minimum": 0,
"description": "How many of them were relevant."
}
},
"required": [
"from",
"matches",
"relevant"
]
},
"description": "The trend in rows of 7 days, oldest first. The last row can be shorter."
},
"cost": {
"type": "object",
"properties": {
"keywordDays": {
"type": "integer",
"minimum": 0,
"description": "Charged days in the window."
},
"keywordCents": {
"type": "integer",
"description": "What keywordDays cost at the keyword rate."
},
"billableMentions": {
"type": "integer",
"minimum": 0,
"description": "Billed matches in the window, dated by scoring time."
},
"mentionCents": {
"type": "integer",
"description": "What billableMentions cost at the mention rate."
},
"billableComments": {
"type": "integer",
"minimum": 0,
"description": "Comments billed on the comments line: comments delivered under mentions and matched replies, each once per workspace."
},
"commentCents": {
"type": "integer",
"description": "Those comments at $0.008 each, rounded once on the total."
},
"totalCents": {
"type": "integer",
"description": "keywordCents plus mentionCents plus commentCents."
}
},
"required": [
"keywordDays",
"keywordCents",
"billableMentions",
"mentionCents",
"billableComments",
"commentCents",
"totalCents"
],
"description": "List-price cost of this keyword in the window."
}
},
"required": [
"matches",
"relevant",
"filtered",
"unscored",
"noiseShare",
"workspaceShare",
"judgedSince",
"byPlatform",
"weekly",
"cost"
],
"description": "Based on this keyword's matches in the window."
},
"sample": {
"type": "object",
"properties": {
"noise": {
"type": "integer",
"minimum": 0,
"description": "Noise posts that were read, up to 200 of the newest in the window or since stats.judgedSince, from platforms matched by text. Zero when fewer than 20 matches were scored since a change."
},
"relevant": {
"type": "integer",
"minimum": 0,
"description": "Relevant posts that were read, chosen the same way."
}
},
"required": [
"noise",
"relevant"
],
"description": "The posts used for noiseTerms, noiseAuthors and the effects. Only posts the keyword's current rules accept count, not ones an older rule let in. Review platforms match by app rather than text and are left out."
},
"noiseTerms": {
"type": "array",
"items": {
"type": "object",
"properties": {
"term": {
"type": "string",
"description": "One word or a phrase of two words."
},
"noisePosts": {
"type": "integer",
"minimum": 0,
"description": "Noise posts in the sample that contain it."
},
"relevantPosts": {
"type": "integer",
"minimum": 0,
"description": "Relevant posts in the sample that contain it."
},
"lift": {
"type": [
"number",
"null"
],
"description": "Ratio of the term's rate in noise to its rate in relevant posts, smoothed. Null without a relevant post."
}
},
"required": [
"term",
"noisePosts",
"relevantPosts",
"lift"
]
},
"description": "Up to 10 words or phrases that show up far more in noise than in relevant posts, strongest first. Words of the keyword itself are skipped."
},
"noiseAuthors": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "The author's name as shown on the platform."
},
"entry": {
"type": "string",
"description": "The entry matching.excludedAuthors would store for this author."
},
"noisePosts": {
"type": "integer",
"minimum": 0,
"description": "Noise posts in the sample written by this author."
}
},
"required": [
"name",
"entry",
"noisePosts"
]
},
"description": "Authors in the sample with no relevant post and 3 or more noise posts."
},
"suggestions": {
"type": "array",
"items": {
"$ref": "#/$defs/KeywordSuggestion"
},
"description": "Suggested noise fixes, each a body for PATCH /v1/keywords/{id}. The list is empty if no fix helps, or if fewer than 20 matches were scored after the last change. A reason explains which."
},
"ai": {
"type": "object",
"properties": {
"requested": {
"type": "boolean",
"description": "True when the request had ai=true."
},
"status": {
"type": "string",
"enum": [
"off",
"generated",
"cached",
"unavailable",
"rate_limited"
],
"description": "off means it was not requested. generated means it was written for this request. cached means it was written earlier today for the same keyword, window and settings. unavailable means the model failed, gave nothing usable or is not set up. rate_limited means the workspace used its 20 model calls for this hour."
}
},
"required": [
"requested",
"status"
],
"description": "The optional part of the report written by a language model."
},
"generatedAt": {
"type": "string",
"description": "When the report was built. Reports are cached for 5 minutes, and changing the keyword builds a new one."
}
},
"required": [
"keyword",
"window",
"status",
"reasons",
"stats",
"sample",
"noiseTerms",
"noiseAuthors",
"suggestions",
"ai",
"generatedAt"
],
"$defs": {
"GroupRef": {
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "The group's id (grp_...)."
},
"name": {
"type": "string",
"description": "Name of the group."
},
"externalId": {
"type": [
"string",
"null"
],
"description": "An id from your own system, or null."
},
"isDefault": {
"type": "boolean",
"description": "True for the workspace's default group. New keywords go there when you don't name a group."
}
},
"required": [
"id",
"name",
"externalId",
"isDefault"
],
"description": "The keyword's group."
},
"KeywordSuggestion": {
"type": "object",
"properties": {
"type": {
"type": "string",
"enum": [
"excluded_terms",
"excluded_authors",
"required_terms",
"platforms",
"context"
],
"description": "What the suggestion changes. The excluded_* types add to the matching rules, required_terms swaps the required list, platforms drops the platforms that bring nearly only noise, and context rewrites the classifier's sentence."
},
"values": {
"type": "array",
"items": {
"type": "string"
},
"description": "The terms or authors to add, the platforms to remove, or the new context."
},
"why": {
"type": "string",
"description": "Why, and what the change would have done, in plain words."
},
"patch": {
"type": "object",
"properties": {
"term": {
"type": "string",
"minLength": 2,
"maxLength": 80,
"description": "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": {
"type": "string",
"enum": [
"brand",
"competitor",
"topic"
],
"description": "New kind (brand, competitor or topic)."
},
"muted": {
"type": "boolean",
"description": "Muting stops polling and matching. Existing mentions are kept."
},
"platforms": {
"type": [
"array",
"null"
],
"items": {
"type": "string",
"enum": [
"bluesky",
"hackernews",
"github",
"stackoverflow",
"devto",
"reddit",
"x",
"youtube",
"news",
"linkedin",
"tiktok",
"instagram"
]
},
"description": "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": {
"type": "array",
"items": {
"type": "object",
"properties": {
"url": {
"type": "string",
"minLength": 4,
"maxLength": 500,
"description": "A feed's address (https://forum.example.com/posts.rss), or a page's (https://forum.example.com), whose advertised feed is used."
}
},
"required": [
"url"
],
"description": "One feed to read."
},
"maxItems": 20,
"description": "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)."
},
"context": {
"type": [
"string",
"null"
],
"maxLength": 300,
"description": "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": {
"type": "object",
"properties": {
"requiredTerms": {
"type": "array",
"items": {
"type": "string",
"minLength": 1,
"maxLength": 80
},
"maxItems": 50,
"description": "Terms the post must contain as well. requiredMode says whether one or all of them must appear. An empty list sets no requirement."
},
"requiredMode": {
"type": "string",
"enum": [
"any",
"all"
],
"description": "any needs one of the required terms in the post. all needs every one."
},
"excludedTerms": {
"type": "array",
"items": {
"type": "string",
"minLength": 1,
"maxLength": 80
},
"maxItems": 50,
"description": "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": {
"type": "array",
"items": {
"type": "string",
"minLength": 1,
"maxLength": 500
},
"maxItems": 100,
"description": "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": {
"type": "boolean",
"description": "When true, the term only matches in the exact case you typed, so RAG never matches rag. Defaults to false."
}
},
"description": "Fields you leave out stay as they are. An empty list clears a field."
},
"cap": {
"type": [
"object",
"null"
],
"properties": {
"mentions": {
"type": "integer",
"minimum": 1,
"maximum": 1000000,
"description": "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."
}
},
"required": [
"mentions"
],
"description": "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."
},
"comments": {
"type": "object",
"properties": {
"enabled": {
"type": "boolean",
"description": "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": {
"type": "integer",
"minimum": 1,
"maximum": 100,
"description": "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."
}
},
"description": "Comments under this keyword's mentions; omitted fields stay as they are (on create: off, 20 per mention)."
},
"groupId": {
"type": "string",
"minLength": 1,
"description": "Target group (grp_...). 409 if it already has this term."
},
"reviewSources": {
"type": "array",
"items": {
"type": "object",
"properties": {
"url": {
"type": "string",
"minLength": 1,
"maxLength": 500,
"description": "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": {
"type": "string",
"enum": [
"appstore",
"googleplay",
"trustpilot",
"googlemaps"
],
"description": "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": {
"type": "string",
"minLength": 1,
"maxLength": 200,
"description": "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": {
"type": "array",
"items": {
"type": "string",
"pattern": "^[A-Za-z]{2}$"
},
"minItems": 1,
"maxItems": 20,
"description": "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": {
"type": "string",
"minLength": 2,
"maxLength": 10,
"description": "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."
}
},
"description": "A review page, by link or by platform and id. Its reviews become this keyword's mentions."
},
"maxItems": 10,
"description": "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."
}
},
"description": "Send this body unchanged to PATCH /v1/keywords/{id} to apply the suggestion. Lists hold the full new list, with the current entries included."
},
"effect": {
"type": [
"object",
"null"
],
"properties": {
"noiseRemoved": {
"type": "integer",
"minimum": 0,
"description": "How many noise matches the change would have removed in the window (since stats.judgedSince if set). Sampled unless exact is true."
},
"relevantRemoved": {
"type": "integer",
"minimum": 0,
"description": "How many relevant matches the change would have cut from the window."
},
"centsSaved": {
"type": "integer",
"description": "Money the change would have saved in the window, at the mention rate for each removed match."
},
"sample": {
"type": "object",
"properties": {
"noiseRemoved": {
"type": "integer",
"minimum": 0,
"description": "Noise posts in the sample that the change would reject."
},
"noise": {
"type": "integer",
"minimum": 0,
"description": "Noise posts in the sample."
},
"relevantRemoved": {
"type": "integer",
"minimum": 0,
"description": "Relevant posts in the sample that the change would reject."
},
"relevant": {
"type": "integer",
"minimum": 0,
"description": "Relevant posts in the sample."
}
},
"required": [
"noiseRemoved",
"noise",
"relevantRemoved",
"relevant"
],
"description": "Counts behind the estimate. The matcher's rules were run on each sampled post."
},
"exact": {
"type": "boolean",
"description": "True when the sample covered the whole window, so the counts are exact."
}
},
"required": [
"noiseRemoved",
"relevantRemoved",
"centsSaved",
"sample",
"exact"
],
"description": "The result of replaying the change on the window's matches with the matcher's rules. Null for context suggestions, which alter scores rather than matches."
},
"source": {
"type": "string",
"enum": [
"rules",
"ai"
],
"description": "rules means it was computed from the posts in the window. ai means a language model wrote it (with ai=true)."
}
},
"required": [
"type",
"values",
"why",
"patch",
"effect",
"source"
]
}
}
}
Work with this as data
Every JSON Schema here is available over the APIs.io API and to AI agents over MCP.
MCP server
One button, every client — Claude, Cursor, VS Code and the rest.
https://apis.io/mcp
Tools for schemas
4 MCP tools reach this
find_json_schemasBrowse and filter every JSON Schema in the catalog.apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.resolveTurn a domain, URL or GitHub org into the provider it belongs to.find_cohortsEvery scored population of providers in the catalog.
Call it yourself
curl for this page
This JSON Schema
curl "https://apis.io/api/v1/json-schemas/rumoro-dev-keyword-health"
All schemas
curl "https://apis.io/api/v1/json-schemas?limit=25"
Discovery needs no key. Ratings and market analysis are Pro.
Get an API key
Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.
A second provider on the same verified email joins the account you already have.