Skip to content

BeckVision/shyft-solana

Repository files navigation

shyft-solana

CI

A Python client for the Shyft API on Solana. Fetch transactions, parse trades, build OHLCV candles, and query RPC — with built-in batching, async support, and multi-key rotation.

Works on the free tier. No paid plan required.

Repository Status

This repository now contains an installable first-pass client package plus the original API exploration notes. The client API is intentionally small and test-covered; parser coverage will expand as more real Shyft payloads are captured.

Related notes:

Features

  • REST + RPC in one client — transaction history, token supply, signature lookups
  • JSON-RPC batching — up to 300 RPC calls in a single request (~300x faster than sequential)
  • Sync and async — same API, powered by httpx
  • Multi-key rotation — use multiple API keys to multiply throughput
  • Trade parser — 4-layer detection pipeline for Solana DEX trades (Raydium, Pump.fun, Jupiter)
  • OHLCV builder — turn raw transactions into candlestick data
  • Fee calculator — accurate fee breakdowns for major Solana DEXes
  • Zero dependencies beyond httpx — no Django, no frameworks, just httpx and stdlib

Installation

PyPI release is planned. Until then, install from source:

git clone https://github.com/BeckVision/shyft-solana.git
cd shyft-solana
pip install -e .

Once published to PyPI:

pip install shyft-solana

Quick Start

Get a free API key at dashboard.shyft.to.

from shyft_solana import ShyftClient

client = ShyftClient(api_key="your-api-key")

# Fetch transaction history for a token
response = client.get_transaction_history("TokenAddress...", tx_num=50)

# Get token supply
supply = client.get_token_supply("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v")

# Count recent swaps for a pool
count = client.count_recent_swaps("PoolAddress...", since=datetime(2026, 3, 1))

Usage

Transaction History (REST)

from shyft_solana import ShyftClient

client = ShyftClient(api_key="your-key")

# Single page (up to 100 transactions)
response = client.get_transaction_history("TokenAddress...", tx_num=100)
transactions = response.get("result", [])

# Paginated — automatically fetches multiple pages
for tx in client.get_transaction_history_paginated("TokenAddress...", total_txns=500):
    print(tx["signatures"][0], tx["type"])

# Fetch all at once as a list
all_txs = client.fetch_all_token_transactions("TokenAddress...", max_txns=500)

RPC Methods

# Token supply
supply = client.get_token_supply("MintAddress...")

# Recent transaction signatures for a pool
count = client.count_recent_swaps("PoolAddress...", since=some_datetime)

Batch RPC

Send up to 300 RPC calls in a single HTTP request. One round-trip instead of 300.

# Batch token supply for multiple mints
mints = ["mint1...", "mint2...", "mint3...", "mint4..."]
supplies = client.batch_token_supply(mints)
# Returns: {"mint1...": 1000000, "mint2...": 500000, ...}

# Batch signature counts for multiple pools
from datetime import datetime, timezone
since = datetime(2026, 3, 14, tzinfo=timezone.utc)

pools = ["pool1...", "pool2...", "pool3..."]
counts = client.batch_recent_swaps(pools, since=since)
# Returns: {"pool1...": 42, "pool2...": 17, ...}

Async

Every method has an async counterpart. Use AsyncShyftClient instead of ShyftClient:

import asyncio
from shyft_solana import AsyncShyftClient

async def main():
    async with AsyncShyftClient(api_key="your-key") as client:
        # Same methods, just await them
        response = await client.get_transaction_history("TokenAddress...", tx_num=50)
        supply = await client.get_token_supply("MintAddress...")
        supplies = await client.batch_token_supply(["mint1...", "mint2...", "mint3..."])

asyncio.run(main())

Multi-Key Rotation

Use multiple API keys to multiply your throughput. Each key gets its own rate limit bucket.

# Pass multiple keys — the client rotates automatically
client = ShyftClient(api_keys=["key1", "key2", "key3", "key4"])

# Everything works the same — the client picks the best key for each request
response = client.get_transaction_history("TokenAddress...", tx_num=100)

What you get with multiple keys (free tier):

Keys REST Concurrent RPC Batch Concurrent
1 ~2 req/burst 300 calls/request
2 ~4 req/burst 600 calls (2 parallel batches)
4 ~8 req/burst 1,200 calls (4 parallel batches)

Multi-key shines with async, where requests actually run concurrently:

async with AsyncShyftClient(api_keys=["k1", "k2", "k3", "k4"]) as client:
    # 4 REST requests in parallel, each using a different key
    results = await asyncio.gather(
        client.get_transaction_history("token1..."),
        client.get_transaction_history("token2..."),
        client.get_transaction_history("token3..."),
        client.get_transaction_history("token4..."),
    )

You can also configure keys via environment variables:

# Single key
export SHYFT_API_KEY=your-key

# Multiple keys (comma-separated)
export SHYFT_API_KEYS=key1,key2,key3,key4
# Auto-discovers from environment
client = ShyftClient()

Parsing Trades

Parse raw Shyft transaction data into structured trade objects. Supports Raydium V4, Raydium CPMM, Pump.fun AMM, and Jupiter V6.

from shyft_solana import ShyftClient
from shyft_solana.parsers import parse_transactions_for_token

client = ShyftClient(api_key="your-key")

# Fetch raw transactions
response = client.get_transaction_history("TokenAddress...", tx_num=100)
transactions = response.get("result", [])

# Parse into structured trades
trades, unparsed, detected_pool = parse_transactions_for_token(
    transactions,
    token_address="TokenAddress...",
    pool_address="PoolAddress...",  # optional — auto-detected if omitted
)

for trade in trades:
    print(f"{trade.trade_type} {trade.token_amount} tokens @ {trade.price_per_token_sol} SOL")
    print(f"  Wallet: {trade.wallet_address}")
    print(f"  DEX: {trade.dex_name}")
    print(f"  Net SOL: {trade.sol_amount_net}")
    print(f"  Fees: LP={trade.lp_fee_sol} Protocol={trade.protocol_fee_sol}")

Each ExtractedTrade contains:

Field Type Description
signature str Transaction signature
timestamp datetime Block time
wallet_address str Trader's wallet
trade_type str BUY, SELL, TRANSFER_IN, TRANSFER_OUT
token_amount Decimal Tokens traded
price_per_token_sol Decimal Price in SOL
sol_amount_gross Decimal SOL before fees
sol_amount_net Decimal SOL after fees
dex_name str Raydium V4, Pump.fun AMM, etc.
pool_address str Liquidity pool address
lp_fee_sol Decimal LP fee
protocol_fee_sol Decimal Protocol fee
creator_fee_sol Decimal Creator fee
detection_layer str Which parser layer found this trade

Building OHLCV Candles

Turn parsed trades into candlestick data:

from shyft_solana.parsers.ohlcv import transactions_to_ohlcv

# From raw transactions (fetches + parses + bins in one call)
candles = transactions_to_ohlcv(transactions, token_address, pool_address, since=some_datetime)

for candle in candles:
    print(f"{candle['timestamp']} O={candle['open_price']:.8f} H={candle['high_price']:.8f} "
          f"L={candle['low_price']:.8f} C={candle['close_price']:.8f} V={candle['volume']:.4f}")

Candles are 5-minute intervals, SOL-denominated, sorted oldest-first.

Fee Calculation

Calculate accurate fee breakdowns for Solana DEX trades:

from decimal import Decimal
from shyft_solana.parsers.fees import calculate_fees_from_gross, calculate_fees_from_net

# From gross SOL amount (what the pool received)
fees = calculate_fees_from_gross(Decimal("1.5"), trade_type="BUY", dex_name="Pump.fun AMM")
print(f"Net: {fees['sol_amount_net']} SOL")
print(f"LP fee: {fees['lp_fee_sol']} SOL ({fees['lp_fee_bps']} bps)")
print(f"Protocol fee: {fees['protocol_fee_sol']} SOL ({fees['protocol_fee_bps']} bps)")

# Supported DEXes
# - Pump.fun AMM (SELL: 1.25%, BUY: 1.25%)
# - Raydium V4 (0.25% both ways)
# - Raydium CPMM (0.25% both ways)
# - Raydium CP Swap (0.25% both ways)
# - Jupiter V6 (0.25% both ways)

Supply Changes

Track token burns and mints:

from shyft_solana.parsers import extract_supply_changes

changes = extract_supply_changes(transactions, token_address="TokenAddress...")

for change in changes:
    print(f"{change.change_type} {change.token_amount} tokens at {change.timestamp}")

Configuration

Constructor Options

ShyftClient(
    api_key="...",        # Single API key
    api_keys=["..."],     # Multiple API keys (overrides api_key)
    timeout=15,           # Request timeout in seconds (default: 15)
    max_retries=3,        # Max retry attempts on failure (default: 3)
)

Environment Variables

Variable Description
SHYFT_API_KEY Single API key
SHYFT_API_KEYS Multiple keys, comma-separated

If both are set, SHYFT_API_KEYS takes priority. SHYFT_API_KEY is appended if it's not already in the list.

Free Tier Limits

Shyft's free plan ($0/month) provides:

Limit With Batching With 4 Keys
REST 1 req/sec (~2 concurrent) N/A ~8 concurrent
RPC 10 req/sec 300 calls/request 1,200 calls/request
Credits Unlimited Unlimited Unlimited

No daily caps. No credit costs. Batching is the biggest free speedup — 300 RPC calls in one request costs the same as one call.

How the Trade Parser Works

The parser uses a 4-layer detection strategy, trying each layer in order until a trade is found:

  1. Action-based (most reliable) — looks for explicit BUY/SELL actions in the transaction
  2. Swap action — parses swap-type actions with token transfers
  3. Pool balance analysis — detects trades from pool balance changes (pool receives tokens = SELL)
  4. Balance change fallback — infers from the user's token balance changes

Each trade is tagged with its detection_layer so you know how it was found.

Error Handling

All methods return None on failure with details in client.last_error:

result = client.get_token_supply("InvalidMint...")

if result is None:
    print(f"Failed: {client.last_error}")
    # {'status_code': 200, 'message': 'RPC error: ...', 'endpoint': 'rpc/getTokenSupply'}

The client automatically retries on:

  • 429 (rate limit) — respects Retry-After header, exponential backoff
  • 5xx (server error) — up to max_retries attempts
  • Network errors — connection timeouts, DNS failures

Testing

Run the test suite with:

pytest

License

MIT. See LICENSE.

About

Python client for Shyft on Solana with async batching, key rotation, trade parsing, and OHLCV utilities.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages