Skip to main content

Overview

Kalshi is a CFTC-regulated prediction market based in the United States. It uses RSA certificate-based authentication for API access.
FeatureStatus
REST APIFull Support
WebSocketNot Supported
AuthenticationRSA Certificate
EnvironmentProduction & Demo

Configuration

Demo Environment (No Auth Required)

use pc_exchange_kalshi::{Kalshi, KalshiConfig};

// Use demo environment for testing
let config = KalshiConfig::demo();
let exchange = Kalshi::new(config)?;

// Fetch demo markets
let markets = exchange.fetch_markets(None).await?;

Production with Authentication

use pc_exchange_kalshi::{Kalshi, KalshiConfig};

let config = KalshiConfig::new()
    .with_api_key_id("your-api-key-id")
    .with_private_key_path("/path/to/private-key.pem");

let exchange = Kalshi::new(config)?;

Full Configuration Options

use pc_exchange_kalshi::{Kalshi, KalshiConfig};

let config = KalshiConfig::new()
    // Authentication
    .with_api_key_id("your-api-key-id")
    .with_private_key_path("/path/to/private-key.pem")
    // Or provide PEM content directly
    // .with_private_key_pem("-----BEGIN RSA PRIVATE KEY-----\n...")

    // API endpoint (optional)
    .with_api_url("https://api.elections.kalshi.com/trade-api/v2")

    // Logging
    .with_verbose(true);

let exchange = Kalshi::new(config)?;

Configuration Reference

OptionTypeDefaultDescription
api_key_idOption<String>NoneAPI key identifier
private_key_pathOption<String>NonePath to RSA private key PEM
private_key_pemOption<String>NoneRSA private key PEM content
api_urlStringProduction URLAPI endpoint
demoboolfalseUse demo environment
verboseboolfalseEnable debug logging

Environment Variables

export KALSHI_API_KEY_ID="your-api-key-id"
export KALSHI_PRIVATE_KEY_PATH="/path/to/private-key.pem"
Load from environment:
use std::env;

let config = KalshiConfig::new()
    .with_api_key_id(env::var("KALSHI_API_KEY_ID")?)
    .with_private_key_path(env::var("KALSHI_PRIVATE_KEY_PATH")?);

API Key Setup

To get API access to Kalshi:
  1. Create an account at kalshi.com
  2. Navigate to API settings in your account
  3. Generate an RSA key pair
  4. Upload your public key to Kalshi
  5. Store your private key securely
Never commit your private key to version control. Use environment variables or a secrets manager.

Examples

Fetch Markets

use pc_core::{Exchange, FetchMarketsParams};
use pc_exchange_kalshi::{Kalshi, KalshiConfig};

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    // Use demo for testing
    let exchange = Kalshi::new(KalshiConfig::demo())?;

    let markets = exchange
        .fetch_markets(Some(FetchMarketsParams {
            limit: Some(10),
            active_only: true,
        }))
        .await?;

    for market in markets {
        println!("─────────────────────────────────");
        println!("ID: {}", market.id);
        println!("Question: {}", market.question);
        println!("Outcomes: {:?}", market.outcomes);

        if !market.prices.is_empty() {
            for (outcome, price) in &market.prices {
                println!("  {}: {:.1}%", outcome, price * 100.0);
            }
        }
    }

    Ok(())
}

Place an Order

use pc_core::{Exchange, OrderSide};
use pc_exchange_kalshi::{Kalshi, KalshiConfig};
use std::collections::HashMap;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let config = KalshiConfig::new()
        .with_api_key_id("your-api-key-id")
        .with_private_key_path("/path/to/private-key.pem");

    let exchange = Kalshi::new(config)?;

    let order = exchange
        .create_order(
            "INXD-24DEC31-T5250",  // ticker
            "Yes",                  // outcome
            OrderSide::Buy,
            0.55,                   // price
            10.0,                   // quantity
            HashMap::new(),
        )
        .await?;

    println!("Order placed: {}", order.id);
    println!("Status: {:?}", order.status);

    Ok(())
}

Fetch Positions

let positions = exchange.fetch_positions(None).await?;

for pos in positions {
    println!(
        "{}: {} contracts @ ${:.2}",
        pos.market_id, pos.size, pos.average_price
    );
}

Fetch Balance

let balance = exchange.fetch_balance().await?;

if let Some(usd) = balance.get("USD") {
    println!("Available balance: ${:.2}", usd);
}

Market Tickers

Kalshi uses ticker-based market identification. Tickers follow a pattern like:
  • INXD-24DEC31-T5250 - S&P 500 above 5250 by Dec 31, 2024
  • FED-24DEC18-T450 - Fed rate at 4.50% by Dec 18, 2024
// Fetch a specific market by ticker
let market = exchange.fetch_market("INXD-24DEC31-T5250").await?;
println!("Question: {}", market.question);

Demo vs Production

AspectDemoProduction
URLdemo-api.kalshi.coapi.elections.kalshi.com
Auth RequiredNoYes
Real MoneyNoYes
Market DataSampleLive
// Demo - for testing
let demo = Kalshi::new(KalshiConfig::demo())?;

// Production - requires authentication
let production = Kalshi::new(
    KalshiConfig::new()
        .with_api_key_id("...")
        .with_private_key_path("...")
)?;

Rate Limits

Kalshi has API rate limits. The exchange automatically handles rate limiting, but be aware:
  • Default rate limit: 10 requests/second
  • Configurable through base ExchangeConfig
use std::time::Duration;

let config = KalshiConfig::new()
    // ... auth config ...
    .with_rate_limit(5);  // More conservative rate

Error Handling

use pc_core::{ParsecError, ExchangeError};

match exchange.fetch_markets(None).await {
    Ok(markets) => println!("Found {} markets", markets.len()),
    Err(ParsecError::Exchange(ExchangeError::Authentication(msg))) => {
        println!("Authentication failed: {}", msg);
        println!("Check your API key and private key configuration");
    }
    Err(ParsecError::RateLimitExceeded) => {
        println!("Rate limit exceeded, please wait");
    }
    Err(e) => println!("Error: {}", e),
}

Next Steps