> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.ntropy.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.ntropy.com/_mcp/server.

# Find recurring groups in a transaction history

POST https://api.ntropy.com/v3/account_holders/{id}/recurring_groups

Identifies and categorizes recurring patterns found in the transaction history of the account holder,
such as periodic payments or subscriptions. These patterns are called recurrence groups. 

Complete guide: [Recurrence](../../../enrichment/recurrence).

Reference: https://docs.ntropy.com/documentation/api/recurrence/get-account-holder-recurring-payments-v-3-account-holders-id-recurring-groups-post

## Authentication

- `X-Api-Key` header (required) — API Key authentication via header

## Request

### Path parameters

- `id` (string, required)

## Response

### 200

Successful Response

- `list of RecurrenceGroup`

## Errors

### 404 Not Found Error

Account holder with the provided id not found.

- `any`

### 422 Unprocessable Entity Error

Validation Error

- `detail` (list of ValidationError, optional)

## Types

### RecurrenceGroup

- `id` (string, required) — A unique UUID identifier for the group
- `start_date` (date, required) — The date of the oldest transaction in the group
- `end_date` (date, required) — The date of the most recent transaction in the group
- `total_amount` (double, required) — The sum of all transaction amounts in this group
- `average_amount` (double, required) — The average amount per transaction in this group
- `periodicity_in_days` (double, required) — The estimated number of days between transactions in this group
- `periodicity` (enum, required) — A human-readable description of the transaction frequency
  - Allowed values: `daily`, `weekly`, `bi-weekly`, `monthly`, `bi-monthly`, `quarterly`, `semi-yearly`, `yearly`, `other`
- `counterparty` (Counterparty, required) — Counterparty of the transactions
- `categories` (Categories, required) — Categories of the transactions in the recurrence group
- `transaction_ids` (list of string, required) — Transactions in this recurrence group
- `entry_type` (enum, required) — The direction of the flow of the money for the transactions within the group.
  - Allowed values: `incoming`, `outgoing`

### ValidationError

- `loc` (list of ValidationErrorLocItems, required)
- `msg` (string, required)
- `type` (string, required)

### Counterparty

- `type` (enum, required)
  - Allowed values: `person`, `organization`
- `id` (string, optional, nullable) — The unique UUID identifier of the entity
- `name` (string, optional, nullable) — The name of the entity
- `website` (string, optional, nullable) — The website URL of the entity
- `phone_number` (string, optional, nullable) — The phone number of the entity. This is a premium feature, please contact support to enable it.
- `tax_number` (string, optional, nullable) — The tax number of the entity. This is a premium feature, please contact support to enable it.
- `naics2017` (string, optional, nullable) — The 2017 NAICS code of the entity. This is a premium feature, please contact support to enable it.
- `logo` (string, optional, nullable) — Logo's URL
- `mccs` (list of integer, optional) — A list of [Merchant Category Codes](https://en.wikipedia.org/wiki/Merchant_category_code)
- `parent` (EntityParent, optional, nullable) — The parent entity

### Categories

- `general` (string, required, nullable) — The category of the transaction. View the valid set of categories for your key [here](../../../categories).
- `accounting` (enum, optional, nullable) — The corresponding accounting category. Only available for `business` transactions.
  - Allowed values: `operational expenses`, `cost of goods sold`, `revenue`, `financing`, `taxes`, `investing`, `not enough information`

### ValidationErrorLocItems

### EntityParent

- `id` (string, optional, nullable) — The unique UUID identifier of the entity
- `name` (string, optional, nullable) — The name of the entity
- `website` (string, optional, nullable) — The website URL of the entity
- `phone_number` (string, optional, nullable) — The phone number of the entity. This is a premium feature, please contact support to enable it.
- `tax_number` (string, optional, nullable) — The tax number of the entity. This is a premium feature, please contact support to enable it.
- `naics2017` (string, optional, nullable) — The 2017 NAICS code of the entity. This is a premium feature, please contact support to enable it.

## Examples

### Example_0

**Request**

```json
{}
```

**Response**

```json
[
  {
    "id": "8efbac45-9bd5-4b67-be29-334106198c40",
    "start_date": "2023-01-01",
    "end_date": "2023-01-31",
    "total_amount": 120.5,
    "average_amount": 30.13,
    "periodicity_in_days": 7,
    "periodicity": "weekly",
    "counterparty": {
      "type": "organization",
      "id": "d4bc3c80-ec1a-3da2-836e-2a4ca4758be5",
      "name": "Starbucks",
      "website": "https://starbucks.com",
      "phone_number": "+1 4153753176",
      "tax_number": "80-0429876",
      "naics2017": "541519",
      "logo": "https://logos.ntropy.com/starbucks.com",
      "mccs": [
        5814
      ]
    },
    "categories": {
      "general": "coffee shop"
    },
    "transaction_ids": [
      "2dc6SE8A7cTQ2jUdUadCg",
      "tQYAhhl0XNkl1wasacpVQ",
      "NNJTqvockIdKnYxBqPlJw",
      "X9JkLmN8PqRsTuVwXyZ0"
    ],
    "entry_type": "outgoing"
  }
]
```

**SDK Code**

```python Example_0
import requests

url = "https://api.ntropy.com/v3/account_holders/id/recurring_groups"

payload = {}
headers = {
    "X-Api-Key": "<apiKey>",
    "Content-Type": "application/json"
}

response = requests.post(url, json=payload, headers=headers)

print(response.json())
```

```javascript Example_0
const url = 'https://api.ntropy.com/v3/account_holders/id/recurring_groups';
const options = {
  method: 'POST',
  headers: {'X-Api-Key': '<apiKey>', 'Content-Type': 'application/json'},
  body: '{}'
};

try {
  const response = await fetch(url, options);
  const data = await response.json();
  console.log(data);
} catch (error) {
  console.error(error);
}
```

```go Example_0
package main

import (
	"fmt"
	"strings"
	"net/http"
	"io"
)

func main() {

	url := "https://api.ntropy.com/v3/account_holders/id/recurring_groups"

	payload := strings.NewReader("{}")

	req, _ := http.NewRequest("POST", url, payload)

	req.Header.Add("X-Api-Key", "<apiKey>")
	req.Header.Add("Content-Type", "application/json")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

```ruby Example_0
require 'uri'
require 'net/http'

url = URI("https://api.ntropy.com/v3/account_holders/id/recurring_groups")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Post.new(url)
request["X-Api-Key"] = '<apiKey>'
request["Content-Type"] = 'application/json'
request.body = "{}"

response = http.request(request)
puts response.read_body
```

```java Example_0
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.post("https://api.ntropy.com/v3/account_holders/id/recurring_groups")
  .header("X-Api-Key", "<apiKey>")
  .header("Content-Type", "application/json")
  .body("{}")
  .asString();
```

```php Example_0
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('POST', 'https://api.ntropy.com/v3/account_holders/id/recurring_groups', [
  'body' => '{}',
  'headers' => [
    'Content-Type' => 'application/json',
    'X-Api-Key' => '<apiKey>',
  ],
]);

echo $response->getBody();
```

```csharp Example_0
using RestSharp;

var client = new RestClient("https://api.ntropy.com/v3/account_holders/id/recurring_groups");
var request = new RestRequest(Method.POST);
request.AddHeader("X-Api-Key", "<apiKey>");
request.AddHeader("Content-Type", "application/json");
request.AddParameter("application/json", "{}", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
```

```swift Example_0
import Foundation

let headers = [
  "X-Api-Key": "<apiKey>",
  "Content-Type": "application/json"
]
let parameters = [] as [String : Any]

let postData = JSONSerialization.data(withJSONObject: parameters, options: [])

let request = NSMutableURLRequest(url: NSURL(string: "https://api.ntropy.com/v3/account_holders/id/recurring_groups")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers
request.httpBody = postData as Data

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```

### cURL

**SDK Code**

```python Python SDK
from ntropy_sdk import SDK

sdk = SDK("cd1H...Wmhl")
recurring_groups = sdk.account_holders.recurring_groups(
    "35b927b6-6fda-40aa-93b8-95b47c2b2cad"
)
```

```javascript cURL
const url = 'https://api.ntropy.com/v3/account_holders/:id/recurring_groups';
const options = {method: 'POST', headers: {'X-Api-Key': '<apiKey>'}};

try {
  const response = await fetch(url, options);
  const data = await response.json();
  console.log(data);
} catch (error) {
  console.error(error);
}
```

```go cURL
package main

import (
	"fmt"
	"net/http"
	"io"
)

func main() {

	url := "https://api.ntropy.com/v3/account_holders/:id/recurring_groups"

	req, _ := http.NewRequest("POST", url, nil)

	req.Header.Add("X-Api-Key", "<apiKey>")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

```ruby cURL
require 'uri'
require 'net/http'

url = URI("https://api.ntropy.com/v3/account_holders/:id/recurring_groups")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Post.new(url)
request["X-Api-Key"] = '<apiKey>'

response = http.request(request)
puts response.read_body
```

```java cURL
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.post("https://api.ntropy.com/v3/account_holders/:id/recurring_groups")
  .header("X-Api-Key", "<apiKey>")
  .asString();
```

```php cURL
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('POST', 'https://api.ntropy.com/v3/account_holders/:id/recurring_groups', [
  'headers' => [
    'X-Api-Key' => '<apiKey>',
  ],
]);

echo $response->getBody();
```

```csharp cURL
using RestSharp;

var client = new RestClient("https://api.ntropy.com/v3/account_holders/:id/recurring_groups");
var request = new RestRequest(Method.POST);
request.AddHeader("X-Api-Key", "<apiKey>");
IRestResponse response = client.Execute(request);
```

```swift cURL
import Foundation

let headers = ["X-Api-Key": "<apiKey>"]

let request = NSMutableURLRequest(url: NSURL(string: "https://api.ntropy.com/v3/account_holders/:id/recurring_groups")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```