Guide

How to detect recurring payments

A recurring payment is a charge that repeats at a regular interval: a subscription, a bill, rent or a salary. To find one, you need the merchant, the amount and the date of each charge.

This guide gives the rules that Kederi uses. The example request and its answer are tested: a test sends the request to the API and compares the answer on this page.

First, find the merchant

The same merchant appears under different descriptors from one month to the next. A reference changes, or a processor prefix comes and goes. If you group the raw descriptors, one subscription becomes several series, and none of them repeats.

So Kederi groups charges by the merchant that it resolves, not by the raw text. A grouping error is a detection error, so the recurring benchmark scores the two together.

The rules

Three charges or more. Two charges are not enough to show a pattern.

The same amount, within 5%. Charges that differ by more are a different series, unless one of the two rules below applies.

A regular interval. The median interval between the charges sets the cadence. Each interval must be near the median: within 4 days, or within a quarter of the median if that is more, to a limit of 45 days. A bill paid a few days late is still monthly.

Cadence, from the median interval

Weekly6 to 8 days
Every two weeks13 to 16 days
Monthly27 to 33 days
Quarterly84 to 95 days
Yearly350 to 380 days

A price change. If a subscription changes price once, the series has two stable amounts. Kederi keeps it as one series and returns the change as price_change, with the old amount, the new amount and the date.

A bill that changes each month. Electricity bills and data bundles change with use. Kederi needs four charges or more for this, and the largest charge must be 3 times the smallest or less. It returns variable and the range. If most of the charges (80% or more) have one fixed amount, Kederi keeps that fixed series, and the other charges are not part of it.

The next date. For a monthly, quarterly or yearly series, the next date keeps the day of the month. If that month is shorter, the next date is its last day: after 31 January, the next monthly date is 28 February, or 29 February in a leap year. For a weekly series, it adds the median interval.

An example

Send the charges in one batch to POST /v1/enrich, each with an amount and a date. Each result that repeats gets a recurrence.

The request

{
  "transactions": [
    { "id": "t1", "descriptor": "NETFLIX.COM 866-579-7172", "amount": 4400, "date": "2026-07-03" },
    { "id": "t2", "descriptor": "NETFLIX.COM 866-579-7172", "amount": 4400, "date": "2026-08-03" },
    { "id": "t3", "descriptor": "NETFLIX.COM 866-579-7172", "amount": 4400, "date": "2026-09-03" }
  ]
}

Part of each result

{
  "enrichment": { "name": "Netflix", "category": "Streaming", "recurring": "monthly" },
  "recurrence": {
    "cadence": "monthly",
    "occurrences": 3,
    "amount": 4400,
    "next_expected": "2026-10-03"
  }
}

Not every recurring payment is a subscription

Rent to a landlord, a monthly transfer to a relative and a salary all repeat. A subscription list that shows them is wrong. The subscriptions report of POST /reports gives each series a kind: subscription, bill, transfer, income or other.

It also gives a state: active, due_soon, overdue or lapsed. If a reversal matches a charge, the report removes the charge and its reversal before it measures the interval. A match is on the same account, with the same amount, in the opposite direction, within 7 days. If a reversal matches no charge, the charge stays in the series. A reversal left in place puts a 1 day interval in a monthly series.

Try it

Find the recurring payments in a statement

Import a statement in the console, and see each series that the API finds. A CSV, OFX or QIF file stays in your browser.