Documentation Index
Fetch the complete documentation index at: https://helius-feat-signup-payment-link-docs.mintlify.app/llms.txt
Use this file to discover all available pages before exploring further.
Best practices and recommended patterns for agents using the Helius Rust SDK. For installation and getting started, see the overview.
Recommendations for Agents
Use get_transactions_for_address instead of two-step lookup
get_transactions_for_address combines signature lookup and transaction fetching into a single call with server-side filtering.
// GOOD: Single call, server-side filtering
let txs = helius.rpc().get_transactions_for_address(
"address".to_string(),
GetTransactionsForAddressOptions {
transaction_details: Some(TransactionDetails::Full),
limit: Some(100),
filters: Some(GetTransactionsFilters {
token_accounts: Some(TokenAccountsFilter::BalanceChanged),
..Default::default()
}),
..Default::default()
},
).await?;
// BAD: Two calls, client-side filtering
let sigs = helius.connection().get_signatures_for_address(&address)?;
Use send_smart_transaction for standard sends
It automatically simulates, estimates compute units, fetches priority fees, and confirms. Do not manually build ComputeBudget instructions — the SDK adds them automatically.
let sig = helius.send_smart_transaction(SmartTransactionConfig {
create_config: CreateSmartTransactionConfig {
instructions: vec![your_instruction],
signers: vec![wallet_signer],
priority_fee_cap: Some(100_000),
cu_buffer_multiplier: Some(1.1),
..Default::default()
},
..Default::default()
}).await?;
Use Helius Sender for ultra-low latency
For time-sensitive transactions (arbitrage, sniping, liquidations), use send_smart_transaction_with_sender. It routes through Helius’s multi-region infrastructure and Jito.
let sig = helius.send_smart_transaction_with_sender(
SmartTransactionConfig {
create_config: CreateSmartTransactionConfig {
instructions: vec![your_instruction],
signers: vec![wallet_signer],
..Default::default()
},
..Default::default()
},
SenderSendOptions {
region: "US_EAST".to_string(), // Default, US_SLC, US_EAST, EU_WEST, EU_CENTRAL, EU_NORTH, AP_SINGAPORE, AP_TOKYO
swqos_only: false, // true = SWQOS only (lower tip), false = Dual (SWQOS + Jito)
poll_timeout_ms: 60_000,
poll_interval_ms: 2_000,
},
).await?;
Use get_asset_batch for multiple assets
When fetching more than one asset, batch them. Do not call get_asset in a loop.
// GOOD: Single request
let assets = helius.rpc().get_asset_batch(GetAssetBatch {
ids: vec!["mint1".to_string(), "mint2".to_string(), "mint3".to_string()],
..Default::default()
}).await?;
// BAD: N requests
for id in mints {
let asset = helius.rpc().get_asset(GetAsset { id, ..Default::default() }).await?;
}
Use webhooks instead of polling
Do not poll get_transactions_for_address in a loop. Use webhooks for server-to-server notifications.
let webhook = helius.create_webhook(CreateWebhookRequest {
webhook_url: "https://your-server.com/webhook".to_string(),
webhook_type: WebhookType::Enhanced,
transaction_types: vec![TransactionType::Transfer, TransactionType::NftSale, TransactionType::Swap],
account_addresses: vec!["address_to_monitor".to_string()],
auth_header: Some("Bearer your-secret".to_string()),
..Default::default()
}).await?;
Token/Cursor-Based (RPC V2 Methods)
// get_transactions_for_address uses pagination_token
let mut pagination_token: Option<String> = None;
let mut all_txs = Vec::new();
loop {
let result = helius.rpc().get_transactions_for_address(
"address".to_string(),
GetTransactionsForAddressOptions {
limit: Some(100),
pagination_token: pagination_token.clone(),
..Default::default()
},
).await?;
all_txs.extend(result.data);
pagination_token = result.pagination_token;
if pagination_token.is_none() { break; }
}
// Or use auto-paginating variants:
let all_accounts = helius.rpc().get_all_program_accounts(
program_id.to_string(),
GetProgramAccountsV2Config::default(),
).await?;
Page-Based (DAS API)
let mut page = 1;
let mut all_assets = Vec::new();
loop {
let result = helius.rpc().get_assets_by_owner(GetAssetsByOwner {
owner_address: "...".to_string(),
page,
limit: Some(1000),
..Default::default()
}).await?;
let count = result.items.len();
all_assets.extend(result.items);
if count < 1000 { break; }
page += 1;
}
token_accounts Filter
When querying get_transactions_for_address, the token_accounts filter controls whether token account activity is included:
| Value | Behavior | Use When |
|---|
None | Only transactions directly involving the address | You only care about SOL transfers and program calls |
BalanceChanged | Also includes token transactions that changed a balance | Recommended for most agents — shows token sends/receives without noise |
All | Includes all token account transactions | You need complete token activity (can return many results) |
changed_since_slot — Incremental Account Fetching
changed_since_slot returns only accounts modified after a given slot. Useful for syncing or indexing workflows. Supported by get_program_accounts_v2, get_token_accounts_by_owner_v2, get_account_info, get_multiple_accounts, get_program_accounts, and get_token_accounts_by_owner.
// First fetch: get all accounts
let baseline = helius.rpc().get_program_accounts_v2(
program_id.to_string(),
GetProgramAccountsV2Config { limit: Some(10_000), ..Default::default() },
).await?;
let last_slot = current_slot;
// Later: only get accounts that changed since your last fetch
let updates = helius.rpc().get_program_accounts_v2(
program_id.to_string(),
GetProgramAccountsV2Config {
limit: Some(10_000),
changed_since_slot: Some(last_slot),
..Default::default()
},
).await?;
Common Mistakes
-
transaction_details: Some(TransactionDetails::Full) is not the default — By default, get_transactions_for_address returns signatures only. Set TransactionDetails::Full to get full transaction data.
-
Do not add ComputeBudget instructions with
send_smart_transaction — The SDK adds them automatically. Adding your own causes a HeliusError::InvalidInput error.
-
Priority fees are in microlamports per compute unit — Not lamports. Values from
get_priority_fee_estimate are already in the correct unit.
-
DAS pagination is 1-indexed —
page: 1 is the first page, not page: 0.
-
async_connection() requires new_async or HeliusBuilder — Calling helius.async_connection() on a client created with Helius::new() returns Err(HeliusError::ClientNotInitialized).
-
get_asset returns Option<Asset> — A successful response may still be None if the asset doesn’t exist. Handle the Option explicitly.
-
Sender tips are mandatory —
send_smart_transaction_with_sender automatically determines and appends tips. Minimum 0.0002 SOL (Dual mode) or 0.000005 SOL (SWQOS-only).
-
TLS feature flags — The crate defaults to
native-tls. Use features = ["rustls"] (and default-features = false) for pure-Rust TLS when OpenSSL is unavailable.
Error Handling and Retries
The SDK provides typed error variants via the HeliusError enum, so you can match on them directly:
use helius::error::{HeliusError, Result};
match helius.rpc().get_asset(request).await {
Ok(asset) => { /* success */ }
Err(HeliusError::Unauthorized { .. }) => { /* 401: invalid or missing API key */ }
Err(HeliusError::RateLimitExceeded { .. }) => { /* 429: too many requests or out of credits */ }
Err(HeliusError::InternalError { .. }) => { /* 5xx: server error, retry with backoff */ }
Err(HeliusError::NotFound { .. }) => { /* 404: resource not found */ }
Err(HeliusError::BadRequest { .. }) => { /* 400: malformed request */ }
Err(HeliusError::Timeout { .. }) => { /* transaction confirmation timed out */ }
Err(e) => { /* other errors: Network, SerdeJson, etc. */ }
}
Retry strategy
Retry on RateLimitExceeded and InternalError with exponential backoff:
async fn with_retry<T, F, Fut>(f: F, max_retries: u32) -> Result<T>
where
F: Fn() -> Fut,
Fut: std::future::Future<Output = Result<T>>,
{
for attempt in 0..=max_retries {
match f().await {
Ok(val) => return Ok(val),
Err(HeliusError::RateLimitExceeded { .. })
| Err(HeliusError::InternalError { .. }) if attempt < max_retries => {
tokio::time::sleep(std::time::Duration::from_millis(1000 * 2u64.pow(attempt))).await;
}
Err(e) => return Err(e),
}
}
unreachable!()
}
| Error Variant | HTTP Status | Action |
|---|
Unauthorized | 401 | Check API key |
RateLimitExceeded | 429 | Back off and retry |
InternalError | 5xx | Retry with exponential backoff |
BadRequest | 400 | Fix request parameters |
NotFound | 404 | Check resource exists |
Timeout | — | Increase timeout or retry |