> ## Documentation Index
> Fetch the complete documentation index at: https://onlytraffic.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Введение

> OnlyTraffic Studio API: управляйте аккаунтами, агентствами, заказами CPL/CPC/RevShare/Swap, подписчиками и транзакциями программно.

OnlyTraffic Studio API позволяет читать данные вашей Studio и работать с ними: подписчики, транзакции, заказы, кампании и многое другое. Используйте его для интеграций, дашбордов и автоматизации.

## Base URL

```text theme={null}
https://studio-api.onlytraffic.com/api/external/v1
```

<CardGroup cols={3}>
  <Card title="Аутентификация" icon="key" href="/docs/ru/api/authentication">
    Получите API-ключ и начните отправлять запросы.
  </Card>

  <Card title="Пагинация" icon="list-ol" href="#пагинация">
    Постраничные и курсорные списки.
  </Card>

  <Card title="Лимиты запросов" icon="gauge-high" href="#лимиты-запросов">
    Часовые и burst-лимиты на аккаунт, по уровням.
  </Card>
</CardGroup>

## Аутентификация

Все запросы требуют заголовок `X-API-Key`. Создайте API-ключ в [Studio Dashboard](https://studio.onlytraffic.com/api).

```bash theme={null}
curl https://studio-api.onlytraffic.com/api/external/v1/subscribers \
  -H "X-API-Key: your-api-key-here"
```

<Info>Коды ошибок и подробная настройка описаны на странице [Аутентификация](/docs/ru/api/authentication).</Info>

## Лимиты запросов

Лимиты считаются на аккаунт (все API-ключи аккаунта делят одни счётчики), в скользящем окне, и **зависят от типа запроса**:

| Уровень | В час | В минуту (burst) | К чему применяется |
| - | - | - | - |
| `read` | 2,000 | 240 | Запросы `GET` |
| `write` | 100 | 20 | `POST` / `PUT` / `PATCH` / `DELETE` |
| `upstream` | 10 | 5 | Небольшой набор эндпоинтов с более строгими лимитами |

Оба окна действуют одновременно. Часовая квота — ограничение на длинном окне; минутный лимит — защита от всплесков поверх неё.

### Заголовки ответа (в каждом ответе, успешном или с ошибкой)

| Заголовок | Описание |
| - | - |
| `X-RateLimit-Limit` | Часовой лимит активного уровня |
| `X-RateLimit-Remaining` | Сколько запросов осталось в текущем часе для этого уровня |
| `X-RateLimit-Reset` | Unix timestamp, когда часовое окно сбросится |
| `X-RateLimit-Tier` | Активный уровень (`read`, `write` или `upstream`) |

Пример:

```http theme={null}
HTTP/1.1 200 OK
X-RateLimit-Limit: 2000
X-RateLimit-Remaining: 1947
X-RateLimit-Reset: 1746453296
X-RateLimit-Tier: read
```

### Когда лимит превышен

`429 Too Many Requests` со стандартным форматом ошибки и заголовком `Retry-After` (сколько секунд ждать):

```json theme={null}
{
  "success": false,
  "error": "rate_limit_exceeded",
  "message": "Too many requests. Try again in 37 seconds.",
  "retry_after": 37
}
```

Рекомендация: читайте `X-RateLimit-Remaining` после каждого вызова и замедляйтесь, когда значение становится низким, а не ждите 429.

## Пагинация

API использует **два стиля пагинации** в зависимости от размера набора данных и сценария доступа.

### Постраничная (по умолчанию для заказов / кампаний)

Эндпоинты с ограниченными наборами результатов (`cpl/orders`, `cpc/orders`, `revshare/campaigns` и т. д.) используют постраничную пагинацию. Размер страницы по умолчанию 50, максимум 100. Для навигации передавайте `?page=N`.

```json theme={null}
{
  "pagination": {
    "page": 1,
    "total": 150,
    "total_pages": 2
  }
}
```

### Курсорная (ленты большого объёма)

Эндпоинты над таблицами большого объёма (`subscribers`, `transactions`) используют курсорную пагинацию. Первый вызов возвращает первую страницу и токен `next_cursor`; передайте этот токен обратно как `?after=`, чтобы продолжить.

```http theme={null}
GET /api/external/v1/transactions?limit=50
```

```json theme={null}
{
  "data": [/* 50 rows, newest first */],
  "pagination": {
    "next_cursor": "eyJ0cyI6MTc0NjQ1MzI5NiwiaWQiOjk4NzY1fQ==",
    "has_next": true,
    "limit": 50
  }
}
```

Продолжение:

```http theme={null}
GET /api/external/v1/transactions?limit=50&after=eyJ0cyI6MTc0NjQ1MzI5NiwiaWQiOjk4NzY1fQ==
```

Когда `has_next` равен `false` (или `next_cursor` равен `null`), вы дошли до конца.

**Почему на этих эндпоинтах курсор**

* Стабильный порядок, даже когда между вызовами добавляются новые строки (постраничная пагинация сдвигалась бы).
* Нет счётчика `total`: курсорные ленты оптимизированы под частичное чтение, а не под подсчёт строк.
* O(1) на страницу независимо от того, как глубоко вы пролистали.

Считайте курсор непрозрачной строкой, не разбирайте его.

**Направление сортировки**

Передайте `?sort=<field>_asc`, чтобы идти от старых к новым. По умолчанию `<field>_desc` (сначала новые). Курсор привязан к направлению, для которого он выдан; чтобы сменить направление посреди пагинации, нужно начать заново.

## Повторы и идемпотентность

### Одновременные изменения

Некоторые эндпоинты записи выполняются последовательно в рамках аккаунта: второй запрос к тому же ресурсу, пока первый ещё выполняется, возвращает `429 action_in_progress`. Дождитесь завершения исходного запроса, затем вызовите соответствующий `GET`, чтобы увидеть результат. Не повторяйте вслепую, пока запрос ещё выполняется.

### Можно безопасно повторять

| Код | Почему можно повторить |
| - | - |
| `502` | Upstream временно недоступен. Повторяйте с back-off. |
| `429 rate_limit_exceeded` | В теле есть `retry_after` (секунды), в заголовке `Retry-After`. Подождите указанное время перед повтором. |
| Сетевой таймаут | Можно повторять для идемпотентных методов (`GET`, `DELETE`). Для неидемпотентных сначала проверьте состояние через `GET`. |

### НЕЛЬЗЯ повторять вслепую

`400`, `404`, `409`, `422`, `426` детерминированы. Тот же запрос даст ту же ошибку. Разберитесь (прочитайте `error` и `details`), исправьте входные данные и только потом отправляйте снова.

### Рекомендуемый back-off

Экспоненциальный, с ограничением: 1s, 2s, 4s, 8s, 16s, 30s. Если в ответе есть `retry_after` (тело) или `Retry-After` (заголовок), соблюдайте время ожидания, предложенное сервером, оно важнее локального back-off.

### Пример: обработка 429

```javascript JavaScript theme={null}
async function fetchWithRetry(url, options) {
  const res = await fetch(url, options);
  if (res.status !== 429) return res;
  const retryAfter = Number(res.headers.get('Retry-After')) || 5;
  await new Promise(r => setTimeout(r, retryAfter * 1000));
  return fetch(url, options); // retry once
}
```

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

Все ответы имеют единую структуру:

<CodeGroup>
  ```json Success theme={null}
  {
    "success": true,
    "data": [ ... ],
    "pagination": { ... }
  }
  ```

  ```json Error theme={null}
  {
    "success": false,
    "error": "error_code",
    "message": "Human-readable message"
  }
  ```
</CodeGroup>

## Ответы на запись и эволюция схемы

### Запись возвращает только id

Успешные ответы `POST` / `PUT` / `PATCH` / `DELETE` содержат только id ресурса (и `success: true`), а не полную запись:

```json theme={null}
{
  "success": true,
  "data": { "order_id": "cplo_xxxxxxx" }
}
```

Чтобы прочитать состояние после записи, вызовите соответствующий эндпоинт `GET`. Эндпоинты списков и деталей — единственный источник истины о формате ответа.

Два исключения:

* **Эндпоинты загрузки изображений** дополнительно возвращают `thumbnail_url` (подписанный URL на 3 дня), чтобы только что сохранённое изображение можно было показать без дополнительного вызова. Оригиналы остаются за обычным `GET`.
* **Одноразовые секреты** (открытый API-ключ, возвращаемый при создании ключа, токены верификации) появляются в `data` только в этом единственном ответе и документированы как «показывается один раз и больше никогда».

### Политика эволюции схемы

Мы можем добавлять поля в существующие ответы **без уведомления**. Потребители ОБЯЗАНЫ игнорировать неизвестные поля, а не падать на лишних.

`v1` ещё молод и может меняться без предупреждения, пока мы его исправляем и дорабатываем. Следите за этой документацией: если у вас что-то сломалось, сначала проверьте здесь.

На практике:

* Настройте парсер так, чтобы он допускал лишние поля, а не отклонял их.
* Входные данные проверяйте строго, к выходным относитесь гибко.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.