Every API here is available over the APIs.io API and to AI agents over MCP.
openapi: 3.2.0
info:
title: Rumoro Usage API
version: 0.0.1
description: Social listening for developers and AI agents.
servers:
- url: https://api.rumoro.dev
tags:
- name: Usage
paths:
/v1/usage:
get:
operationId: getUsage
summary: Get usage and balance
description: Shows whether tracking is stopped or the balance is low. Also returns the prepaid balance (ledger total, pending mention charges, and the effective balance that decides pausing), daily spend and days left, running and paused keywords, and matches today and over 30 days. Pricing is $0.008 per matched mention, relevant or not, and $5 a month per active keyword, charged daily.
tags:
- Usage
security:
- bearerAuth: []
responses:
'200':
description: A summary of usage
content:
application/json:
schema:
$ref: '#/components/schemas/UsageSummary'
'401':
description: The API key is missing or not valid
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
/v1/usage/breakdown:
get:
operationId: getUsageBreakdown
summary: Get the usage breakdown
description: Returns what the workspace used and was charged over a period, in US cents at list price. Each call groups rows by one `by` value, day, platform or keyword, and always includes the totals. `range` reads the past UTC days up to today (30d by default). `month` reads one calendar month (YYYY-MM), which is what you match against a bill or a per-client margin. Keyword-days come from the daily run and mention charges from billed matches. So a deleted keyword keeps its charges in the keyword rows (`keyword.removed`), while its mention counts show 0. Each keyword carries the same numbers for the current month as `stats.cost`. `totals.ledgerDebitCents` is what the balance has been debited so far for the period's days. Mentions settle the morning after, so a period ending today is below `totals.totalCents` by the unsettled ones, and a finished month differs only by cumulative rounding. Rows come in pages (`limit`, `offset`, `total`). A workspace can call this 30 times a minute across all its keys and tokens.
tags:
- Usage
security:
- bearerAuth: []
parameters:
- schema:
type: string
enum:
- day
- platform
- keyword
- group
default: keyword
description: How to group the rows. day gives one row per UTC day, then platform, keyword (the default, used to work out margins), or group (the cost of a client or campaign).
required: false
description: How to group the rows. day gives one row per UTC day, then platform, keyword (the default, used to work out margins), or group (the cost of a client or campaign).
name: by
in: query
- schema:
type: string
enum:
- 7d
- 30d
- 90d
description: How many UTC days back from today to read. One of 7d, 30d or 90d, 30d by default. Ignored when you send `month`.
required: false
description: How many UTC days back from today to read. One of 7d, 30d or 90d, 30d by default. Ignored when you send `month`.
name: range
in: query
- schema:
type: string
pattern: ^\d{4}-(0[1-9]|1[0-2])$
description: A calendar month (YYYY-MM, UTC) to read instead of a range. It covers the month's first to last day, or up to today for the current month. A future month returns 400.
required: false
description: A calendar month (YYYY-MM, UTC) to read instead of a range. It covers the month's first to last day, or up to today for the current month. A future month returns 400.
name: month
in: query
- schema:
type: integer
minimum: 1
maximum: 500
default: 100
description: How many rows to return, from 1 to 500, 100 by default. Only by=keyword can need more than one page, since a period has at most 90 days and there are only a dozen platforms.
required: false
description: How many rows to return, from 1 to 500, 100 by default. Only by=keyword can need more than one page, since a period has at most 90 days and there are only a dozen platforms.
name: limit
in: query
- schema:
type:
- integer
- 'null'
minimum: 0
default: 0
description: How many rows to skip.
required: false
description: How many rows to skip.
name: offset
in: query
responses:
'200':
description: The period's totals and a page of rows
content:
application/json:
schema:
$ref: '#/components/schemas/UsageBreakdown'
'400':
description: Invalid by, range, month or limit. A future month is invalid too
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'401':
description: The API key is missing or not valid
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'429':
description: The workspace read the breakdown over 30 times in this minute
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
components:
schemas:
ErrorResponse:
type: object
properties:
error:
type: object
properties:
code:
type: string
enum:
- unauthorized
- forbidden
- read_only_key
- validation_error
- not_found
- invalid_cursor
- payload_too_large
- rate_limited
- duplicate_keyword
- insufficient_balance
- keyword_limit_reached
- billing_not_configured
- unknown_channel
- not_a_digest
- hourly_email_unsupported
- slack_not_connected
- slack_not_configured
- telegram_not_configured
- email_not_configured
- invalid_assignee
- classification_pending
- already_member
- last_owner
- duplicate_segment
- duplicate_view
- duplicate_group
- default_group
- default_group_context
- invalid_signature
- invalid_token
- upstream_unavailable
- internal_error
- unsupported_media_type
- request_timeout
- key_limit
- conflict
- no_billing_owner
- rule_disabled
- no_channels
- channel_disabled
- no_recipients
- recipient_suppressed
- confirmation_rate_limited
- confirmation_requires_resend
- email_test_rate_limited
- email_hourly_limit
- email_idempotency_expired
- email_transport_unavailable
- delivery_not_configured
- delivery_key_unavailable
- not_retryable
- not_replayable
- digest_has_replay
- digest_retry_expired
- delivery_request_incomplete
- request_conflict
- stale_version
- version_conflict
- already_classified
- keyword_inactive
- source_unavailable
- source_timeout
- invalid_host
- invalid_origin
- database_unavailable
- database_not_ready
- not_configured
description: A stable code for programs. Branch on it, not on the message. New codes may be added, so treat an unknown one as a general failure with the same status.
message:
type: string
description: A description for people. Its wording can change.
requestId:
type: string
description: This request's id, also sent in the X-Request-Id header. Include it when you contact support.
retryAfterSeconds:
type: integer
description: Seconds to wait before retrying, on rate_limited and other errors you can retry. Same value as the Retry-After header.
required:
- code
- message
description: Details of the error.
required:
- error
UsageBreakdown:
type: object
properties:
window:
type: object
properties:
from:
type: string
description: Start date (UTC, YYYY-MM-DD), included.
to:
type: string
description: The last day included. It is today for a trailing range or the current month.
days:
type: integer
minimum: 0
description: How many days the window spans.
keywordDaysFrom:
type:
- string
- 'null'
description: 'Earliest day in the window with a keyword count, or null. Earlier days show `keywordDays: null`.'
required:
- from
- to
- days
- keywordDaysFrom
description: Report range in UTC days.
by:
type: string
enum:
- day
- platform
- keyword
- group
description: Grouping used for the rows.
currency:
type: string
enum:
- USD
description: All amounts are in US cents.
totals:
type: object
properties:
keywordDays:
type: integer
minimum: 0
description: Sum of keyword-days over the window.
keywordCents:
type: integer
description: Those keyword-days priced at $5 a month (500/30 cents a day), with the total rounded once.
matchedMentions:
type: integer
minimum: 0
description: Match count for the window, relevant or not.
billableMentions:
type: integer
minimum: 0
description: Matches billed in the window that fall in this row. Every scored match is billed, relevant or not.
mentionCents:
type: integer
description: Those mentions priced at $0.008 each, with the total rounded once.
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.
unclassifiedMentions:
type: integer
minimum: 0
description: Matches whose classification failed. They are never charged.
ledgerDebitCents:
type: integer
description: Debits recorded so far for the window's days, each on its settlement day. Mentions settle the next morning, so a window ending today trails totalCents by today's mentions, plus yesterday's before the 00:05 UTC run. A closed month differs only by cumulative rounding.
unattributedBillable:
type: integer
minimum: 0
description: Billed mentions whose match was removed with a deleted keyword, so they belong to no platform or keyword row. They were still charged.
required:
- keywordDays
- keywordCents
- matchedMentions
- billableMentions
- mentionCents
- billableComments
- commentCents
- totalCents
- unclassifiedMentions
- ledgerDebitCents
- unattributedBillable
description: Totals for the whole window. They are the same whatever `by` is.
data:
type: array
items:
type: object
properties:
key:
type: string
description: What the row stands for. A UTC day (YYYY-MM-DD) with by=day, a platform with by=platform, a keyword id with by=keyword and a group id with by=group.
label:
type: string
description: A name to display. The keyword's term or the group's name, or else the key.
keyword:
type:
- object
- 'null'
properties:
id:
type: string
description: The keyword's id (kw_...). Deleted keywords keep their id here.
term:
type: string
description: The term when it was last charged, or its current term.
removed:
type: boolean
description: The keyword was deleted later. Charges remain, but its mentions are gone, so mention counts are 0.
required:
- id
- term
- removed
description: Set only with by=keyword. Null otherwise.
group:
type:
- object
- 'null'
properties:
id:
type: string
description: The group's id. It is "none" for keyword-days charged before groups existed when the keyword has since been deleted.
name:
type: string
description: Current group name, or "Deleted group" or "No group".
removed:
type: boolean
description: True when the group was deleted later.
required:
- id
- name
- removed
description: Set only with by=group. Null otherwise.
keywordDays:
type:
- integer
- 'null'
minimum: 0
description: Keyword-days charged in this row by the daily run. Null with by=platform, since a keyword-day has no platform, and for days before the first recorded run, where the count is unknown rather than zero. A keyword row only counts recorded days, so before window.keywordDaysFrom it is a minimum, not zero.
keywordCents:
type: integer
description: Those keyword-days priced at $5 a month (500/30 cents a day), with the total rounded once.
matchedMentions:
type: integer
minimum: 0
description: Match count for the window, relevant or not.
billableMentions:
type: integer
minimum: 0
description: Matches billed in the window that fall in this row. Every scored match is billed, relevant or not.
mentionCents:
type: integer
description: Those mentions priced at $0.008 each, with the total rounded once.
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:
- key
- label
- keyword
- group
- keywordDays
- keywordCents
- matchedMentions
- billableMentions
- mentionCents
- billableComments
- commentCents
- totalCents
description: With by=day the rows are in date order. With by=platform and by=keyword the most expensive come first, then those with the most matches, deleted keywords included.
total:
type: integer
minimum: 0
description: The number of rows before `limit` and `offset` are applied.
required:
- window
- by
- currency
- totals
- data
- total
UsageSummary:
type: object
properties:
balance:
type: object
properties:
cents:
type: integer
description: The ledger balance, all credits minus all settled debits.
pendingCents:
type: integer
description: Cost of mentions matched after the last daily settlement. Not debited yet.
effectiveCents:
type: integer
description: cents minus pendingCents. Pausing at zero and the check before adding keywords both use this number.
currency:
type: string
enum:
- USD
required:
- cents
- pendingCents
- effectiveCents
- currency
description: The prepaid balance.
burn:
type: object
properties:
perDayCents:
type: integer
description: Mean daily debit across the past 7 days, or the workspace's lifetime if shorter.
daysLeft:
type:
- integer
- 'null'
minimum: 0
description: effectiveCents over perDayCents, rounded down. Null when perDayCents is 0.
required:
- perDayCents
- daysLeft
description: How quickly the balance is being spent.
keywords:
type: object
properties:
active:
type: integer
minimum: 0
description: Keywords that are not muted. These are charged each day.
paused:
type: integer
minimum: 0
description: Keywords paused because the balance ran out. A top-up resumes them.
capped:
type: integer
minimum: 0
description: Keywords that are not muted but reached their monthly mention cap. They are still charged each day and are not matched until the next month or until the cap is raised.
limit:
type: integer
minimum: 0
description: Keywords the workspace may run now. 0 if the balance can't cover one more keyword-day, otherwise the self-serve maximum.
dayCents:
type: integer
description: Daily cost of the active keywords.
required:
- active
- paused
- capped
- limit
- dayCents
description: Keyword counts compared with the balance.
mentions:
type: object
properties:
today:
type: integer
minimum: 0
description: Count of matches since midnight UTC, relevant or not.
last30d:
type: integer
minimum: 0
description: Match count for the past 30 days.
required:
- today
- last30d
description: Matched mentions, which are billed along with keyword-days.
stopped:
type: boolean
description: True when tracking is paused because the balance ran out. It resumes after a top-up that covers one day of every keyword.
lowBalance:
type: boolean
description: true when 20 percent or less of the last credit remains (effective balance) and tracking is on.
lastTopUpAt:
type:
- string
- 'null'
description: Latest paid top-up time, or null if there is none.
required:
- balance
- burn
- keywords
- mentions
- stopped
- lowBalance
- lastTopUpAt
securitySchemes:
bearerAuth:
type: http
scheme: bearer
description: An API key from POST /v1/api-keys. Keys start with ref_.
x-rumoro-errors:
- code: unauthorized
status: 401
source: user
retryable: false
description: The credential is missing or does not belong to a workspace.
fix: 'Send Authorization: Bearer ref_… with a key from /api-keys.'
- code: forbidden
status: 403
source: user
retryable: false
description: The credential works but is not allowed to do this.
- code: read_only_key
status: 403
source: user
retryable: false
description: A read-only key called an endpoint that writes.
fix: Create a key with write scope.
- code: validation_error
status: 400
source: user
retryable: false
description: The body or query is not valid. The message says which field.
- code: not_found
status: 404
source: user
retryable: false
description: Your workspace has nothing with this id. An id from another workspace also returns 404, never 403.
- code: invalid_cursor
status: 400
source: user
retryable: false
description: The cursor could not be read, or was issued for another sort. Start again from the first page.
- code: payload_too_large
status: 413
source: user
retryable: false
description: 'The body exceeds the endpoint limit (128 KiB for JSON). A 409 with this code: a delivery whose payload was too large cannot be retried or replayed.'
- code: rate_limited
status: 429
source: user
retryable: true
description: The workspace sent over 600 requests in a minute across all its keys and tokens, or reached an endpoint's own limit. Retry-After and error.retryAfterSeconds tell you when to try again.
fix: Wait Retry-After seconds and send the same request again. X-RateLimit-Remaining on every response says how much room is left.
- code: duplicate_keyword
status: 409
source: user
retryable: false
description: This group already has the term. Each group can hold a term once, and other groups can hold it too.
- code: insufficient_balance
status: 402
source: user
retryable: false
description: The balance cannot pay for one more keyword-day.
fix: Top up from Billing; tracking resumes on its own.
- code: keyword_limit_reached
status: 402
source: user
retryable: false
description: The workspace already runs 500 keywords, or has 500 that are not deleted.
- code: billing_not_configured
status: 503
source: system
retryable: false
description: No payment provider is set up on this deployment.
- code: unknown_channel
status: 400
source: user
retryable: false
description: An alert named a channel id that is not in this workspace.
- code: not_a_digest
status: 400
source: user
retryable: false
description: An action for digests was used on an instant alert.
- code: hourly_email_unsupported
status: 400
source: user
retryable: false
description: An hourly alert got an email channel, which could mean 24 emails a day. Use Slack, Telegram or a webhook, or send a daily digest by email.
- code: slack_not_connected
status: 409
source: user
retryable: false
description: Slack must be connected in Settings first. Returned as 404 by the Slack connection endpoints and 409 elsewhere.
- code: slack_not_configured
status: 503
source: system
retryable: false
description: No Slack app is set up on this deployment.
- code: telegram_not_configured
status: 503
source: system
retryable: false
description: No Telegram bot is set up on this deployment.
- code: email_not_configured
status: 503
source: system
retryable: false
description: This deployment has no email sender set up, so email destinations cannot be used.
- code: invalid_assignee
status: 400
source: user
retryable: false
description: The assignee is not in the workspace.
- code: classification_pending
status: 409
source: user
retryable: true
description: Relevance or sentiment feedback was sent before the classifier scored the mention.
- code: already_member
status: 409
source: user
retryable: false
description: The invited address already belongs to a member.
- code: last_owner
status: 409
source: user
retryable: false
description: The workspace would have no owner left after this removal.
- code: duplicate_segment
status: 409
source: user
retryable: false
description: Another segment in the workspace has this name.
- code: duplicate_view
status: 409
source: user
retryable: false
description: Another view in the workspace has this name.
- code: duplicate_group
status: 409
source: user
retryable: false
description: Another group in the workspace has this name or externalId.
- code: default_group
status: 409
source: user
retryable: false
description: The default group cannot be deleted. It holds keywords created without a group.
- code: default_group_context
status: 400
source: user
retryable: false
description: The default group uses the workspace's company profile, which you change with PATCH /v1/company. Only other groups can have their own description.
- code: invalid_signature
status: 401
source: user
retryable: false
description: An incoming webhook had no valid signature.
- code: invalid_token
status: 400
source: user
retryable: false
description: A signed link (unsubscribe, confirmation, preferences) was tampered with or is malformed.
- code: upstream_unavailable
status: 502
source: platform
retryable: true
description: An outside service such as Slack or Polar failed during the request. Retry with increasing waits.
- code: internal_error
status: 500
source: system
retryable: false
description: Something failed on our side; 502 when Telegram failed a bot lookup. The response carries a requestId; quote it to support.
- code: unsupported_media_type
status: 415
source: user
retryable: false
description: 'A body was sent without Content-Type: application/json.'
- code: request_timeout
status: 408
source: user
retryable: true
description: The body did not arrive within 5 seconds.
- code: key_limit
status: 409
source: user
retryable: false
description: The workspace already has 100 active keys. Revoke one first.
- code: conflict
status: 409
source: user
retryable: false
description: A resource with these values already exists.
- code: no_billing_owner
status: 404
source: user
retryable: false
description: There is no owner to bill in this workspace yet.
- code: rule_disabled
status: 409
source: user
retryable: false
description: The rule is turned off. Enable it before a test or a run.
- code: no_channels
status: 409
source: user
retryable: false
description: The rule has no destination to test.
- code: channel_disabled
status: 409
source: user
retryable: false
description: A destination is turned off. Enable it first.
- code: no_recipients
status: 409
source: user
retryable: false
description: An email destination needs a recipient before it can be turned on.
- code: recipient_suppressed
status: 409
source: user
retryable: false
description: The address bounced or complained, so no more email goes to it.
- code: confirmation_rate_limited
status: 429
source: user
retryable: true
description: 'Too many confirmation emails: wait a minute; at most three per address a day and twenty per workspace an hour.'
- code: confirmation_requires_resend
status: 409
source: user
retryable: false
description: A confirmation email is sent again with a resend, not a delivery retry.
- code: email_test_rate_limited
status: 429
source: user
retryable: true
description: More than five email tests on this destination this hour.
- code: email_hourly_limit
status: 409
source: user
retryable: false
description: The destination's hourly email limit is reached; the mention waits for the next daily digest.
- code: email_idempotency_expired
status: 409
source: user
retryable: false
description: The email's safe retry window passed. Check its receipts before an explicit replay, which may send it twice.
- code: email_transport_unavailable
status: 503
source: system
retryable: false
description: Email sending is not set up for this delivery on this deployment.
- code: delivery_not_configured
status: 503
source: system
retryable: false
description: Destinations that need a stored secret are not set up on this deployment.
- code: delivery_key_unavailable
status: 503
source: system
retryable: false
description: A destination's stored secret cannot be read on this deployment.
- code: not_retryable
status: 409
source: user
retryable: false
description: Only failed or blocked deliveries can be retried.
- code: not_replayable
status: 409
source: user
retryable: false
description: A delivery can be replayed once it has finished.
- code: digest_has_replay
status: 409
source: user
retryable: false
description: This digest delivery has a newer replay. Recover that one.
- code: digest_retry_expired
status: 409
source: user
retryable: false
description: The digest's six-hour retry window passed. Replay it instead.
- code: delivery_request_incomplete
status: 500
source: system
retryable: true
description: The saved outcome of a test could not be read. Read it again; don't send a new test.
- code: request_conflict
status: 409
source: user
retryable: false
description: This requestId was already used for another request. Retry the original, or use a new id.
- code: stale_version
status: 409
source: user
retryable: false
description: The resource changed since you read it. Read it again before saving.
- code: version_conflict
status: 409
source: user
retryable: true
description: The webhook's managed rules changed at the same time. Read it again and retry.
- code: already_classified
status: 409
source: user
retryable: false
description: The mention is already scored; there is nothing to retry.
- code: keyword_inactive
status: 409
source: user
retryable: false
description: The mention's keyword is paused. Resume it before retrying the score.
- code: source_unavailable
status: 502
source: platform
retryable: true
description: 'Live search: the platform didn''t answer (503 when it is rate limiting us).'
- code: source_timeout
status: 504
source: platform
retryable: true
description: 'Live search: the platform didn''t answer in time.'
- code: invalid_host
status: 403
source: user
retryable: false
description: 'MCP: the request''s Host is not this server''s.'
- code: invalid_origin
status: 403
source: user
retryable: false
description: 'MCP: the request''s Origin is not allowed.'
- code: database_unavailable
status: 503
source: system
retryable: true
description: A temporary database problem. Retry with backoff.
- code: database_not_ready
status: 503
source: system
retryable: false
description: The deployment's database needs maintenance; retrying won't help until it is done.
- code: not_configured
status: 503
source: system
retryable: false
description: This deployment has no database configured.