Finance OS / API

Pagination

Все endpoints, возвращающие коллекции (реестр клиентов, история операций, список проверок), используют единый формат пагинации по принципу Laravel paginate().

Query-параметры

page integer optional
Номер страницы, начиная с 1.
Default: 1
per_page integer optional
Сколько элементов на странице. Максимум — 100. Передача большего значения вернёт 422.
Default: 20
sort string optional
Поле сортировки. Префикс - для DESC: sort=-created_at. Поддерживаемые поля указаны в каждом endpoint отдельно.
filter[*] mixed optional
Фильтрация. Синтаксис: filter[status]=pending, filter[date_from]=2026-05-01. Поддерживаемые ключи — в каждом endpoint.

Формат ответа

{
  "data": [
    { "id": "...", ... },
    { "id": "...", ... }
  ],
  "links": {
    "first": "https://fin-os.io/api/v1/customers?page=1",
    "last":  "https://fin-os.io/api/v1/customers?page=42",
    "prev":  null,
    "next":  "https://fin-os.io/api/v1/customers?page=2"
  },
  "meta": {
    "current_page": 1,
    "from": 1,
    "to": 20,
    "per_page": 20,
    "total": 837,
    "last_page": 42,
    "path": "https://fin-os.io/api/v1/customers"
  }
}

meta

current_page integer optional
Текущая страница.
last_page integer optional
Общее число страниц.
per_page integer optional
Размер страницы (эхо параметра запроса).
total integer optional
Общее число элементов в коллекции (с учётом фильтров).
from integer optional
Номер первого элемента на странице (для UI «1–20 из 837»).
to integer optional
Номер последнего элемента на странице.

Курсорная пагинация лент

Для длинных лент (история операций клиента, движения мастер-счёта) page-based быстро деградирует - большой OFFSET сканирует много строк. Такие эндпоинты вместо page принимают курсор:

GET /api/v1/customers/{uuid}/transactions?starting_after=ctx_9c12d&per_page=100

starting_after - идентификатор последней полученной записи, он же meta.next_cursor предыдущего ответа. В ответе приходят meta.has_more и meta.next_cursor (null, когда лента закончилась). Неизвестный или устаревший курсор ошибкой не считается - вернётся первая страница.

Полный обход коллекции

Не качайте всё одной страницей
per_page=10000 вернёт 422. Если нужно выгрузить всю историю - итерируйте по страницам (для лент - по курсору), а не увеличивайте размер страницы.

Примеры кода

# Первая страница
curl -X GET "https://fin-os.io/api/v1/customers?page=1&per_page=50" \
  -H "Authorization: Bearer ${TOKEN}"

# С фильтром по статусу и сортировкой
curl -X GET "https://fin-os.io/api/v1/customers?\
filter[status]=active&\
filter[date_from]=2026-05-01&\
sort=-created_at&\
per_page=20" \
  -H "Authorization: Bearer ${TOKEN}"
// Полный обход всех страниц
async function* iterateAll(url, headers) {
  let page = 1;
  while (true) {
    const res = await fetch(`${url}?page=${page}&per_page=100`, { headers });
    const body = await res.json();
    for (const item of body.data) yield item;
    if (!body.links.next) break;
    page++;
  }
}

for await (const customer of iterateAll('https://fin-os.io/api/v1/customers',
                                       { 'Authorization': `Bearer ${TOKEN}` })) {
  console.log(customer.uuid, customer.phone);
}
// Обход всех страниц через генератор
function iterateAll(string $url, string $token): \Generator
{
    $page = 1;
    while (true) {
        $body = Http::withToken($token)
            ->get($url, ['page' => $page, 'per_page' => 100])
            ->json();

        foreach ($body['data'] as $item) {
            yield $item;
        }
        if (! $body['links']['next']) break;
        $page++;
    }
}

foreach (iterateAll('https://fin-os.io/api/v1/customers', $token) as $customer) {
    // process $customer
}
import requests

def iterate_all(url, headers):
    page = 1
    while True:
        r = requests.get(url, params={'page': page, 'per_page': 100}, headers=headers)
        body = r.json()
        for item in body['data']:
            yield item
        if not body['links']['next']:
            break
        page += 1

for customer in iterate_all(
    'https://fin-os.io/api/v1/customers',
    headers={'Authorization': f'Bearer {TOKEN}'},
):
    print(customer['uuid'], customer['phone'])