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

<AgentInstructions>

## Submitting Feedback

If you encounter incorrect, outdated, or confusing documentation on this page, submit feedback:

POST https://docs.sqd.dev/feedback

```json
{
  "path": "/en/portal/solana/api",
  "feedback": "Description of the issue"
}
```

Only submit feedback when you have something specific and actionable to report.

</AgentInstructions>

# Solana Portal API Reference

> Quick reference for the SQD Portal Solana HTTP API — endpoints, request schemas, and code samples for querying blocks, transactions, and instructions.

This page provides a quick reference for the Portal API. For the complete OpenAPI specification with all fields and options:

<Card title="Complete Solana API Specification" icon="book-open" href="/en/portal/solana/api" className="mb-8">
  View the full OpenAPI documentation with all available fields, filters, and
  detailed schemas →
</Card>

## Base URL

All Portal requests are made to:

```
https://portal.sqd.dev/datasets/{network}
```

Replace `{network}` with your target blockchain (e.g., `solana-mainnet`).

## Core Endpoints

<CardGroup cols={3}>
  <Card title="POST /stream" icon="wave-pulse">
    Stream blockchain data with filters. Includes unfinalized blocks.
  </Card>

  {" "}

  <Card title="POST /finalized-stream" icon="check-circle">
    Stream only finalized blocks. Use for production pipelines requiring
    certainty.
  </Card>

  <Card title="GET /metadata" icon="info-circle">
    Get dataset information: slot range, finality rules, and network details.
  </Card>
</CardGroup>

## Installation

```bash theme={"system"}
npm install @subsquid/portal-client
```

## Quick Start Examples

<CodeGroup>
  ```bash curl theme={"system"}
  curl --compressed -X POST 'https://portal.sqd.dev/datasets/solana-mainnet/stream' \
    -H 'Content-Type: application/json' \
    -d '{
      "type": "solana",
      "fromBlock": 259984950,
      "toBlock": 259985000,
      "fields": {
        "block": { "number": true, "timestamp": true },
        "instruction": { "programId": true, "accounts": true, "data": true }
      }
    }'
  ```

  ```typescript TypeScript theme={"system"}
  import { DataSource } from "@subsquid/portal-client";

  const dataSource = new DataSource({
    network: "solana-mainnet",
  });

  const blocks = await dataSource.getBlocks({
    from: 259984950,
    to: 259985000,
    fields: {
      block: { number: true, timestamp: true },
      instruction: { programId: true, accounts: true, data: true },
    },
  });
  ```

  ```python Python theme={"system"}
  import requests

  url = "https://portal.sqd.dev/datasets/solana-mainnet/stream"
  headers = {"Content-Type": "application/json"}
  payload = {
      "type": "solana",
      "fromBlock": 259984950,
      "toBlock": 259985000,
      "fields": {
          "block": {"number": True, "timestamp": True},
          "instruction": {"programId": True, "accounts": True, "data": True}
      }
  }

  response = requests.post(url, headers=headers, json=payload)
  ```
</CodeGroup>

## Available Fields

### Block Fields

Select which block header fields to retrieve:

```typescript theme={"system"}
fields: {
  block: {
    number: true,          // Slot number (field name: number)
    hash: true,            // Block hash
    parentNumber: true,    // Parent slot number
    parentHash: true,      // Parent block hash
    height: true,          // Block height
    timestamp: true,       // Unix timestamp
  }
}
```

### Transaction Fields

Select transaction data fields:

```typescript theme={"system"}
fields: {
  transaction: {
    signatures: true,      // Transaction signatures (base58)
    feePayer: true,        // Fee payer address
    err: true,             // Transaction error (null if successful)
    version: true,         // Transaction version
    accountKeys: true,     // Account keys
    recentBlockhash: true, // Recent blockhash
    fee: true,             // Transaction fee
    computeUnitsConsumed: true, // Compute units consumed
  }
}
```

### Instruction Fields

Select instruction data fields:

```typescript theme={"system"}
fields: {
  instruction: {
    programId: true,       // Program address
    accounts: true,        // Account addresses
    data: true,            // Instruction data (base58)
    transactionIndex: true, // Transaction index (use to match with transactions)
    instructionAddress: true,  // Instruction address in call tree
    isCommitted: true,     // Whether instruction succeeded
  }
}
```

### Log Fields

Select log message fields:

```typescript theme={"system"}
fields: {
  log: {
    programId: true,       // Program address
    message: true,         // Log message
    kind: true,            // Log kind (log, data, other)
    transactionIndex: true, // Transaction index (use to match with transactions)
    instructionAddress: true,  // Instruction address
  }
}
```

### Token Balance Fields

Select token balance fields:

```typescript theme={"system"}
fields: {
  tokenBalance: {
    account: true,         // Token account address
    preMint: true,         // Mint before transaction
    postMint: true,        // Mint after transaction
    preAmount: true,       // Balance before transaction
    postAmount: true,      // Balance after transaction
    preOwner: true,        // Owner before transaction
    postOwner: true,       // Owner after transaction
  }
}
```

### Balance Fields

Select SOL balance fields:

```typescript theme={"system"}
fields: {
  balance: {
    account: true,         // Account address
    pre: true,             // Balance before transaction
    post: true,            // Balance after transaction
  }
}
```

### Reward Fields

Select reward fields:

```typescript theme={"system"}
fields: {
  reward: {
    pubkey: true,          // Reward recipient
    lamports: true,        // Reward amount
    rewardType: true,      // Reward type
    postBalance: true,     // Balance after reward
  }
}
```

## Filtering Options

### Instruction Filters

Filter instructions by program, discriminator, or accounts:

```typescript theme={"system"}
instructions: [
  {
    programId: ["whirLbMiicVdio4qvUfM5KAg6Ct8VwpYzGff3uctyCc"], // Orca Whirlpool
    d8: ["0xf8c69e91e17587c8"], // Swap instruction discriminator
    accounts: ["7qbRF6YsyGuLUVs6Y1q64bdVrfe4ZcUUz1JRdoVNUJnm"], // Specific pool
  },
];
```

**Filter logic:**

* Multiple program IDs in one object = OR
* Multiple discriminators in one object = OR
* Multiple filter objects = OR

### Transaction Filters

Filter transactions by fee payer:

```typescript theme={"system"}
transactions: [
  {
    feePayer: ["YourWalletAddressHere"], // Fee payer addresses
  },
];
```

### Log Filters

Filter logs by program and kind:

```typescript theme={"system"}
logs: [
  {
    programId: ["whirLbMiicVdio4qvUfM5KAg6Ct8VwpYzGff3uctyCc"],
    kind: ["log"], // log, data, or other
  },
];
```

### Token Balance Filters

Filter token balances by mint or account:

```typescript theme={"system"}
tokenBalances: [
  {
    preMint: ["EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"], // USDC
    account: ["TokenAccountAddress..."],
  },
];
```

## Network Selection

Specify the Solana network when creating the data source:

```typescript theme={"system"}
// Solana mainnet
const solana = new DataSource({ network: "solana-mainnet" });
```

<Card title="View All Networks" icon="database" href="/en/data/networks/evm">
  See complete list of supported Solana networks
</Card>

## Query Patterns

### Pattern 1: Range Query

Query a specific slot range:

```typescript theme={"system"}
const blocks = await dataSource.getBlocks({
  from: 259984950,
  to: 260000000,
  fields: {
    /* fields */
  },
  instructions: [
    /* filters */
  ],
});
```

### Pattern 2: Batch Processing

Process large ranges in batches:

```typescript theme={"system"}
const BATCH_SIZE = 10000;

for (let i = startSlot; i < endSlot; i += BATCH_SIZE) {
  const blocks = await dataSource.getBlocks({
    from: i,
    to: Math.min(i + BATCH_SIZE, endSlot),
    fields: {
      /* fields */
    },
  });

  // Process blocks
  await processBlocks(blocks);
}
```

### Pattern 3: Multi-Program Query

Query multiple programs simultaneously:

```typescript theme={"system"}
const blocks = await dataSource.getBlocks({
  from: 259984950,
  to: 260000000,
  fields: {
    instruction: { programId: true, accounts: true, data: true },
  },
  instructions: [
    {
      programId: [
        "whirLbMiicVdio4qvUfM5KAg6Ct8VwpYzGff3uctyCc", // Orca Whirlpool
        "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", // SPL Token
      ],
    },
  ],
});
```

## Performance Optimization

### 1. Request Only Needed Fields

```typescript theme={"system"}
// Good: Minimal fields
fields: {
  instruction: {
    programId: true,
    accounts: true,
  }
}

// Avoid: Requesting unnecessary fields
fields: {
  instruction: {
    programId: true,
    accounts: true,
    data: true,        // Only if needed
    transactionIndex: true,     // Only if needed (use to match with transactions)
  }
}
```

### 2. Use Specific Filters

```typescript theme={"system"}
// Good: Filter at Portal level
instructions: [
  {
    programId: ["SpecificProgram"],
    d8: ["SpecificDiscriminator"],
  },
];

// Avoid: Fetching everything and filtering in code
instructions: []; // Gets all instructions
```

### 3. Optimal Batch Sizes

```typescript theme={"system"}
// Good: 10k-50k slots per query for most use cases
const BATCH_SIZE = 10000;

// Adjust based on:
// - More filters = larger batches OK
// - More fields = smaller batches
// - More activity (mainnet) = smaller batches
```

### 4. Parallel Queries

```typescript theme={"system"}
// Process multiple ranges in parallel
const ranges = [
  { from: 259984950, to: 260000000 },
  { from: 260000000, to: 260015000 },
  { from: 260015000, to: 260030000 },
];

const results = await Promise.all(
  ranges.map((range) =>
    dataSource.getBlocks({
      from: range.from,
      to: range.to,
      fields: {
        /* fields */
      },
    })
  )
);
```

## Stream continuation

A single `/stream` response is a **batch**, not the complete range. The server may close the connection at any point: when a worker's range ends, when a connection is recycled, or at the current dataset head. Clients that treat one HTTP response as the whole query will silently drop slots.

To stream a range to completion, loop:

```typescript theme={"system"}
async function streamRange(from: number, to: number | undefined, body: any) {
  let currentFrom = from;
  while (to === undefined || currentFrom <= to) {
    const res = await fetch(
      "https://portal.sqd.dev/datasets/solana-mainnet/stream",
      {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ ...body, fromBlock: currentFrom, toBlock: to }),
      }
    );

    if (res.status === 204) break; // range is above the dataset head
    if (!res.ok) throw new Error(`HTTP ${res.status}: ${await res.text()}`);

    const lines = (await res.text()).trim().split("\n").filter(Boolean);
    if (lines.length === 0) break;

    let lastBlock = currentFrom - 1;
    for (const line of lines) {
      const block = JSON.parse(line);
      // ... process block ...
      lastBlock = block.header.number;
    }
    currentFrom = lastBlock + 1;
  }
}
```

Key points:

* Read the last block's `header.number`, issue the next request with `fromBlock = lastNumber + 1`.
* Keep `toBlock`, `fields`, and filters identical across retries.
* A `204 No Content` response means the range is above the dataset head. For historical queries, you're done; for open-ended queries, wait and retry.

## Handling reorgs near the chain head

When a request starts at or near the chain tip, the block you asked for may no longer be on the canonical chain by the time Portal processes the query. To detect this, pass `parentBlockHash` with each request (the hash of the block immediately before `fromBlock`):

```typescript theme={"system"}
const body = {
  type: "solana",
  fromBlock: lastBlock.number + 1,
  parentBlockHash: lastBlock.hash, // hash of lastBlock, i.e. parent of fromBlock
  fields: { block: { number: true, hash: true, parentHash: true } },
  instructions: [
    /* filters */
  ],
};
```

If the chain has reorged away from `parentBlockHash`, Portal returns **HTTP 409** with a list of blocks that **are** on the current canonical chain:

```json theme={"system"}
{
  "previousBlocks": [
    { "number": 259984850, "hash": "A1b2C3..." },
    { "number": 259984849, "hash": "9F8e7D..." }
  ]
}
```

Client recovery:

1. Find a block in your local state whose `(number, hash)` matches one of `previousBlocks`.
2. Roll back your pipeline to that block.
3. Resume the stream with `fromBlock = matched.number + 1` and `parentBlockHash = matched.hash`.

Streams opened against `/finalized-stream` (instead of `/stream`) never return 409: only finalized blocks are served, so there's nothing to reorg. Use that endpoint when you don't need real-time data.

## Error Handling

```typescript theme={"system"}
try {
  const blocks = await dataSource.getBlocks({
    from: 259984950,
    to: 260000000,
    fields: {
      /* fields */
    },
  });
} catch (error) {
  if (error.code === "RATE_LIMIT_EXCEEDED") {
    // HTTP 429: Public Portal rate limit. Honor Retry-After if set.
    console.error("Rate limit exceeded, waiting...");
    await new Promise((resolve) => setTimeout(resolve, 10000));
  } else if (error.code === "CONFLICT") {
    // HTTP 409: chain reorg. See "Handling reorgs near the chain head" above.
    const { previousBlocks } = error.body;
    // Roll back to a common ancestor and resume.
  } else if (error.code === "INVALID_SLOT_RANGE") {
    // HTTP 400: fromBlock is below the dataset's start_block, or the query is malformed.
    console.error("Invalid slot range");
  } else if (error.code === "NETWORK_ERROR") {
    // Transport error, retry with backoff.
    console.error("Network error, retrying...");
  } else {
    // HTTP 500: do not retry. HTTP 503: retry later, honor Retry-After.
    console.error("Unknown error:", error);
  }
}
```

## Common Use Cases

### Use Case 1: Instruction Monitoring

Monitor specific program instructions:

```typescript theme={"system"}
const blocks = await dataSource.getBlocks({
  from: latestProcessedSlot + 1,
  fields: {
    block: { number: true, timestamp: true },
    instruction: { programId: true, accounts: true, data: true },
  },
  instructions: [
    {
      programId: [PROGRAM_ADDRESS],
      d8: [INSTRUCTION_DISCRIMINATOR],
    },
  ],
});
```

### Use Case 2: Transaction Tracking

Track transactions from/to specific addresses:

```typescript theme={"system"}
const blocks = await dataSource.getBlocks({
  from: startSlot,
  to: endSlot,
  fields: {
    transaction: { signatures: true, feePayer: true },
  },
  transactions: [
    {
      feePayer: [WATCHED_ADDRESS],
    },
  ],
});
```

### Use Case 3: Analytics Pipeline

Build analytics on blockchain data:

```typescript theme={"system"}
const blocks = await dataSource.getBlocks({
  from: startSlot,
  to: endSlot,
  fields: {
    block: { number: true, timestamp: true },
    transaction: { fee: true },
  },
});

// Aggregate statistics
const stats = blocks.map((block) => ({
  slot: block.header.number,
  txCount: block.transactions.length,
  totalFees: block.transactions.reduce(
    (sum, tx) => sum + BigInt(tx.fee || 0),
    0n
  ),
}));
```

## Request Structure Reference

Every Portal request follows this structure:

```typescript theme={"system"}
{
  type: "solana",              // Chain type
  fromBlock: number,            // Starting slot number (inclusive, field name: fromBlock)
  toBlock?: number,             // Ending slot number (inclusive, optional, field name: toBlock)
  fields: {                    // What data to return
    block?: { ... },
    transaction?: { ... },
    instruction?: { ... },
    log?: { ... },
    tokenBalance?: { ... },
    balance?: { ... },
    reward?: { ... }
  },
  instructions?: [...],        // Filter by instructions
  transactions?: [...],       // Filter by transactions
  logs?: [...],               // Filter by logs
  tokenBalances?: [...],      // Filter by token balances
  balances?: [...],           // Filter by balances
  rewards?: [...]             // Filter by rewards
}
```

## Response Format

Portal returns **newline-delimited JSON (NDJSON)**, with one block per line:

```json theme={"system"}
{"header": {"number": 259984950, ...}, "instructions": [...], "transactions": [...]}
{"header": {"number": 259984951, ...}, "instructions": [...], "transactions": [...]}
{"header": {"number": 259984952, ...}, "instructions": [...], "transactions": [...]}
```

This format enables constant-memory streaming of arbitrary ranges.

## Common Patterns

<AccordionGroup>
  <Accordion title="Batch Processing">
    Process large slot ranges by splitting into manageable chunks:

    ```typescript theme={"system"}
    const BATCH_SIZE = 10000;
    for (let from = startSlot; from < endSlot; from += BATCH_SIZE) {
      const to = Math.min(from + BATCH_SIZE, endSlot);
      const blocks = await fetch(/* ... */);
    }
    ```
  </Accordion>

  <Accordion title="Real-time Streaming">
    Stream new blocks as they arrive using /finalized-stream:

    ```typescript theme={"system"}
    const response = await fetch(
      'https://portal.sqd.dev/datasets/solana-mainnet/finalized-stream',
      { method: 'POST', body: JSON.stringify({
        type: 'solana',
        fromBlock: latestProcessedSlot + 1,
        fields: { /* ... */ }
      })}
    );
    ```
  </Accordion>

  <Accordion title="Error Handling">
    Handle rate limits and network errors gracefully:

    ```typescript theme={"system"}
    try {
      const response = await fetch(/* ... */);
      if (!response.ok) {
        if (response.status === 429) {
          // Rate limited - wait and retry
          await sleep(10000);
        }
      }
    } catch (error) {
      // Network error - implement retry logic
    }
    ```
  </Accordion>
</AccordionGroup>

## Complete API Specification

For the complete OpenAPI specification with all available fields, options, and detailed schemas:

<Card title="Full Solana API Specification" icon="file-code" href="/en/portal/solana/api">
  View complete OpenAPI documentation with all endpoints, fields, and options →
</Card>

## Next Steps

<CardGroup cols={3}>
  <Card title="View Examples" icon="code" href="/en/portal/solana/examples/query-instructions">
    Explore practical Portal examples for common use cases
  </Card>

  <Card title="Supported Networks" icon="database" href="/en/data/networks/evm">
    See complete list of supported Solana networks
  </Card>

  <Card title="Quickstart Guide" icon="rocket" href="/en/portal/solana/quickstart">
    Make your first Portal request in 5 minutes
  </Card>
</CardGroup>
