# listTransactions

List Transactions - retrieves transactions for the authenticated user with optional filtering.

Last updated October 2, 2026

## Reference

Authentication: **bearer token required** (see [how to get a token](https://financesavi.com/developers/authentication#get-a-token))

```
List Transactions - retrieves transactions for the authenticated user with optional filtering.

This handler returns transactions from accounts where the user is either the owner or collaborator.
It supports comprehensive filtering by account, tags, vendors, categories, dates, amounts, and search queries.

Authentication: Required (Bearer token)

Parameters:
- pagination (object, optional): Controls result paging
  - limit (int): Max records to return, 0-100 (default 50; 0 also means the default)
  - skip (int): Number of records to skip (non-negative)
- sort (array, optional): Sort criteria (default: transactedAt DESC); only the first entry is applied
  - field (string): One of transactedAt, amount, name, vendorName, category, createdAt, updatedAt, _id
  - operator (string): Sort direction ("ASC" or "DESC")
- ids (array, optional): Filter by transaction IDs (at most 500, unique, non-empty)
- accountId (string, optional): Filter by single account ID (legacy, use accountIds)
- accountIds (array, optional): Filter by multiple account IDs (at most 100, unique, non-empty)
- tagIds (array, optional): Filter transactions that have all specified tags (at most 500)
- vendorIds (array, optional): Filter by vendor IDs (at most 500)
- vendorNames (array, optional): Filter by vendor names (vendorId must be null/empty; at most 500)
- categories (array, optional): Filter by transaction categories (FOOD, RENT, INCOME, etc.; at most 500)
- categoryIds (array, optional): Filter by transaction category IDs (new category system; at most 500). A JOURNAL entry matches when one of its category lines posts to a listed category; minAmountInclusive/maxAmountInclusive then test that line's amount (credit minus debit) instead of the entry's amount
- sources (array, optional): Filter by transaction sources (BANK_INTEGRATION, MANUAL, etc.; at most 500)
- startDate (string, optional): Filter transactions on or after this date (ISO 8601)
- endDate (string, optional): Filter transactions on or before this date (ISO 8601)
- searchQuery (string, optional): Case-insensitive literal substring match on transaction name, description, or vendor name
- transactionType (string, optional): Filter by type ("INCOME", "EXPENSE", "TRANSFER")
- minAmountInclusive (number, optional): Minimum transaction amount (inclusive)
- maxAmountInclusive (number, optional): Maximum transaction amount (inclusive)
- minimumAbsoluteAmountInclusive (number, optional): Minimum absolute amount (inclusive, matches positive or negative)
- maximumAbsoluteAmountInclusive (number, optional): Maximum absolute amount (inclusive, matches positive or negative)
- includeUntagged (boolean, optional): Include untagged transactions when filtering by tags

Returns:
- transactions (array): Array of transaction objects
- numItems (int): Total number of transactions matching the filter (ignoring limit)

Example:
{
  "method": "listTransactions",
  "params": {
    "pagination": {
      "limit": 20,
      "skip": 0
    },
    "sort": [
      {
        "field": "transactedAt",
        "operator": "DESC"
      }
    ],
    "accountIds": ["account_123"],
    "categories": ["FOOD", "ENTERTAINMENT"],
    "startDate": "2024-01-01T00:00:00Z",
    "endDate": "2024-12-31T23:59:59Z",
    "minAmountInclusive": -10000,
    "maxAmountInclusive": 0
  }
}
```

## listTransactions request

POST https://api-v2.financesavi.com/rpc · Bearer token required

### cURL

```bash
curl -sS --max-time 30 -X POST 'https://api-v2.financesavi.com/rpc' \
  -H 'Content-Type: application/json' \
  -H "Authorization: Bearer $SAVI_TOKEN" \
  -d '{"jsonrpc":"2.0","method":"listTransactions","params":{"pagination":{"limit":10,"skip":0}},"id":1}'
```

### JavaScript

```javascript
const response = await fetch("https://api-v2.financesavi.com/rpc", {
  method: 'POST',
  signal: AbortSignal.timeout(30_000),
  headers: {
    'Content-Type': 'application/json',
    Authorization: `Bearer ${process.env.SAVI_TOKEN}`,
  },
  body: JSON.stringify({
      "jsonrpc": "2.0",
      "method": "listTransactions",
      "params": {
        "pagination": {
          "limit": 10,
          "skip": 0
        }
      },
      "id": 1
    }),
});
console.log(await response.json());
```

### TypeScript

```typescript
const response = await fetch("https://api-v2.financesavi.com/rpc", {
  method: 'POST',
  signal: AbortSignal.timeout(30_000),
  headers: {
    'Content-Type': 'application/json',
    Authorization: `Bearer ${process.env.SAVI_TOKEN}`,
  },
  body: JSON.stringify({
      "jsonrpc": "2.0",
      "method": "listTransactions",
      "params": {
        "pagination": {
          "limit": 10,
          "skip": 0
        }
      },
      "id": 1
    }),
});
console.log(await response.json());
```

### Python

```python
import os
import requests

response = requests.post(
    "https://api-v2.financesavi.com/rpc",
    headers={"Authorization": f"Bearer {os.environ['SAVI_TOKEN']}"},
    json={
        "jsonrpc": "2.0",
        "method": "listTransactions",
        "params": {
            "pagination": {
                "limit": 10,
                "skip": 0,
            },
        },
        "id": 1,
    },
    timeout=30,
)
print(response.json())
```

### Go

```go
package main

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

func main() {
  payload := strings.NewReader("{\"jsonrpc\":\"2.0\",\"method\":\"listTransactions\",\"params\":{\"pagination\":{\"limit\":10,\"skip\":0}},\"id\":1}")
  req, err := http.NewRequest("POST", "https://api-v2.financesavi.com/rpc", payload)
  if err != nil { panic(err) }
  req.Header.Set("Content-Type", "application/json")
  req.Header.Set("Authorization", "Bearer " + os.Getenv("SAVI_TOKEN"))
  client := &http.Client{Timeout: 30 * time.Second}
  res, err := client.Do(req)
  if err != nil { panic(err) }
  defer res.Body.Close()
  body, err := io.ReadAll(res.Body)
  if err != nil { panic(err) }
  fmt.Println(res.Status, string(body))
}
```

### C#

```csharp
// .NET 6 or later
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;

using var client = new HttpClient { Timeout = TimeSpan.FromSeconds(30) };
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", Environment.GetEnvironmentVariable("SAVI_TOKEN"));
using var content = new StringContent(@"{""jsonrpc"":""2.0"",""method"":""listTransactions"",""params"":{""pagination"":{""limit"":10,""skip"":0}},""id"":1}", Encoding.UTF8, "application/json");
using var response = await client.PostAsync(@"https://api-v2.financesavi.com/rpc", content);
Console.WriteLine($"HTTP {(int)response.StatusCode}");
Console.WriteLine(await response.Content.ReadAsStringAsync());
```

### Java

```java
// Java 11+: save as SaviExample.java, then run java SaviExample.java
import java.net.URI;
import java.net.http.*;
import java.time.Duration;

class SaviExample {
  public static void main(String[] args) throws Exception {
    var client = HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(30)).build();
    var builder = HttpRequest.newBuilder(URI.create("https://api-v2.financesavi.com/rpc"))
        .timeout(Duration.ofSeconds(30))
        .header("Content-Type", "application/json");
    builder.header("Authorization", "Bearer " + System.getenv("SAVI_TOKEN"));
    var request = builder.POST(HttpRequest.BodyPublishers.ofString("{\"jsonrpc\":\"2.0\",\"method\":\"listTransactions\",\"params\":{\"pagination\":{\"limit\":10,\"skip\":0}},\"id\":1}")).build();
    var response = client.send(request, HttpResponse.BodyHandlers.ofString());
    System.out.println("HTTP " + response.statusCode());
    System.out.println(response.body());
  }
}
```

### PHP

```php
<?php
// Requires the PHP cURL extension.
$curl = curl_init('https://api-v2.financesavi.com/rpc');
curl_setopt_array($curl, [
    CURLOPT_POST => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CONNECTTIMEOUT => 30,
    CURLOPT_TIMEOUT => 30,
    CURLOPT_HTTPHEADER => [
        'Content-Type: application/json',
        'Authorization: Bearer ' . getenv('SAVI_TOKEN'),
    ],
    CURLOPT_POSTFIELDS => '{"jsonrpc":"2.0","method":"listTransactions","params":{"pagination":{"limit":10,"skip":0}},"id":1}',
]);
$body = curl_exec($curl);
if ($body === false) {
    throw new RuntimeException(curl_error($curl));
}
echo "HTTP " . curl_getinfo($curl, CURLINFO_HTTP_CODE) . PHP_EOL;
echo $body . PHP_EOL;
curl_close($curl);
```

### Ruby

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

uri = URI('https://api-v2.financesavi.com/rpc')
request = Net::HTTP::Post.new(uri)
request['Content-Type'] = 'application/json'
request['Authorization'] = 'Bearer ' + ENV.fetch('SAVI_TOKEN')
request.body = '{"jsonrpc":"2.0","method":"listTransactions","params":{"pagination":{"limit":10,"skip":0}},"id":1}'
response = Net::HTTP.start(uri.host, uri.port,
  use_ssl: uri.scheme == 'https', open_timeout: 30, read_timeout: 30, write_timeout: 30
) { |http| http.request(request) }
puts "HTTP #{response.code}"
puts response.body
```

### Swift

```swift
// Swift 5.5+: save as SaviExample.swift, then run swift SaviExample.swift
import Foundation
#if canImport(FoundationNetworking)
import FoundationNetworking
#endif

var request = URLRequest(url: URL(string: "https://api-v2.financesavi.com/rpc")!)
request.httpMethod = "POST"
request.timeoutInterval = 30
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
guard let token = ProcessInfo.processInfo.environment["SAVI_TOKEN"] else { fatalError("Set SAVI_TOKEN first") }
request.setValue("Bearer " + token, forHTTPHeaderField: "Authorization")
request.httpBody = Data("{\"jsonrpc\":\"2.0\",\"method\":\"listTransactions\",\"params\":{\"pagination\":{\"limit\":10,\"skip\":0}},\"id\":1}".utf8)
let configuration = URLSessionConfiguration.ephemeral
configuration.timeoutIntervalForResource = 30
let session = URLSession(configuration: configuration)
let (data, response) = try await session.data(for: request)
if let http = response as? HTTPURLResponse { print("HTTP", http.statusCode) }
print(String(decoding: data, as: UTF8.self))
```

### Local API capture (scrubbed) (HTTP 200)

```json
{
  "jsonrpc": "2.0",
  "result": {
    "transactions": [
      {
        "id": "tx_9bLm4Rt7Yc",
        "accountId": "account_5kQe2Xv9Lm",
        "name": "Coffee Shop",
        "amount": -450,
        "currency": "CAD",
        "source": "MANUAL",
        "category": "FOOD",
        "type": "EXPENSE",
        "categoryId": "cat_3nTq8Wd2Zs",
        "categoryDecision": {
          "source": "USER",
          "locked": true,
          "decidedAt": "2026-10-01T15:48:50.481Z",
          "lockedAt": "2026-10-01T15:48:50.481Z"
        },
        "transactedAt": "2026-10-01T09:30:00Z",
        "createdAt": "2026-10-01T15:48:50.481Z",
        "updatedAt": "2026-10-01T15:48:50.481Z",
        "fileIds": [],
        "isApproved": true,
        "tagIds": [],
        "lineItems": [],
        "activityLog": [],
        "oneOffSplitParticipants": [],
        "netAmount": -450,
        "refundStatus": "NONE"
      }
    ],
    "numItems": 1
  },
  "id": 1
}
```

Source: https://financesavi.com/developers/api/listTransactions
