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

# Краткое руководство по Parsed Streams

> Подключитесь к Parsed Streams, отправьте свой первый фильтр и прочитайте декодированное уведомление. Также здесь приведена полная справочная информация по протоколу JSON-RPC 2.0.

<Tip>
  Впервые используете Parsed Streams? Сначала прочитайте [описание ментальной модели](/docs/ru/parsed-streams#ментальная-модель) — оно объясняет, почему фильтры устроены именно так.
</Tip>

## Краткое руководство

<Steps>
  <Step title="Get Access">
    Parsed Streams доступен на всех тарифных планах по цене 1 кредит за каждое доставленное событие. Получите ключ API в [панели управления Helius](https://dashboard.helius.dev) и подключитесь к конечной точке [Gatekeeper](/docs/ru/gatekeeper/overview) по адресу `wss://beta.helius-rpc.com` — на том же хосте, который используется для трафика Helius RPC и WebSocket.

    Пройдите аутентификацию с помощью ключа API вашего проекта, передав его как параметр запроса `api-key` (или заголовок `x-api-key`).
  </Step>

  <Step title="Connect">
    ```bash wscat theme={"system"}
    wscat -c "wss://beta.helius-rpc.com/?api-key=YOUR_API_KEY"
    ```

    Если ключ отсутствует или недействителен, запрос отклоняется с HTTP 401. Если проект достиг лимита подключений, возвращается HTTP 429.
  </Step>

  <Step title="Subscribe with a Filter">
    Отправьте `parsedTransactionSubscribe` с фильтром и необязательными параметрами:

    ```json theme={"system"}
    {"jsonrpc":"2.0","id":1,"method":"parsedTransactionSubscribe","params":[{"programs":["JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4"]}]}
    ```

    Поле `result` в ответе — это целочисленный **идентификатор подписки**:

    ```json theme={"system"}
    { "jsonrpc": "2.0", "id": 1, "result": 23 }
    ```
  </Step>

  <Step title="Read a Notification">
    Каждая соответствующая транзакция поступает как `parsedTransactionNotification` уже в декодированном виде, а `matchedIndexes` указывает на инструкции, соответствующие вашему фильтру. Полную структуру см. в разделе [«Уведомления»](#уведомления).
  </Step>

  <Step title="Unsubscribe">
    ```json theme={"system"}
    { "jsonrpc": "2.0", "id": 2, "method": "parsedTransactionUnsubscribe", "params": [23] }
    ```

    Или просто закройте подключение — при этом все его подписки будут удалены.
  </Step>
</Steps>

## Руководства

<CardGroup cols={2}>
  <Card title="Track Jupiter Swaps" icon="arrow-right-arrow-left" href="/docs/ru/parsed-streams/guides/track-jupiter-swaps">
    Используйте `describeProgram`, чтобы создать надежный фильтр перед оформлением подписки.
  </Card>

  <Card title="Track Pump.fun Mints" icon="rocket" href="/docs/ru/parsed-streams/guides/track-pumpfun-mints">
    Устойчивый к переподключениям обработчик, который регистрирует развертывание каждого нового токена Pump.fun.
  </Card>

  <Card title="Handling Reconnects" icon="rotate" href="/docs/ru/parsed-streams/guides/handling-reconnects">
    Обрабатывайте тайм-ауты бездействия и развертывания, а затем загружайте только пропущенные данные.
  </Card>
</CardGroup>

## Справочник по протоколу

Parsed Streams использует **JSON-RPC 2.0** через одно подключение WebSocket. На каждый запрос поступает ответ с тем же `id`. Затем подписка отправляет сообщения `parsedTransactionNotification`, пока вы не отмените подписку или не отключитесь.

| Метод | Назначение |
| - | - |
| `parsedTransactionSubscribe` | Создать подписку с фильтром |
| `parsedTransactionUnsubscribe` | Остановить подписку |
| `describeProgram` | Получить список инструкций, событий и ролей аккаунтов программы |

### Подписка

Отправьте `parsedTransactionSubscribe` с фильтром и необязательными параметрами. Поле `result` в ответе — это целочисленный **идентификатор подписки**.

```json Request theme={"system"}
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "parsedTransactionSubscribe",
  "params": [
    {
      "programs": ["JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4"],
      "instructionNames": ["route", "shared_accounts_route"],
      "accounts": {
        "include": ["So11111111111111111111111111111111111111112"],
        "roles": { "user_transfer_authority": "9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin" }
      },
      "includeFailed": false,
      "includeCpi": true
    },
    { "commitment": "confirmed", "details": "full" }
  ]
}
```

```json Response theme={"system"}
{ "jsonrpc": "2.0", "id": 1, "result": 23 }
```

#### Поля фильтра

Необходимо указать хотя бы одно из полей `programs` или `accounts.include`. Заданные поля объединяются оператором **AND**: для совпадения инструкция должна соответствовать каждому из них.

<ParamField body="programs" type="string[]">
  Идентификаторы программ для сопоставления (адреса base58, а не имена). Инструкция соответствует фильтру, если ее программа входит в этот список. Внутри списка применяется OR.
</ParamField>

<ParamField body="instructionNames" type="string[]">
  Имена декодированных инструкций, например `route`. Сначала выполняется точное сопоставление, а затем — резервное сопоставление без учета регистра и разделителей, поэтому `sharedAccountsRoute` также соответствует имени `shared_accounts_route` в передаваемых данных. Внутри списка применяется OR. Соответствовать фильтру могут только инструкции, имя которых удалось определить по каталогу, поэтому берите имена из `describeProgram`.
</ParamField>

<ParamField body="accounts.include" type="string[]">
  Адреса аккаунтов. Инструкция соответствует фильтру, если любой из этих адресов присутствует в ее списке аккаунтов. Внутри списка применяется OR. Работает для каждой инструкции независимо от того, декодирована она или нет. Сам идентификатор программы здесь не считается аккаунтом.
</ParamField>

<ParamField body="accounts.roles" type="object">
  Сопоставление имени роли декодированного аккаунта с адресом, например `{ "user_transfer_authority": "<pubkey>" }`. Каждая запись должна выполняться (между записями применяется AND), а для применения этого условия инструкция должна быть декодирована. Имена ролей сопоставляются **точно**, без изменения регистра, поэтому копируйте их из `describeProgram`, а не подбирайте самостоятельно.
</ParamField>

<ParamField body="includeFailed" type="boolean" default="false">
  Включать инструкции из неуспешных транзакций.
</ParamField>

<ParamField body="includeCpi" type="boolean" default="true">
  Внутренние инструкции (CPI) могут соответствовать фильтру. Укажите `false`, чтобы сопоставлять только инструкции верхнего уровня.
</ParamField>

Неизвестные поля в любой части фильтра или параметров не игнорируются, а отклоняются с ошибкой `-32602`. Поэтому опечатки приводят к явной ошибке, а не к отсутствию совпадений.

#### Параметры

Второй параметр необязателен.

<ParamField body="commitment" type="string" default="confirmed">
  Поддерживается только `confirmed`.
</ParamField>

<ParamField body="details" type="string" default="full">
  Содержимое каждого уведомления. `full`: вся транзакция, каждая инструкция и `matchedIndexes`, указывающий на совпадения с фильтром. `matched`: только совпавшие инструкции без списка индексов. `raw`: только совпавшие инструкции, каждая из которых сокращена до своей позиции, `programId` и блока `data` в формате base58, без декодированных полей и массива `accountKeys`. Используйте `matched`, если пропускная способность важнее контекста (полный объем данных в среднем примерно втрое больше), и `raw`, если вы самостоятельно декодируете данные инструкций и вам нужны только байты.
</ParamField>

Количество одновременных подключений на проект зависит от тарифного плана: **5** для Free, **10** для Developer и **50** для Business и Professional. Лимит общий для всех ключей API проекта. См. [«Ограничения частоты запросов»](/docs/ru/billing/rate-limits#разобранные-потоки).

### Уведомления

Для каждой соответствующей транзакции отправляется одно уведомление на подписку. При значении `details: "full"` по умолчанию:

```json theme={"system"}
{
  "jsonrpc": "2.0",
  "method": "parsedTransactionNotification",
  "params": {
    "subscription": 23,
    "result": {
      "context": { "slot": 430172053 },
      "value": {
        "transaction": {
          "signature": "3riSYL4HTRxgQjLayt6L2JPaDR3oaEQg1H4v3fnjUxNU...",
          "slot": 430172053,
          "blockTime": null,
          "feePayer": "6jduWNCTQzG91JGBchfGGxd55Vi5FxJCCJEV18RkXzJX",
          "fee": 5000,
          "accountKeys": ["6jduWNCT...", "..."],
          "status": "ok",
          "error": null,
          "summary": {
            "type": "swap",
            "description": "6jduWNCTQzG91JGBchfGGxd55Vi5FxJCCJEV18RkXzJX swapped 0.001 SOL for 0.183985 EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v via Jupiter",
            "parsedData": {
              "type": "swap",
              "protocol": "jupiter",
              "kind": "swap",
              "in_amount": "1000000",
              "actual_out_amount": "183985",
              "input_mint": "So11111111111111111111111111111111111111112",
              "output_mint": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"
            }
          },
          "nativeTransfers": [
            { "fromUserAccount": "6jduWNCT...", "toUserAccount": "DfXygSm4...", "amount": 1000000 }
          ],
          "tokenTransfers": [
            {
              "fromUserAccount": "6jduWNCT...",
              "toUserAccount": "AeUfFU6L...",
              "fromTokenAccount": "HLaEoW1s...",
              "toTokenAccount": "G13P9kSY...",
              "rawTokenAmount": 183985,
              "decimals": 6,
              "tokenStandard": "Fungible",
              "mint": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"
            }
          ]
        },
        "instructions": [
          {
            "instructionIndex": 4,
            "innerInstructionIndex": null,
            "stackHeight": 1,
            "programId": "JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4",
            "programName": "jupiter",
            "instructionName": "route",
            "summary": {
              "type": "swap",
              "description": "6jduWNCTQzG91JGBchfGGxd55Vi5FxJCCJEV18RkXzJX swapped 0.001 SOL for 0.183985 EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v via Jupiter",
              "parsedData": {
                "type": "swap",
                "protocol": "jupiter",
                "kind": "swap",
                "in_amount": "1000000",
                "actual_out_amount": "183985",
                "input_mint": "So11111111111111111111111111111111111111112",
                "output_mint": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"
              }
            },
            "decoded": {
              "args": { "in_amount": "1000000", "slippage_bps": 50 },
              "accounts": [
                { "name": "user_transfer_authority", "pubkey": "9xQe...", "isSigner": true, "isWritable": false }
              ]
            }
          }
        ],
        "matchedIndexes": [8, 13]
      }
    }
  }
}
```

Как это читать:

* **`transaction`** содержит полный контекст. Значение `fee` указано в лампортах. `accountKeys` — полный список ключей, включая ключи, загруженные из таблиц поиска адресов, в том же порядке, в котором их возвращает блокчейн. Значение `feePayer` всегда равно `accountKeys[0]`. Поле `error` содержит ошибку транзакции в виде структурированного JSON, например `{"InstructionError": [2, {"Custom": 6001}]}`, когда `status` равно `"error"`.
* **`summary`** имеет одинаковую структуру везде, где встречается: `type` (например, `swap` или `transfer`), понятное человеку поле `description` и структурированные данные `parsedData`, когда синтаксический анализатор распознает действие. Для обмена это протокол, суммы и минты. `transaction.summary` обозначает основное действие транзакции; каждая распознанная инструкция содержит собственное поле `summary` с той же структурой. Чтобы получить все обмены в транзакции, переберите `instructions` и прочитайте `summary.parsedData` там, где `summary.type` равно `"swap"`.
* **Суммы обмена сообщаются площадкой.** Значения `in_amount`, `actual_out_amount` и суммы внутренних обменов в `parsedData` — это значения, указанные площадкой, а не суммы, фактически отправленные или полученные кошельком пользователя. Они не учитывают комиссии за перевод Token-2022 и другие удержания, примененные вне обмена. Чтобы узнать точную полученную сумму, сравните балансы токен-аккаунта пользователя до и после транзакции.
* **`nativeTransfers`** и **`tokenTransfers`** содержат движения SOL и токенов, извлеченные синтаксическим анализатором из всей транзакции, в той же структуре, которую возвращает Parsed Events API. Благодаря этому потребители потока и API могут использовать общий код обработки. Оба поля присутствуют всегда, но могут быть пустыми.
* **`instructions`** содержит все инструкции транзакции в порядке выполнения: за каждой инструкцией верхнего уровня следуют ее внутренние инструкции. Каждая запись содержит собственную позицию: `instructionIndex` указывает, к какой инструкции верхнего уровня она относится (нумерация начинается с 0), `innerInstructionIndex` — ее позицию среди внутренних вызовов этой инструкции (`null` означает, что это сама инструкция верхнего уровня), а `stackHeight` — глубину вызова (1 для верхнего уровня). Используйте эти значения, а не позицию в массиве.
* **`matchedIndexes`** содержит индексы элементов `instructions`, которые фактически соответствуют вашему фильтру. Остальные элементы предоставляются для контекста. При `details: "matched"` массив содержит только совпадения, а `matchedIndexes` отсутствует.
* **Имена `decoded` записываются в snake\_case** (`in_amount`, `user_transfer_authority`), как они опубликованы в IDL программы. Целочисленные аргументы часто представлены строками (`"1000000"`), поскольку значения u64 не помещаются в числовой тип JavaScript.
* **`blockTime`** в настоящее время всегда равно `null`. Не полагайтесь на это поле.
* В одной транзакции может встречаться **сочетание декодированных и недекодированных инструкций**: полностью декодированный обмен может находиться рядом с нераспознанной заметкой. Выбирайте ветвь по `decoded`: когда его значение равно `null`, инструкция вместо этого содержит `rawData` (байты base58) и `rawAccounts` (обычный список публичных ключей), поэтому у вас всегда будут данные для обработки.

При `details: "raw"` поле `value` сокращается до метаданных транзакции и блоков данных. `accountKeys`, `nativeTransfers`, `tokenTransfers`, `matchedIndexes` и все декодированные поля отсутствуют (поле транзакции `summary` по-прежнему включено). Каждая совпавшая инструкция представлена своей позицией, программой и байтами `data` в формате base58 точно в том виде, в котором они находятся в блокчейне (они присутствуют даже для инструкций, которые можно было декодировать по каталогу):

```json theme={"system"}
"value": {
  "transaction": {
    "signature": "3riSYL4HTRxgQjLayt6L2JPaDR3oaEQg1H4v3fnjUxNU...",
    "slot": 430172053,
    "blockTime": null,
    "feePayer": "6jduWNCTQzG91JGBchfGGxd55Vi5FxJCCJEV18RkXzJX",
    "fee": 5000,
    "status": "ok",
    "error": null,
    "summary": {
      "type": "swap",
      "description": "6jduWNCTQzG91JGBchfGGxd55Vi5FxJCCJEV18RkXzJX swapped 0.001 SOL for 0.183985 EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v via Jupiter",
      "parsedData": {
        "type": "swap",
        "protocol": "jupiter",
        "kind": "swap",
        "in_amount": "1000000",
        "actual_out_amount": "183985",
        "input_mint": "So11111111111111111111111111111111111111112",
        "output_mint": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"
      }
    }
  },
  "instructions": [
    { "instructionIndex": 4, "innerInstructionIndex": null, "stackHeight": 1, "programId": "JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4", "data": "3Bxs4h24hBtQy9rw" }
  ]
}
```

### Отмена подписки

```json theme={"system"}
{ "jsonrpc": "2.0", "id": 2, "method": "parsedTransactionUnsubscribe", "params": [23] }
```

Возвращает `true`, если подписка существовала и принадлежала вам. Уведомления прекращаются немедленно. При закрытии подключения удаляются все его подписки.

### Обнаружение

Самая распространенная ошибка при работе с API такого типа — допустимый фильтр, который ничего не находит, обычно из-за неверно подобранного имени инструкции или роли. `describeProgram` предотвращает это, возвращая точные имена, с которыми выполняется сопоставление. Сейчас этот метод доступен только в `wss://fs-beta.helius-rpc.com/?api-key=<API_KEY>`, поэтому отправляйте его через отдельное от подписок подключение:

```json Request theme={"system"}
{ "jsonrpc": "2.0", "id": 1, "method": "describeProgram", "params": [{ "program": "JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4" }] }
```

```json Response theme={"system"}
{
  "jsonrpc": "2.0", "id": 1,
  "result": {
    "id": "JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4",
    "name": "jupiter",
    "instructions": ["route", "shared_accounts_route", "exact_out_route"],
    "events": ["SwapEvent"],
    "roles": ["user_transfer_authority", "destination_token_account"]
  }
}
```

Можно передать адрес программы или ее имя в каталоге, но **предпочтительно использовать адрес**: имена могут быть неоднозначными в разных версиях программы (несколько записей каталога имеют имя `jupiter`, и поиск по имени может вернуть более старую запись). Если вы выполняете поиск по имени, убедитесь, что `result.id` — это программа, на которую вы хотите подписаться.

Рекомендуемый порядок действий: вызовите `describeProgram`, чтобы получить точные имена инструкций и ролей, создайте фильтр с этими именами, а затем оформите подписку. Полный процесс пошагово описан в руководстве [«Отслеживание обменов Jupiter»](/docs/ru/parsed-streams/guides/track-jupiter-swaps).

### Ограничения

| Ограничение | Значение |
| - | - |
| Одновременные подключения на проект | 5 (Free), 10 (Developer), 50 (Business и Professional) |
| Подписки на одно подключение | 25 |
| Сообщения клиента | 10 в секунду, пакет до 20 |
| Размер сообщения клиента | 64 КиБ |
| `programs` на фильтр | 10 |
| `instructionNames` на фильтр | 50, до 64 символов каждый |
| `accounts.include` на фильтр | 100 |
| `accounts.roles` на фильтр | 20, каждое имя — до 64 символов |
| Буфер исходящих данных на подключение | 2048 уведомлений, после чего подключение закрывается |

### Ошибки

Ошибки соответствуют JSON-RPC 2.0: `{ "error": { "code": <int>, "message": "<text>" }, "id": <id> }`. В сообщениях точно указано, что и где пошло не так.

| Код | Значение |
| - | - |
| `-32700` | Ошибка синтаксического анализа (недопустимый JSON) |
| `-32600` | Недопустимый запрос |
| `-32601` | Метод не найден |
| `-32602` | Недопустимые параметры: неверный публичный ключ, неизвестное поле, неподдерживаемое значение commitment или details |
| `-32000` | Превышен лимит фильтра |
| `-32001` | Сервер не готов; повторите попытку с увеличивающейся задержкой |
| `-32002` | Превышено ограничение частоты запросов (10 сообщений в секунду) |
| `-32006` | Слишком много подписок (25 на подключение) |

Подключения также могут закрываться с кодом закрытия WebSocket. Значение каждого кода и способы восстановления см. в руководстве [«Обработка переподключений»](/docs/ru/parsed-streams/guides/handling-reconnects).

## Примеры клиентов

<CodeGroup>
  ```bash wscat theme={"system"}
  wscat -c "wss://beta.helius-rpc.com/?api-key=<API_KEY>"
  # then send:
  {"jsonrpc":"2.0","id":1,"method":"parsedTransactionSubscribe","params":[{"programs":["JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4"]}]}
  ```

  ```typescript TypeScript theme={"system"}
  import WebSocket from "ws";

  const ws = new WebSocket("wss://beta.helius-rpc.com/?api-key=<API_KEY>");

  ws.on("open", () => {
    ws.send(JSON.stringify({
      jsonrpc: "2.0",
      id: 1,
      method: "parsedTransactionSubscribe",
      params: [{ programs: ["JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4"] }],
    }));
  });

  ws.on("message", (data) => {
    const msg = JSON.parse(data.toString());
    if (msg.method === "parsedTransactionNotification") {
      const { transaction, instructions, matchedIndexes } = msg.params.result.value;
      for (const i of matchedIndexes ?? instructions.keys()) {
        const ix = instructions[i];
        console.log(transaction.signature, ix.programName, ix.instructionName, ix.decoded?.args);
      }
    }
  });
  ```

  ```python Python theme={"system"}
  import asyncio, json, websockets

  URL = "wss://beta.helius-rpc.com/?api-key=<API_KEY>"

  async def main():
      async with websockets.connect(URL) as ws:
          await ws.send(json.dumps({
              "jsonrpc": "2.0", "id": 1, "method": "parsedTransactionSubscribe",
              "params": [{"programs": ["JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4"]}],
          }))
          async for raw in ws:
              msg = json.loads(raw)
              if msg.get("method") == "parsedTransactionNotification":
                  value = msg["params"]["result"]["value"]
                  for i in value.get("matchedIndexes") or range(len(value["instructions"])):
                      ix = value["instructions"][i]
                      print(ix.get("programName"), ix.get("instructionName"), (ix.get("decoded") or {}).get("args"))

  asyncio.run(main())
  ```
</CodeGroup>


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