---
title: "Billing"
description: "How Rumoro's prepaid balance works, what keywords and mentions cost, and how to read your wallet and ledger through the API."
canonical: https://docs.rumoro.dev/billing
markdown: https://docs.rumoro.dev/billing.mdx
---

# Billing

How Rumoro's prepaid balance works, what keywords and mentions cost, and how to read your wallet and ledger through the API.

You pay for what you use from a prepaid balance. That means the keywords you track and the mentions Rumoro finds for them. There are no seats, plans, subscriptions or bundles. You add money in any amount, and the balance goes down a little every day.

All amounts in the API are whole US cents, so `500` means $5.00.

## Prices

| What | Price | How it is charged |
| --- | --- | --- |
| Keywords | $5 per keyword per month | Charged per day at $5/30. A keyword tracked for 12 days costs 12/30 of a month. |
| Mentions | $0.008 per matched mention | Every match counts, relevant or not. Keywords don't include any mentions. |

A mention is charged once it matches a keyword and has been scored, whether it turns out relevant or noise. Mentions that fail scoring are free, and so are the reviews a new review page brings in.

Charges are settled once a day, shortly after midnight UTC. Until then new matches show as `pendingCents`. The pause rule and the keyword check use `balanceCents - pendingCents`.

[`GET /v1/usage`](/api/usage/get-usage) gives you the overview in one call. It returns the balance (ledger, pending and effective), what a day costs and how many days are left, how many keywords run or are paused, matches today and over 30 days, and whether tracking is paused or the balance is low.

## Cost per keyword

[`GET /v1/usage/breakdown`](/api/usage/get-usage-breakdown) shows usage and cost over a period, in cents at list price, split one of four ways.

- `by=keyword` (the default) gives each keyword's `keywordDays`, `keywordCents`, `billableMentions`, `mentionCents` and `totalCents`. A keyword deleted during the period keeps its row with `keyword.removed: true`, and its mention counts show 0.
- `by=group` gives a row for each [group](/guides/groups), so you can bill per customer or campaign. Keyword-days count under the group the keyword was in on that day. A group deleted during the period keeps its row with `group.removed: true`.
- `by=platform` gives mentions per platform. `keywordDays` is null here.
- `by=day` gives one row per UTC day, oldest first.

Choose the period with `range=7d|30d|90d` (the default is `30d`) or `month=2026-10` for a calendar month. A month in the future returns `400`.

`totals` sums up the whole period. It has keyword-days, matched, billed and unscored mentions, the total at list price, `unattributedBillable` (billed mentions of deleted keywords) and `ledgerDebitCents`, which is what was actually charged. Mentions are settled the day after, so a period that ends today shows less than list price by today's mentions. Rows are paged with `limit` (up to 500) and `offset`, and come with a `total`. Keys and tokens can read the breakdown 30 times a minute.

### This month, per keyword

Each keyword also carries this month's numbers in `stats.cost`, so `GET /v1/keywords` shows what every keyword has cost so far this month.

```ts tab="TypeScript"
const { data: breakdown } = await rumoro.getUsageBreakdown({ query: { by: 'keyword', range: '30d', limit: 100 }, throwOnError: true });
for (const row of breakdown.data) if (row.totalCents > 0) console.log(row.label, row.totalCents);
```

```python tab="Python"
breakdown = rumoro.usage.breakdown(by="keyword", range_="30d", limit=100)
for row in breakdown.data:
    if row.total_cents > 0:
        print(row.label, row.total_cents)
```

```bash tab="curl"
curl -s "https://api.rumoro.dev/v1/usage/breakdown?by=keyword&range=30d&limit=100" \
  -H "Authorization: Bearer $RUMORO_API_KEY" | jq '.data[] | select(.totalCents > 0) | {keyword: .label, cents: .totalCents}'
```

## The welcome credit

Every new account starts with $5.80, enough for one keyword for a month plus 100 mentions. Each person gets it once, without a card. It shows in the wallet as `signupCredit`.

Two guards protect the credit until your first top-up or a credit from our team. After that neither applies, although noisy keywords are still marked in `stats.noise.noisy`.

### 200 mentions per keyword

Each keyword can match up to 200 mentions a month, shown as `cap.welcome: true`. When it reaches 200 it reads `pausedForCap: true` and waits for the next month, without the daily keyword charge. If you set your own cap below 200, yours applies. A cap of 200 or more is kept in `cap.own` and takes over after your first top-up. While `cap.welcome` is true, sending `cap: { "mentions": 200 }` has no effect.

### The noise brake

A keyword with 20 or more scored matches in 14 days, of which less than 30% are relevant, is paused (`muted: true`, `pausedForNoise: true`, `status=noisy`). Owners get one email. If you change its terms, platforms or context, it starts again and is judged only on new matches, as long as the balance covers another day. You can also unmute it unchanged. A top-up doesn't restart it. [Keyword health](/guides/keyword-health) shows where the noise comes from.

## Adding funds

**Add funds** in the dashboard opens a Polar checkout. The default is $20, and you can add anything from $20 to $5,000. Polar is the seller of record and adds tax. Once the order is paid, the amount before tax is added to your balance and paused tracking starts again right away. Refunds are taken off the balance.

The billing page links to Polar's customer portal for cards, receipts and billing details.

## Auto recharge

An owner can set a threshold and an amount under **Auto recharge** on the billing page. When the effective balance falls below the threshold, or below one day of running keywords, Rumoro charges the saved card and adds the amount like a top-up. This also restarts a paused workspace.

- It charges at most once per workspace per UTC day.
- The amount can be $20 to $5,000, and the threshold anything from zero.
- If a charge fails, owners get an email and Rumoro tries again the next day.

The wallet shows the settings as `autoRecharge`. Only an owner can change them, on the billing page. API keys can't.

## When the balance runs out

Tracking pauses when the effective balance can't pay for another day of running keywords (`nextDayCents`). That way you are never charged for a day you don't get. Nothing is deleted. Paused keywords are counted in `autoMutedKeywords`.

Tracking starts again as soon as a top-up covers one day of all keywords, running and paused (`resumeCostCents`).

Owners get an email when the effective balance drops to 20% of the last credit (once per credit), and every time tracking pauses.

## Keyword limit

A workspace can track up to 500 keywords. If you need more, [write to us](mailto:markus@rumoro.dev).

Creating or unmuting a keyword fails with `402 insufficient_balance` when the effective balance can't pay for another keyword-day, and with `402 keyword_limit_reached` at 500. The check happens in the same database write, so parallel requests can't get around it. See [Errors](/errors).

## Capping a keyword's mentions

To limit how much a keyword can spend on mentions in a month, for example when you resell Rumoro, set `cap: { "mentions": 500 }` on [`POST /v1/keywords`](/api/keywords/create-keyword) or [`PATCH /v1/keywords/{id}`](/api/keywords/update-keyword). `null` removes the cap.

- The cap counts `stats.thisMonth`, which is every match in the current UTC month, look-back included.
- At the cap the keyword stops matching, reads `pausedForCap: true` and shows up under `status=capped`. Owners get one email, and webhooks can subscribe to [`keyword.capped`](/webhooks/account-events).
- It starts again on the first of the next month, or when a `PATCH` raises or removes the cap.
- The daily keyword charge continues. Set `muted: true` to stop that as well.

## The wallet through the API

| Call | What it does |
| --- | --- |
| [`GET /v1/billing/wallet`](/api/billing/get-wallet) | The balance, top-up limits and auto recharge settings |
| [`GET /v1/billing/ledger`](/api/billing/list-ledger) | Every change to the balance, newest first, with cursor paging |
| [`POST /v1/billing/top-ups`](/api/billing/create-top-up) | Returns a checkout link for `amountCents`. An optional `successUrl` must be on app.rumoro.dev. Charges nothing by itself, 5 a minute. |
| [`GET /v1/billing/invoices`](/api/billing/list-invoices), [`GET /v1/billing/invoices/{id}/url`](/api/billing/get-invoice-url) | Receipts, and a short-lived link to each PDF |

A `read` key can read all of this. Creating a checkout needs `write`. Cards and the customer portal are only in the dashboard.

### Checking your balance

```ts tab="TypeScript"
const { data: wallet } = await rumoro.getWallet();
```

```python tab="Python"
wallet = rumoro.billing.wallet()
```

```bash tab="curl"
curl https://api.rumoro.dev/v1/billing/wallet -H "Authorization: Bearer $RUMORO_API_KEY"
```

```json
{
  "balanceCents": 2615,
  "pendingCents": 72,
  "effectiveBalanceCents": 2543,
  "burnPerDayCents": 118,
  "daysLeft": 21,
  "stopped": false,
  "lowBalance": false,
  "activeKeywords": 4,
  "autoMutedKeywords": 0,
  "nextDayCents": 67,
  "resumeCostCents": 67,
  "signupCredit": { "amountCents": 580, "grantedAt": "2026-09-21T14:05:47.000Z" },
  "lastTopUpAt": "2026-10-01T08:33:19.000Z",
  "billingConfigured": true,
  "minTopUpCents": 2000,
  "maxTopUpCents": 500000,
  "defaultTopUpCents": 2000,
  "currency": "USD",
  "autoRecharge": { "available": true, "enabled": false, "thresholdCents": 500, "amountCents": 2000, "lastRunAt": null, "lastError": null }
}
```

| Field | Meaning |
| --- | --- |
| `balanceCents` | Credits minus settled charges |
| `pendingCents` | Matches since the last settlement, priced but not charged yet |
| `effectiveBalanceCents` | `balanceCents` minus `pendingCents`, which the pause rule uses |
| `burnPerDayCents` | Average daily charge over the last 7 days, or since the workspace was created |
| `daysLeft` | Effective balance divided by the daily charge, rounded down. `null` when nothing is charged. |
| `stopped` | `true` while tracking is paused for lack of balance |
| `lowBalance` | `true` at or below 20% of the last credit |
| `activeKeywords` | Keywords running now |
| `autoMutedKeywords` | Keywords paused for lack of balance |
| `nextDayCents` | What one more day of the running keywords costs |
| `resumeCostCents` | What one day of all keywords costs. Tracking restarts once the balance covers it. |
| `signupCredit` | The welcome credit (`amountCents`, `grantedAt`), or `null` |
| `lastTopUpAt` | The latest paid top-up, or `null` |
| `billingConfigured` | `false` where top-ups aren't available |
| `minTopUpCents`, `maxTopUpCents`, `defaultTopUpCents` | The allowed top-up range (2000 to 500000) and the default (2000) |
| `currency` | Always `USD` |
| `autoRecharge` | Auto recharge settings and the last run |

### The ledger

```ts tab="TypeScript"
const { data: ledger } = await rumoro.listLedger({ query: { limit: 50 } });
```

```python tab="Python"
ledger = rumoro.billing.ledger(limit=50)
```

```bash tab="curl"
curl "https://api.rumoro.dev/v1/billing/ledger?limit=50" -H "Authorization: Bearer $RUMORO_API_KEY"
```

```json
{
  "data": [
    { "id": "led_...", "kind": "debit_mentions", "amountCents": -72, "day": "2026-10-03", "units": 2410, "note": null, "polarOrderId": null, "createdAt": "2026-10-04T00:01:38.000Z" },
    { "id": "led_...", "kind": "topup", "amountCents": 2500, "day": null, "units": null, "note": null, "polarOrderId": "7f3e91c0-...", "createdAt": "2026-10-01T08:33:19.000Z" }
  ],
  "nextCursor": null
}
```

Credits are positive and charges negative. Use `cursor` for the next page. On a charge, `day` is the last UTC day it settles, and `units` is the running total of keyword-days or mentions settled so far.

| Kind | Meaning |
| --- | --- |
| `signup_credit` | The welcome credit |
| `topup` | A paid Polar order, with its `polarOrderId` |
| `refund` | A Polar refund, taken off the balance |
| `debit_keyword_days` | The daily keyword charge |
| `debit_mentions` | The daily mention charge |
| `adjustment` | A manual correction by the Rumoro team, with a `note` |

Each charge bills the total owed so far minus what was already charged, so rounding errors never build up. 30 keyword-days are exactly 500 cents and 100 mentions exactly 80.

Where top-ups aren't available, the top-up endpoints return `503 billing_not_configured` and `billingConfigured` is `false`. Everything else keeps working.
