Skip to main content

EVSignals API

Scan for live +EV signals, pull historical odds, and run backtests across 500+ connected sources through one API.

Start Here

Set your API key, make one filtered signals request, then branch into markets, webhooks, or backtests.

Best First Page

Use quickstart if you want the fastest successful first request instead of a full reference sweep.

Use This Page For

Endpoint paths, example payloads, auth patterns, error handling, and the main API surface in one place.

Real-time Signals

Get notifications when new +EV signals are detected across connected venues.

Historical Data

Access 15+ years of historical market data for backtesting and analysis.

Backtesting Engine

Test your strategies against historical data before deploying capital.

Base URL

https://api.evsignals.com/v1
Quick Start
Install SDK
pip install evsignals
Initialize Client
import evsignals

client = evsignals.Client(api_key="evs_test_your_api_key_here")
Get Signals
signals = client.signals.list(min_ev=0.02)
print(signals.data)

Authentication

The EVSignals API uses API keys to authenticate requests. You can view and manage your API keys in your dashboard. Your API keys carry many privileges, so be sure to keep them secure.

API Key Types

  • evs_live_ Production keys for live data and trading
  • evs_test_ Test keys for development and testing

Security Notice

Never expose your API keys in client-side code or public repositories. Use environment variables or a secrets manager.

Code Example
Authentication
import evsignals

# Initialize with your API key
client = evsignals.Client(
    api_key="evs_test_your_api_key_here"
)

# All subsequent requests are authenticated
signals = client.signals.list()

Signals

Signals represent +EV opportunities detected by our algorithms. Each signal includes the expected value, fair probability, current market price, and metadata about the underlying market.

Endpoints

GET /v1/signals List all signals
GET /v1/signals/{id} Retrieve a signal
POST /v1/signals/scan Scan for opportunities

Parameters

min_ev float Minimum expected value (0.01 = 1%)
markets array Filter by market platforms
status string Signal status (active, resolved, expired)
limit integer Number of results (default: 20, max: 100)
starting_after string Cursor for forward pagination (signal ID)
ending_before string Cursor for backward pagination (signal ID)

Pagination

List endpoints use cursor-based pagination. Use starting_after with the last object's ID to fetch the next page.

has_more Boolean indicating if more results exist
total_count Total number of matching results
List Signals
Request
# List all +EV signals
signals = client.signals.list(
    min_ev=0.02,        # Minimum 2% edge
    markets=["kalshi", "polymarket"],
    status="active",
    limit=100
)

for signal in signals.data:
    print(f"{signal.market}: {signal.ev_percent}% EV")
    print(f"  Contract: {signal.contract_name}")
    print(f"  Fair Value: {signal.fair_value}")
    print(f"  Current Price: {signal.current_price}")
Response
200 OK
{
  "object": "list",
  "data": [
    {
      "id": "sig_abc123",
      "object": "signal",
      "market": "kalshi",
      "contract_name": "Will BTC hit $150k by Dec 2026?",
      "ev_percent": 4.2,
      "fair_value": 0.65,
      "current_price": 0.58,
      "confidence": 0.85,
      "created_at": "2026-01-15T10:30:00Z",
      "expires_at": "2026-12-31T23:59:59Z"
    }
  ],
  "has_more": true
}
Scan for +EV
Request
# Scan for new +EV opportunities
scan_result = client.signals.scan(
    markets=["kalshi", "polymarket", "predictit"],
    categories=["politics", "economics", "sports"],
    min_ev=0.015,
    max_odds=10.0,
    min_liquidity=1000
)

print(f"Found {len(scan_result.opportunities)} opportunities")
for opp in scan_result.opportunities:
    print(f"  {opp.contract}: {opp.ev_percent:.2f}% edge")
Response
200 OK
{
  "object": "scan_result",
  "opportunities": [
    {
      "id": "opp_def456",
      "contract": "Chiefs ML vs Ravens",
      "market": "kalshi",
      "ev_percent": 3.85,
      "fair_value": 0.52,
      "current_price": 0.47,
      "max_odds": 2.13,
      "liquidity": 15000,
      "expires_at": "2026-02-10T01:00:00Z"
    }
  ],
  "total_scanned": 1250,
  "scan_duration_ms": 340
}

Markets

Markets represent individual prediction market contracts. Access real-time pricing, historical odds data, volume statistics, and market metadata across Kalshi, Polymarket, PredictIt, and more.

Endpoints

GET /v1/markets List all markets
GET /v1/markets/{id} Retrieve a market
GET /v1/markets/{id}/odds Get odds history

Supported Platforms

Kalshi
Polymarket
PredictIt
Manifold (Beta)
Retrieve Market
Request
# Get detailed market information
market = client.markets.retrieve("mkt_abc123")

print(f"Market: {market.name}")
print(f"Platform: {market.platform}")
print(f"Category: {market.category}")
print(f"Resolution Date: {market.resolution_date}")
print(f"Volume: ${market.volume:,.2f}")

# Get odds history
odds_history = client.markets.odds(
    "mkt_abc123",
    interval="1h",
    start_date="2024-01-01"
)
Response
200 OK
{
  "id": "mkt_abc123",
  "object": "market",
  "platform": "kalshi",
  "name": "Will BTC hit $150k by Dec 2026?",
  "category": "crypto",
  "status": "open",
  "yes_price": 0.58,
  "no_price": 0.42,
  "volume": 125000.00,
  "resolution_date": "2026-12-31T23:59:59Z",
  "created_at": "2026-01-01T00:00:00Z"
}

Portfolios

Create and manage portfolios to track your positions, monitor P&L, and automate rebalancing based on your strategy parameters.

Endpoints

GET /v1/portfolios List portfolios
POST /v1/portfolios Create portfolio
GET /v1/portfolios/{id} Retrieve portfolio
PUT /v1/portfolios/{id} Update portfolio
DELETE /v1/portfolios/{id} Delete portfolio
Create Portfolio
Request
# Create a new portfolio
portfolio = client.portfolios.create(
    name="High EV Strategy",
    description="Targeting 5%+ edge opportunities",
    settings={
        "min_ev": 0.05,
        "max_position_size": 500,
        "markets": ["kalshi", "polymarket"],
        "auto_rebalance": True
    }
)

print(f"Created portfolio: {portfolio.id}")

# Add a position
position = client.portfolios.add_position(
    portfolio.id,
    signal_id="sig_xyz789",
    amount=100
)
Response
200 OK
{
  "id": "pf_abc123",
  "object": "portfolio",
  "name": "High EV Strategy",
  "description": "Targeting 5%+ edge opportunities",
  "settings": {
    "min_ev": 0.05,
    "max_position_size": 500,
    "markets": ["kalshi", "polymarket"],
    "auto_rebalance": true
  },
  "total_value": 0,
  "positions": [],
  "created_at": "2026-01-15T10:30:00Z"
}

Backtests

Test your strategies against 15+ years of historical data. Analyze returns, drawdowns, win rates, and Sharpe ratios before deploying real capital.

Endpoints

POST /v1/backtests Run a backtest
GET /v1/backtests/{id} Get results
GET /v1/backtests/{id}/trades List trades

Metrics Returned

Total Return
Sharpe Ratio
Max Drawdown
Win Rate
Avg Position Size
Total Trades
Run Backtest
Request
# Run a backtest on historical data
backtest = client.backtests.create(
    strategy={
        "min_ev": 0.03,
        "max_odds": 5.0,
        "position_sizing": "kelly",
        "kelly_fraction": 0.25
    },
    start_date="2023-01-01",
    end_date="2024-01-01",
    initial_capital=10000,
    markets=["kalshi", "polymarket"]
)

# Wait for completion and get results
result = client.backtests.retrieve(backtest.id)
print(f"Total Return: {result.total_return:.2%}")
print(f"Sharpe Ratio: {result.sharpe_ratio:.2f}")
print(f"Max Drawdown: {result.max_drawdown:.2%}")
print(f"Win Rate: {result.win_rate:.2%}")
Response
200 OK
{
  "id": "bt_abc123",
  "object": "backtest",
  "status": "completed",
  "total_return": 0.342,
  "sharpe_ratio": 1.85,
  "max_drawdown": -0.12,
  "win_rate": 0.64,
  "total_trades": 156,
  "avg_position_size": 245.50,
  "start_date": "2023-01-01",
  "end_date": "2024-01-01",
  "created_at": "2026-01-15T10:30:00Z"
}

Webhooks

Receive real-time notifications when events occur. Webhooks are useful for building automated trading systems or alerting workflows.

Event Types

signal.created New +EV signal detected
signal.updated Signal EV or price changed
signal.resolved Market resolved, signal complete
signal.result Signal outcome determined (win/loss)
odds.movement Significant line movement detected
portfolio.alert Portfolio threshold triggered
Create Webhook
Request
# Create a webhook endpoint
webhook = client.webhooks.create(
    url="https://your-app.com/webhooks/evsignals",
    events=[
        "signal.created",
        "signal.updated",
        "signal.resolved",
        "portfolio.alert"
    ],
    secret="whsec_xxxxxxxxxxxxx"  # Optional, for signature verification
)

print(f"Webhook ID: {webhook.id}")
print(f"Webhook Secret: {webhook.secret}")
Webhook Payload
{
  "id": "evt_xyz789",
  "type": "signal.created",
  "created_at": "2026-01-15T10:30:00Z",
  "data": {
    "signal": {
      "id": "sig_abc123",
      "market": "kalshi",
      "ev_percent": 4.2,
      "contract_name": "Will BTC..."
    }
  }
}

Errors

EVSignals uses conventional HTTP response codes to indicate success or failure. Codes in the 2xx range indicate success, 4xx indicate client errors, and 5xx indicate server errors.

Error Codes

400 Bad Request The request was malformed or missing required parameters.
401 Unauthorized Invalid or missing API key.
403 Forbidden The API key does not have permission to perform the request.
404 Not Found The requested resource does not exist.
429 Rate Limited Too many requests. Please slow down and retry after the indicated time.
500 Server Error An error occurred on our servers. Please try again later.
Error Response Format
400 Bad Request
{
  "error": {
    "type": "invalid_request_error",
    "code": "parameter_invalid",
    "message": "min_ev must be between 0 and 1",
    "param": "min_ev",
    "doc_url": "https://evsignals.com/docs#errors"
  }
}

Rate Limits

API requests are rate limited to ensure fair usage. Rate limits vary by endpoint and plan tier.

Endpoint Free Pro Enterprise
Read (GET) 100/min 1,000/min 10,000/min
Write (POST/PUT/DELETE) 20/min 100/min 1,000/min
Scan 5/min 30/min 120/min
Backtest 2/min 10/min 50/min

Rate Limit Headers

X-RateLimit-Limit Max requests per window
X-RateLimit-Remaining Remaining in current window
X-RateLimit-Reset Unix timestamp when window resets
Retry-After Seconds to wait (on 429 only)
Rate Limit Response
429 Too Many Requests
{
  "error": {
    "type": "rate_limit_error",
    "message": "Rate limit exceeded",
    "retry_after": 30
  }
}

Headers:
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1705329600
Retry-After: 30

Need Higher Limits?

Enterprise plans include custom rate limits. Contact us to discuss your needs.

Start finding +EV opportunities

Get your API key in minutes and start scanning 500+ connected sources for live +EV signals.