MCPcopy Index your code
hub / github.com/PublicDotCom/publicdotcom-py

github.com/PublicDotCom/publicdotcom-py @v0.1.17

Chat with this repo
repository ↗ · DeepWiki ↗ · release v0.1.17 ↗ · + Follow
1,547 symbols 5,980 edges 67 files 459 documented · 30% updated 20d agov0.1.17 · 2026-06-17★ 521 open issues
What it actually does AI analysis from the code graph — generated when you open this
loading…
README

Public API Python SDK

Version Python License

Public API Python SDK

A Python SDK for interacting with the Public Trading API, providing a simple and intuitive interface for trading operations, market data retrieval, and account management.

Installation

From PyPI

$ pip install publicdotcom-py

Run locally

$ python3 -m venv .venv
$ source .venv/bin/activate
$ pip install .

$ pip install -e .
$ pip install -e ".[dev]"  # for dev dependencies

$ # run example
$ python example.py

Run tests

$ pytest

Run examples

Inside of the examples folder are multiple python scripts showcasing specific ways to use the SDK. To run these Python files, first add your API_SECRET_KEY and DEFAULT_ACCOUNT_NUMBER to the .env.example file and change the filename to .env.

Quick Start

from public_api_sdk import PublicApiClient, PublicApiClientConfiguration, ApiKeyAuthConfig

# Initialize the client
client = PublicApiClient(
    ApiKeyAuthConfig(api_secret_key="INSERT_API_SECRET_KEY"),
    config=PublicApiClientConfiguration(
        default_account_number="INSERT_ACCOUNT_NUMBER"
    )
)

# Get accounts
accounts = client.get_accounts()

# Get a quote
from public_api_sdk import OrderInstrument, InstrumentType

quotes = client.get_quotes([
    OrderInstrument(symbol="AAPL", type=InstrumentType.EQUITY)
])

Async Quick Start

The SDK ships an AsyncPublicApiClient for use in async/await code. It uses httpx as the HTTP transport, so no background threads are needed.

import asyncio
from public_api_sdk import (
    AsyncPublicApiClient,
    AsyncPublicApiClientConfiguration,
    ApiKeyAuthConfig,
    OrderInstrument,
    InstrumentType,
)

async def main():
    async with AsyncPublicApiClient(
        auth_config=ApiKeyAuthConfig(api_secret_key="INSERT_API_SECRET_KEY"),
        config=AsyncPublicApiClientConfiguration(
            default_account_number="INSERT_ACCOUNT_NUMBER"
        ),
    ) as client:
        accounts = await client.get_accounts()

        quotes = await client.get_quotes([
            OrderInstrument(symbol="AAPL", type=InstrumentType.EQUITY)
        ])
        for q in quotes:
            print(f"{q.instrument.symbol}: ${q.last}")

asyncio.run(main())

The async with block automatically cancels any active price subscriptions and closes the HTTP connection when the block exits — no try/finally or manual close() call required.

API Reference

Client Configuration

The PublicApiClient is initialized with an API secret key create in your settings page at public.com and optional configuration. The SDK client will handle generation and refresh of access tokens:

from public_api_sdk import PublicApiClient, PublicApiClientConfiguration
from public_api_sdk.auth_config import ApiKeyAuthConfig

config = PublicApiClientConfiguration(
    default_account_number="INSERT_ACCOUNT_NUMBER",  # Optional default account
)

client = PublicApiClient(
        ApiKeyAuthConfig(api_secret_key="INSERT_API_SECRET_KEY"),
        config=config
    )

Access Token Validity

ApiKeyAuthConfig accepts an optional validity_minutes argument (default: 15) that controls how long each minted access token stays valid on the server. Valid range is 5 to 1440 minutes (24 hours); values outside this range raise ValueError.

# Long-lived session for a batch job — refresh every ~1 hour
client = PublicApiClient(
    ApiKeyAuthConfig(
        api_secret_key="INSERT_API_SECRET_KEY",
        validity_minutes=60,
    ),
    config=config,
)

The SDK automatically refreshes the token ahead of its expiry (5 minutes of slack), so you don't need to manage refresh yourself — longer validity_minutes just means fewer token-mint round trips.

Default Account Number

The default_account_number configuration option simplifies API calls by eliminating the need to specify account_id in every method call. When set, any method that accepts an optional account_id parameter will automatically use the default account number if no account ID is explicitly provided.

# With default_account_number configured
from public_api_sdk import OrderInstrument, InstrumentType

config = PublicApiClientConfiguration(
    default_account_number="INSERT_ACCOUNT_NUMBER"
)

client = PublicApiClient(
        ApiKeyAuthConfig(api_secret_key="INSERT_API_SECRET_KEY"), 
        config=config
    )

instruments = [
    OrderInstrument(symbol="AAPL", type=InstrumentType.EQUITY),
    OrderInstrument(symbol="MSFT", type=InstrumentType.EQUITY)
]

# No need to specify account_id
portfolio = client.get_portfolio()  # Uses default account number
quotes = client.get_quotes(instruments)   # Uses default account number

# You can still override with a specific account
other_portfolio = client.get_portfolio(account_id="DIFFERENT123")  # Uses "DIFFERENT123"
# Without default_account_number
config = PublicApiClientConfiguration()

client = PublicApiClient(
        ApiKeyAuthConfig(api_secret_key="INSERT_API_SECRET_KEY"), 
        config=config
    )

# Must specify account_id for each call
portfolio = client.get_portfolio(account_id="INSERT_ACCOUNT_NUMBER")  # Required
quotes = client.get_quotes(instruments, account_id="INSERT_ACCOUNT_NUMBER")  # Required

This is particularly useful when working with a single account, as it reduces code repetition and makes the API calls cleaner.

Account Management

Get Accounts

Retrieve all accounts associated with the authenticated user.

accounts_response = client.get_accounts()
for account in accounts_response.accounts:
    print(f"Account ID: {account.account_id}, Type: {account.account_type}")

Get Portfolio

Get a snapshot of account portfolio including positions, equity, buying power, open orders, and multi-leg option strategies.

portfolio = client.get_portfolio(account_id="YOUR_ACCOUNT_NUMBER")  # account_id optional if default set
print(f"Total equity: {portfolio.equity}")
print(f"Buying power: {portfolio.buying_power}")

# Positions include the strategy IDs they belong to (empty list if not part of any strategy)
for position in portfolio.positions:
    if position.strategy_ids:
        print(f"{position.instrument.symbol} is part of strategies: {position.strategy_ids}")

# Multi-leg option strategies (e.g. spreads). Null if the backend does not support strategies.
if portfolio.strategies:
    for strategy in portfolio.strategies:
        print(f"\n{strategy.display_name} (id={strategy.strategy_id})")
        print(f"  Quantity: {strategy.quantity}, current value: ${strategy.current_value}")
        for leg in strategy.option_legs:
            print(f"  {leg.position_type} {leg.ratio_quantity}x {leg.symbol}")

Get Account History

Retrieve paginated account history with optional filtering.

from public_api_sdk import HistoryRequest

history = client.get_history(
    HistoryRequest(page_size=10),
    account_id="YOUR_ACCOUNT"
)

Market Data

Get Quotes

Retrieve real-time quotes for multiple instruments. Each Quote includes the last/bid/ask, volume, open interest, previous close, a one-day change breakdown, and option-specific details (strike, mid price, and greeks) when the instrument is an option.

from public_api_sdk import OrderInstrument, InstrumentType

quotes = client.get_quotes([
    OrderInstrument(symbol="AAPL", type=InstrumentType.EQUITY),
    OrderInstrument(symbol="GOOGL", type=InstrumentType.EQUITY)
])

for quote in quotes:
    print(f"{quote.instrument.symbol}: ${quote.last} (prev close ${quote.previous_close})")
    if quote.one_day_change:
        print(f"  1d change: ${quote.one_day_change.change} ({quote.one_day_change.percent_change}%)")

For option quotes (either via get_quotes on an OPTION instrument, or the calls/puts inside get_option_chain), quote.option_details exposes greeks and mid price:

if quote.option_details:
    print(f"Strike: ${quote.option_details.strike_price}, mid: ${quote.option_details.mid_price}")
    if quote.option_details.greeks:
        g = quote.option_details.greeks
        print(f"  Δ={g.delta} Γ={g.gamma} Θ={g.theta} ν={g.vega} IV={g.implied_volatility}")

All fields on GreekValues are optional — the API may omit greeks for illiquid or expired contracts. Always guard with if quote.option_details.greeks: before reading individual values.

Get Instrument Details

Get detailed information about a specific instrument, including trading permissions, short-selling availability, option price increments, and type-specific details (bond or crypto).

from public_api_sdk import (
    BondInstrumentDetails,
    CryptoInstrumentDetails,
    ShortingAvailability,
)

instrument = client.get_instrument(
    symbol="AAPL",
    instrument_type=InstrumentType.EQUITY
)

print(f"Symbol: {instrument.instrument.symbol}")
print(f"Type: {instrument.instrument.type}")
print(f"Trading: {instrument.trading}")
print(f"Fractional Trading: {instrument.fractional_trading}")
print(f"Option Trading: {instrument.option_trading}")
print(f"Option Spread Trading: {instrument.option_spread_trading}")

# Short-selling — only populated for shortable equities
if instrument.shorting_availability:
    print(f"Shorting: {instrument.shorting_availability.value}")
    if instrument.shorting_availability == ShortingAvailability.HARD_TO_BORROW:
        print(f"  HTB rate: {instrument.hard_to_borrow_percentage_rate}%")

# Option price increments — present for optionable equities
if instrument.option_contract_price_increments:
    inc = instrument.option_contract_price_increments
    print(f"Option increments: below $3 = {inc.increment_below_3}, above $3 = {inc.increment_above_3}")

# Type-specific details (polymorphic on payload_type)
details = instrument.instrument_details
if isinstance(details, CryptoInstrumentDetails):
    print(f"Crypto precision: qty={details.crypto_quantity_precision}, price={details.crypto_price_precision}")
    print(f"Tradable in NY: {details.tradable_in_new_york}")
elif isinstance(details, BondInstrumentDetails):
    print(f"Bond outstanding: {details.has_outstanding}")

Get All Instruments

Retrieve all available trading instruments with optional filtering.

from public_api_sdk import InstrumentsRequest, InstrumentType, TradingPermission

instruments = client.get_all_instruments(
    InstrumentsRequest(
        type_filter=[InstrumentType.EQUITY],
        trading_filter=[TradingPermission.BUY_AND_SELL],
    )
)

Options Trading

Get Historic Bar Data

Fetch OHLCV bar data for any symbol over a standard time period. The response is split into pre-market, regular-market, and after-hours sessions, each containing a list of Bar objects.

from public_api_sdk import BarPeriod

bars = client.get_bars("AAPL", BarPeriod.YEAR)

print(f"Total expected bars: {bars.total_expected_bars}")
for bar in bars.regular_market.bars:
    print(f"  {bar.timestamp}  O={bar.open}  H={bar.high}  L={bar.low}  C={bar.close}  V={bar.volume}")

Available periods: DAY, WEEK, MONTH, QUARTER, HALF_YEAR, YEAR, FIVE_YEARS, YTD, SINCE_PURCHASE.

Aggregation override

Override the bar size by passing an aggregation:

from public_api_sdk import BarAggregation

# Today's intraday bars at 5-minute resolution
bars = client.get_bars("AAPL", BarPeriod.DAY, aggregation=BarAggregation.FIVE_MINUTES)

# Past week at 30-minute resolution
bars = client.get_bars("AAPL", BarPeriod.WEEK, aggregation=BarAggregation.THIRTY_MINUTES)

Available aggregations: ONE_MINUTE, FIVE_MINUTES, TEN_MINUTES, FIFTEEN_MINUTES, THIRTY_MINUTES, ONE_HOUR, ONE_DAY, ONE_WEEK, ONE_MONTH, THREE_MONTHS, SIX_MONTHS, ONE_YEAR.

Instrument type

get_bars defaults to InstrumentType.EQUITY. Pass instrument_type to request bars for crypto, options, or indices:

from public_api_sdk import BarAggregation, BarPeriod, InstrumentType

# Bitcoin year-to-date, hourly bars
bars = client.get_bars(
    "BTC",
    BarPeriod.YTD,
    instrument_type=InstrumentType.CRYPTO,
    aggregation=BarAggregation.ONE_HOUR,
)

# Index bars
bars = client.get_bars("SPX", BarPeriod.YEAR, instrument_type=InstrumentType.INDEX)

Supported values: EQUITY, CRYPTO, OPTION, INDEX. Any other InstrumentType raises ValueError.

Last regular trading session close

bars.last_regular_trading_session_close is a LastSessionClose model (or None) carrying the prior session's close price and change:

last = bars.last_regular_trading_session_close
if last is not None:
    print(f"Prior close: ${last.close} on {last.close_date}  ({last.percent_change}%)")
Performance since purchase

Use BarPeriod.SINCE_PURCHASE with a purchase_date to chart performance from a specific entry point:

bars = client.get_bars(
    "AAPL",
    BarPeriod.SINCE_PURCHASE,
    purchase_date="2024-01-02",  # YYYY-MM-DD
)

if bars.total_gain_loss is not None:
    print(f"Gain/loss since purchase: ${bars.total_gain_loss} ({bars.total_gain_loss_percentage}%)")

Get Option Expirations

Retrieve available option expiration dates for an underlying instrument.

```python from public_api_sdk import OptionExpirationsRequest, OrderInstrument, InstrumentType

expirations = client.get_option_expirations( OptionExpirationsRequest( instrument=OrderInstrument( symbol="AAPL", type=InstrumentType.EQUITY ) ) ) print(f"Available expirations: {expirations.expiratio

Core symbols most depended-on inside this repo

get
called by 101
src/public_api_sdk/api_client.py
subscribe
called by 51
src/public_api_sdk/price_stream.py
refresh_token_if_needed
called by 36
src/public_api_sdk/auth_manager.py
_handle_response
called by 36
src/public_api_sdk/api_client.py
get_bars
called by 30
src/public_api_sdk/public_api_client.py
unsubscribe
called by 29
src/public_api_sdk/price_stream.py
post
called by 29
src/public_api_sdk/api_client.py
stop
called by 26
src/public_api_sdk/subscription_manager.py

Shape

Method 1,191
Class 274
Function 74
Route 8

Languages

Python100%

Modules by API surface

tests/test_strategy_preflight.py149 symbols
tests/test_public_api_client.py126 symbols
tests/test_async_public_api_client.py96 symbols
tests/test_response_models.py76 symbols
tests/test_async_api_client.py64 symbols
tests/test_order_preflight_validation.py59 symbols
tests/test_production_readiness.py56 symbols
src/public_api_sdk/models/order.py55 symbols
tests/test_api_client.py51 symbols
tests/test_async_subscription.py45 symbols
tests/test_async_auth_providers.py42 symbols
tests/test_multileg_order_validation.py41 symbols

For agents

$ claude mcp add publicdotcom-py \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact