> ## 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.

# Panduan Memulai Cepat Parsed Streams

> Hubungkan ke Parsed Streams, kirim filter pertama Anda, dan baca notifikasi yang telah didekode. Dilengkapi referensi lengkap protokol JSON-RPC 2.0.

<Tip>
  Baru mengenal Parsed Streams? Baca [model mentalnya](/docs/id/parsed-streams#model-mental) terlebih dahulu — bagian tersebut menjelaskan alasan filter memiliki struktur seperti ini.
</Tip>

## Panduan Memulai Cepat

<Steps>
  <Step title="Get Access">
    Parsed Streams tersedia pada semua paket dengan biaya 1 kredit per peristiwa yang dikirimkan. Dapatkan kunci API Anda dari [Dasbor Helius](https://dashboard.helius.dev), lalu hubungkan ke endpoint [Gatekeeper](/docs/id/gatekeeper/overview) di `wss://beta.helius-rpc.com`, host yang sama dengan lalu lintas RPC dan WebSocket Helius.

    Lakukan autentikasi dengan kunci API proyek Anda, yang diteruskan sebagai parameter kueri `api-key` (atau header `x-api-key`).
  </Step>

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

    Kunci yang tidak ada atau tidak valid akan ditolak dengan HTTP 401. Proyek yang telah mencapai batas koneksinya akan menerima HTTP 429.
  </Step>

  <Step title="Subscribe with a Filter">
    Kirim `parsedTransactionSubscribe` dengan filter dan opsi opsional:

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

    `result` dalam respons adalah bilangan bulat **ID langganan**:

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

  <Step title="Read a Notification">
    Setiap transaksi yang cocok diterima sebagai `parsedTransactionNotification` yang telah didekode, dengan `matchedIndexes` yang menunjuk ke instruksi yang cocok dengan filter Anda. Lihat [Notifikasi](#notifikasi) untuk struktur lengkapnya.
  </Step>

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

    Atau cukup tutup koneksi — tindakan ini akan menghapus semua langganannya.
  </Step>
</Steps>

## Panduan

<CardGroup cols={2}>
  <Card title="Track Jupiter Swaps" icon="arrow-right-arrow-left" href="/docs/id/parsed-streams/guides/track-jupiter-swaps">
    Gunakan `describeProgram` untuk membuat filter yang dapat Anda percayai sebelum berlangganan.
  </Card>

  <Card title="Track Pump.fun Mints" icon="rocket" href="/docs/id/parsed-streams/guides/track-pumpfun-mints">
    Listener yang aman terhadap koneksi ulang dan mencatat setiap penerapan token Pump.fun baru.
  </Card>

  <Card title="Handling Reconnects" icon="rotate" href="/docs/id/parsed-streams/guides/handling-reconnects">
    Tangani batas waktu saat tidak aktif dan penerapan, lalu lakukan backfill secara tepat untuk data yang terlewat.
  </Card>
</CardGroup>

## Referensi Protokol

Parsed Streams menggunakan **JSON-RPC 2.0** melalui satu koneksi WebSocket. Setiap permintaan menerima respons dengan `id` yang sama. Langganan kemudian mengirim pesan `parsedTransactionNotification` hingga Anda berhenti berlangganan atau memutuskan koneksi.

| Metode                         | Tujuan                                                          |
| ------------------------------ | --------------------------------------------------------------- |
| `parsedTransactionSubscribe`   | Memulai langganan dengan filter                                 |
| `parsedTransactionUnsubscribe` | Menghentikan langganan                                          |
| `describeProgram`              | Mencantumkan instruksi, peristiwa, dan peran akun suatu program |

### Berlangganan

Kirim `parsedTransactionSubscribe` dengan filter dan opsi opsional. `result` dalam respons adalah bilangan bulat **ID langganan**.

```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 }
```

#### Kolom filter

Setidaknya salah satu dari `programs` atau `accounts.include` wajib diisi. Kolom yang Anda tetapkan digabungkan dengan **AND**: sebuah instruksi harus memenuhi semuanya agar dianggap cocok.

<ParamField body="programs" type="string[]">
  ID program yang akan dicocokkan (alamat base58, bukan nama). Sebuah instruksi cocok jika programnya ada dalam daftar ini. OR berlaku di dalam daftar.
</ParamField>

<ParamField body="instructionNames" type="string[]">
  Nama instruksi yang telah didekode, seperti `route`. Nama pertama-tama dicocokkan secara persis, lalu menggunakan pencocokan cadangan yang tidak peka terhadap kapitalisasi dan pemisah. Karena itu, `sharedAccountsRoute` juga cocok dengan nama wire `shared_accounts_route`. OR berlaku di dalam daftar. Hanya instruksi yang namanya dapat diidentifikasi oleh katalog yang bisa cocok. Oleh karena itu, ambil nama dari `describeProgram`.
</ParamField>

<ParamField body="accounts.include" type="string[]">
  Alamat akun. Sebuah instruksi cocok jika salah satu alamat ini muncul dalam daftar akunnya. OR berlaku di dalam daftar. Berlaku untuk setiap instruksi, baik yang didekode maupun tidak. ID program itu sendiri tidak dihitung sebagai akun di sini.
</ParamField>

<ParamField body="accounts.roles" type="object">
  Pemetaan nama peran akun yang telah didekode ke alamat, seperti `{ "user_transfer_authority": "<pubkey>" }`. Setiap entri harus terpenuhi (AND di seluruh entri), dan instruksi harus didekode agar aturan ini dapat diterapkan. Nama peran dicocokkan **secara persis**, tanpa penyeragaman kapitalisasi. Jadi, salin nama dari `describeProgram` dan jangan menebaknya.
</ParamField>

<ParamField body="includeFailed" type="boolean" default="false">
  Sertakan instruksi dari transaksi yang gagal.
</ParamField>

<ParamField body="includeCpi" type="boolean" default="true">
  Instruksi internal (CPI) dapat dicocokkan. Tetapkan `false` untuk hanya mencocokkan instruksi tingkat teratas.
</ParamField>

Kolom yang tidak dikenal di mana pun dalam filter atau opsi akan ditolak dengan `-32602` dan tidak diabaikan secara diam-diam. Dengan demikian, kesalahan ketik akan langsung menghasilkan kegagalan, bukan filter yang tidak cocok dengan apa pun.

#### Opsi

Parameter kedua bersifat opsional.

<ParamField body="commitment" type="string" default="confirmed">
  Hanya `confirmed` yang didukung.
</ParamField>

<ParamField body="details" type="string" default="full">
  Data yang dibawa setiap notifikasi. `full`: seluruh transaksi, setiap instruksi, serta `matchedIndexes` yang menunjuk ke kecocokan filter. `matched`: hanya instruksi yang cocok, tanpa daftar indeks. `raw`: hanya instruksi yang cocok, masing-masing disederhanakan menjadi posisinya, `programId`, dan blob `data` base58, tanpa kolom yang didekode dan tanpa larik `accountKeys`. Gunakan `matched` ketika bandwidth lebih penting daripada konteks (ukuran payload lengkap rata-rata sekitar tiga kali lebih besar), dan `raw` ketika Anda mendekode sendiri data instruksi dan hanya memerlukan byte-nya.
</ParamField>

Jumlah koneksi serentak per proyek bergantung pada paket Anda: **5** untuk Free, **10** untuk Developer, serta **50** untuk Business dan Professional, yang digunakan bersama oleh semua kunci API proyek. Lihat [Batas Laju](/docs/id/billing/rate-limits#stream-yang-diuraikan).

### Notifikasi

Satu notifikasi per transaksi yang cocok untuk setiap langganan. Dengan `details: "full"` default:

```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]
      }
    }
  }
}
```

Cara membacanya:

* **`transaction`** adalah konteks lengkap. `fee` dinyatakan dalam lamport. `accountKeys` adalah daftar kunci lengkap, termasuk kunci yang dimuat dari tabel pencarian alamat, dalam urutan yang sama seperti yang dilaporkan oleh chain. `feePayer` selalu bernilai `accountKeys[0]`. `error` memuat kesalahan transaksi sebagai JSON terstruktur, misalnya `{"InstructionError": [2, {"Custom": 6001}]}`, ketika `status` bernilai `"error"`.
* **`summary`** memiliki satu struktur yang sama di setiap kemunculannya: `type` (seperti `swap` atau `transfer`), `description` yang mudah dibaca manusia, serta payload `parsedData` terstruktur ketika parser mengenali tindakan tersebut — untuk swap: protokol, jumlah, dan mint. `transaction.summary` menandai tindakan utama transaksi; setiap instruksi yang dikenali memiliki `summary` sendiri dengan struktur yang sama. Untuk mengumpulkan setiap swap dalam transaksi, iterasikan `instructions` dan baca `summary.parsedData` ketika `summary.type` bernilai `"swap"`.
* **`nativeTransfers`** dan **`tokenTransfers`** mencantumkan perpindahan SOL dan token yang diekstrak parser dari seluruh transaksi, dengan struktur yang sama seperti yang dikembalikan API Parsed Events. Dengan demikian, konsumen stream dan API dapat menggunakan kode pemrosesan yang sama. Keduanya selalu ada, tetapi mungkin kosong.
* **`instructions`** berisi setiap instruksi transaksi dalam urutan eksekusi: setiap instruksi tingkat teratas diikuti instruksi internalnya. Setiap entri memiliki posisinya sendiri: `instructionIndex` menunjukkan instruksi tingkat teratas yang menaunginya (dimulai dari 0), `innerInstructionIndex` menunjukkan posisinya di antara panggilan internal instruksi tersebut (`null` berarti entri tersebut adalah instruksi tingkat teratas itu sendiri), dan `stackHeight` adalah kedalaman panggilan (1 untuk tingkat teratas). Gunakan nilai-nilai ini, bukan posisi lariknya.
* **`matchedIndexes`** adalah indeks ke dalam `instructions` yang menunjukkan instruksi mana yang benar-benar cocok dengan filter Anda. Instruksi lainnya disertakan sebagai konteks. Dengan `details: "matched"`, larik hanya berisi instruksi yang cocok dan `matchedIndexes` tidak ada.
* **Nama `decoded` menggunakan snake\_case** (`in_amount`, `user_transfer_authority`), sebagaimana dipublikasikan dalam IDL program. Argumen bilangan bulat biasanya berupa string (`"1000000"`) karena nilai u64 tidak dapat ditampung dalam angka JavaScript.
* **`blockTime`** saat ini selalu bernilai `null`. Jangan mengandalkannya.
* Dalam satu transaksi, Anda dapat menemukan **campuran instruksi yang didekode dan tidak didekode**: swap yang sepenuhnya didekode dapat muncul di samping memo yang tidak dikenali. Buat percabangan berdasarkan `decoded`: ketika nilainya `null`, instruksi tersebut membawa `rawData` (byte base58) dan `rawAccounts` (daftar pubkey biasa) sebagai gantinya. Dengan demikian, Anda selalu memiliki data yang dapat diproses.

Dengan `details: "raw"`, `value` menyusut menjadi metadata transaksi dan blob. `accountKeys`, `nativeTransfers`, `tokenTransfers`, `matchedIndexes`, dan semua kolom yang didekode dihapus (`summary` transaksi tetap disertakan); setiap instruksi yang cocok berisi posisinya, programnya, dan byte `data` dalam base58, persis seperti yang muncul pada chain (tetap ada bahkan untuk instruksi yang sebenarnya dapat didekode oleh katalog):

```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" }
  ]
}
```

### Berhenti Berlangganan

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

Mengembalikan `true` jika langganan tersebut ada dan merupakan milik Anda. Notifikasi langsung berhenti. Menutup koneksi akan menghapus semua langganannya.

### Penemuan

Kegagalan paling umum pada API semacam ini adalah filter yang valid tetapi tidak cocok dengan apa pun, biasanya karena nama instruksi atau peran ditebak. `describeProgram` mencegah hal tersebut dengan mengembalikan nama persis yang dibandingkan oleh pencocok. Saat ini, metode tersebut hanya tersedia pada `wss://fs-beta.helius-rpc.com/?api-key=<API_KEY>`. Jadi, kirim melalui koneksi terpisah dari langganan Anda:

```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"]
  }
}
```

Anda dapat meneruskan alamat program atau nama katalog, tetapi **utamakan alamat**: nama dapat ambigu di berbagai versi program (lebih dari satu entri katalog bernama `jupiter`, dan pencarian berdasarkan nama dapat mengarah ke entri yang lebih lama). Jika Anda melakukan pencarian berdasarkan nama, pastikan `result.id` adalah program yang ingin Anda langgani.

Alur yang disarankan: gunakan `describeProgram` untuk mendapatkan nama instruksi dan peran yang persis, buat filter dengan nama tersebut, lalu berlangganan. Panduan [Melacak Swap Jupiter](/docs/id/parsed-streams/guides/track-jupiter-swaps) menjelaskan proses ini dari awal hingga akhir.

### Batas

| Batas                         | Nilai                                                    |
| ----------------------------- | -------------------------------------------------------- |
| Koneksi serentak per proyek   | 5 (Free), 10 (Developer), 50 (Business dan Professional) |
| Langganan per koneksi         | 25                                                       |
| Pesan klien                   | 10 per detik, lonjakan hingga 20                         |
| Ukuran pesan klien            | 64 KiB                                                   |
| `programs` per filter         | 10                                                       |
| `instructionNames` per filter | 50, masing-masing hingga 64 karakter                     |
| `accounts.include` per filter | 100                                                      |
| `accounts.roles` per filter   | 20, setiap nama hingga 64 karakter                       |
| Buffer keluar per koneksi     | 2048 notifikasi, kemudian koneksi ditutup                |

### Kesalahan

Kesalahan mengikuti JSON-RPC 2.0: `{ "error": { "code": <int>, "message": "<text>" }, "id": <id> }`. Pesan menjelaskan secara persis apa yang salah dan lokasinya.

| Kode     | Arti                                                                                                                   |
| -------- | ---------------------------------------------------------------------------------------------------------------------- |
| `-32700` | Kesalahan penguraian (JSON tidak valid)                                                                                |
| `-32600` | Permintaan tidak valid                                                                                                 |
| `-32601` | Metode tidak ditemukan                                                                                                 |
| `-32602` | Parameter tidak valid: pubkey salah, kolom tidak dikenal, commitment tidak didukung, atau nilai details tidak didukung |
| `-32000` | Batas filter terlampaui                                                                                                |
| `-32001` | Server belum siap; coba lagi dengan backoff                                                                            |
| `-32002` | Terkena batas laju (10 pesan per detik)                                                                                |
| `-32006` | Terlalu banyak langganan (25 per koneksi)                                                                              |

Koneksi juga dapat ditutup dengan kode penutupan WebSocket — lihat [Menangani Koneksi Ulang](/docs/id/parsed-streams/guides/handling-reconnects) untuk mengetahui arti setiap kode dan cara memulihkan koneksi.

## Contoh Klien

<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>
