# Solana RPC

> Endpoints, request format, limits and method availability for the constant-k Solana JSON-RPC API.

Source: https://docs.constant-k.com/rpc/

constant-k serves the standard Solana JSON-RPC API from mainnet nodes it builds and runs. Any Solana SDK works unchanged against a constant-k endpoint with your API key.

* Standard JSON-RPC methods, including `getTransaction`, `getSignaturesForAddress` and `getBlock`.
* Accounts indexed by program ID and by SPL token mint and owner, so queries such as `getTokenAccountsByOwner` resolve from an index.
* `sendTransaction` forwards to the next five leaders and rebroadcasts. See [Sending Transactions](https://docs.constant-k.com/rpc/sending-transactions/).
* WebSocket subscriptions from the Operator plan up. See [WebSockets](https://docs.constant-k.com/rpc/websockets/).

## Endpoints

```text
https://rpc-newyork.constant-k.com/?api-key=YOUR_API_KEY
https://rpc-frankfurt.constant-k.com/?api-key=YOUR_API_KEY
https://rpc-amsterdam.constant-k.com/?api-key=YOUR_API_KEY
https://rpc.constant-k.com/?api-key=YOUR_API_KEY          # nearest region
```

The key can also go in a header. See [Authentication](https://docs.constant-k.com/get-started/authentication/).

## Request format

Requests are JSON-RPC 2.0 over HTTP `POST`, one call per request. This example reads a wallet's SOL balance.

**cURL**

```bash
curl "https://rpc-newyork.constant-k.com/?api-key=YOUR_API_KEY" \
  -X POST \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "getBalance",
    "params": ["83astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDLVcri", {"commitment": "confirmed"}]
  }'
```

**TypeScript**

```ts title="balance.ts"
import { address, createSolanaRpc } from "@solana/kit";

const rpc = createSolanaRpc(
  `https://rpc-newyork.constant-k.com/?api-key=${process.env.CK_API_KEY}`,
);

const wallet = address("83astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDLVcri");
const { value: lamports } = await rpc
  .getBalance(wallet, { commitment: "confirmed" })
  .send();

console.log(`${lamports} lamports`);
```

**Python**

```python title="balance.py"
import os
import requests

url = f"https://rpc-newyork.constant-k.com/?api-key={os.environ['CK_API_KEY']}"
payload = {
    "jsonrpc": "2.0",
    "id": 1,
    "method": "getBalance",
    "params": ["83astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDLVcri", {"commitment": "confirmed"}],
}

body = requests.post(url, json=payload, timeout=10).json()
if "error" in body:
    raise RuntimeError(body["error"])

print(body["result"]["value"], "lamports")
```

**Rust**

```toml title="Cargo.toml"
[dependencies]
anyhow = "1"
solana-commitment-config = "3.1"
solana-pubkey = "4.2"
solana-rpc-client = "4.2"
tokio = { version = "1", features = ["rt-multi-thread", "macros"] }
```

```rust title="src/main.rs"
use std::str::FromStr;

use solana_commitment_config::CommitmentConfig;
use solana_pubkey::Pubkey;
use solana_rpc_client::nonblocking::rpc_client::RpcClient;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let url = format!(
        "https://rpc-newyork.constant-k.com/?api-key={}",
        std::env::var("CK_API_KEY")?
    );
    let client = RpcClient::new_with_commitment(url, CommitmentConfig::confirmed());

    let wallet = Pubkey::from_str("83astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDLVcri")?;
    let balance = client.get_balance(&wallet).await?;

    println!("{balance} lamports");
    Ok(())
}
```

Each method's parameters and response format are in the method reference, for example [getBalance](https://docs.constant-k.com/rpc/http/getbalance/), [getAccountInfo](https://docs.constant-k.com/rpc/http/getaccountinfo/) and [getTransaction](https://docs.constant-k.com/rpc/http/gettransaction/).

A response can carry HTTP `200` and a JSON-RPC `error` object. Any body with an `error` field is a failure, whatever the status code. See [Errors](https://docs.constant-k.com/rpc/errors/).

## Commitment levels

Most read methods take a `commitment` option. It sets how final the data must be.

| Level       | Meaning                                                    | Suits                                                 |
| :---------- | :--------------------------------------------------------- | :---------------------------------------------------- |
| `processed` | The node has processed the block. It can still be skipped. | The lowest latency, when you can handle rollbacks     |
| `confirmed` | A supermajority of stake has voted on the block.           | Most apps and trading logic                           |
| `finalized` | The block is rooted and will not change.                   | Balances you report to users, accounting, withdrawals |

## Method availability

Most methods are on every plan. Per-plan rates are on [Plans & Limits](https://docs.constant-k.com/get-started/plans-and-limits/).

| Methods                                                                                                                                                 | Plan                |
| :------------------------------------------------------------------------------------------------------------------------------------------------------ | :------------------ |
| `getLatestBlockhash`, `getMultipleAccounts`, `getSignatureStatuses`, `getSignaturesForAddress`, `getTokenAccountsByOwner`, the staked (SWQoS) send path | Operator and up     |
| `getBlocks`, `getBlocksWithLimit`, `getSupply`, `getTokenLargestAccounts`, `getProgramAccounts`, `getProgramAccountsV2`, Address Lookup Table lookups   | Blade Runner and up |

`getProgramAccounts`, `getProgramAccountsV2` and Address Lookup Table lookups have extra conditions:

* [`getProgramAccounts`](https://docs.constant-k.com/rpc/http/getprogramaccounts/) needs at least one filter (`dataSize` or `memcmp`). Scans of the token programs must be owner-scoped: a `dataSize: 165` filter plus a `memcmp` of the full 32-byte owner at offset 32. It runs at 2 calls per second on Blade Runner and 5 on Nexus. Each API key can have 1 call open at a time on Blade Runner and 3 on Nexus, counted together with `getProgramAccountsV2`. Over that, the endpoint returns `429`.
* [`getProgramAccountsV2`](https://docs.constant-k.com/rpc/http/getprogramaccountsv2/) is paginated and returns up to 10,000 accounts per page, 1,000 by default, at 2 calls per second on Blade Runner and 5 on Nexus.
* Address Lookup Table lookups are served from an index at 2 calls per second on Blade Runner and 5 on Nexus, through `getProgramAccounts` or `getProgramAccountsV2` with the ALT program id as the first parameter. There is no separate method or hostname.

### Methods not served on any plan

| Method                                 | Instead                                          |
| :------------------------------------- | :----------------------------------------------- |
| Batch requests (a JSON array of calls) | One call per HTTP request, in parallel if needed |
| `getLargestAccounts`                   | Not available                                    |
| `sendBundle`                           | Single transactions with `sendTransaction`       |

A batch is any JSON array body, including an array with one call in it. It is rejected with HTTP `400` before it reaches a node.

## Request limits

| Cap                                           | Value                                                                                                                         |
| :-------------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------- |
| Calls per request                             | One. A JSON array is rejected.                                                                                                |
| Request body                                  | 1 MB. Over that the gateway returns a plain HTTP `413`. The nodes themselves accept 200 KB, so that is the effective ceiling. |
| `getMultipleAccounts`, `getSignatureStatuses` | 100 items per call                                                                                                            |
| Time per request                              | 60 seconds. A longer call is cut off, so heavy `getBlock` and `getProgramAccounts` calls need narrowing.                      |

## Cached reads

Reads can be up to one slot old. Identical `getAccountInfo`, `getMultipleAccounts`, `getTokenAccountsByOwner`, `getLatestBlockhash`, `getSignaturesForAddress`, `getRecentPrioritizationFees` and `getSlotLeaders` calls that arrive within 366 ms of each other, which is one slot, share a single answer.

Polling the same account faster than that returns the same data and still uses your quota. A `getTransaction` for a transaction that has not landed is never cached, so confirmation polling always reads live state.

## Transaction history

History is served from each region's own ledger. Once a slot falls out of a region's ledger, `getTransaction` and `getSignaturesForAddress` can no longer return it.

[Cube Runner](https://docs.constant-k.com/cube-runner/) answers every `getBlock` request and can return blocks older than the ledger window. On every plan, it returns an older block when the region cache or another region's cache holds that block. On Blade Runner and Nexus tier plans, it also retrieves older blocks from long-term storage, which holds every block since genesis.

* The window is finite and moves with the chain. It is measured in stored data, not in hours, so its length changes with the size of recent blocks.
* The window differs between regions. A signature that resolves in one region can be missing in another. Only the region being queried can answer for its own window.
* [`getFirstAvailableBlock`](https://docs.constant-k.com/rpc/http/getfirstavailableblock/) returns the oldest block from which every later block can be retrieved with `getBlock`. On Blade Runner and Nexus tier plans, it returns `0`, the genesis block. On Free and Operator tier plans, it returns the oldest block from which the region holds every later block, in its cache or its ledger. Older blocks are still returned when a cache in any region holds them.
* [`minimumLedgerSlot`](https://docs.constant-k.com/rpc/http/minimumledgerslot/) returns the oldest slot the region's ledger holds. That slot is the history depth for `getTransaction` and `getSignaturesForAddress`.
* Both calls are cheap and report the actual depth rather than an assumed one.

Transaction and signature history older than the window has to come from your own store, written as the data arrives, or from an archive service.

## Browser requests

The endpoints answer CORS preflight requests and allow any origin, including credentials. They accept the `X-API-Key` and `solana-client` request headers, so a page can call the RPC directly.

> **A browser call exposes your API key:** Anyone who loads the page can read the key. A public site needs its own backend in front.

## Efficient calls

* One HTTP client with keep-alive per process. A new TLS handshake on every call adds a full round trip or more.
* One `getMultipleAccounts` call fetches up to 100 accounts, in place of many `getAccountInfo` calls.
* `encoding: "base64"` with `dataSlice` returns only the bytes you need.
* `minContextSlot` rejects state older than a slot you have already seen.
* A [WebSocket](https://docs.constant-k.com/rpc/websockets/) subscription or a [Kaldera](https://docs.constant-k.com/kaldera/) stream replaces fast polling.
* One region per flow. See [Choosing a region](https://docs.constant-k.com/get-started/endpoints/#choosing-a-region).
