HTTP Client

A naive, unopinionated wrapper around the TD Ameritrade HTTP API. This client provides access to all endpoints of the API in as easy and direct a way as possible. For example, here is how you can fetch the past 20 years of data for Apple stock:

Do not attempt to use more than one Client object per token file, as this will likely cause issues with the underlying OAuth2 session management

from tda.auth import easy_client
from tda.client import Client

c = easy_client(
        api_key='APIKEY',
        redirect_uri='https://localhost',
        token_path='/tmp/token.json')

resp = c.get_price_history('AAPL',
        period_type=Client.PriceHistory.PeriodType.YEAR,
        period=Client.PriceHistory.Period.TWENTY_YEARS,
        frequency_type=Client.PriceHistory.FrequencyType.DAILY,
        frequency=Client.PriceHistory.Frequency.DAILY)
assert resp.status_code == httpx.codes.OK
history = resp.json()

Note we we create a new client using the auth package as described in Authentication and Client Creation. Creating a client directly is possible, but not recommended.

Asyncio Support

An asynchronous variant is available through a keyword to the client constructor. This allows for higher-performance API usage, at the cost of slightly increased application complexity.

from tda.auth import easy_client
from tda.client import Client

async def main():
    c = easy_client(
            api_key='APIKEY',
            redirect_uri='https://localhost',
            token_path='/tmp/token.json',
            asyncio=True)

    resp = await c.get_price_history('AAPL',
            period_type=Client.PriceHistory.PeriodType.YEAR,
            period=Client.PriceHistory.Period.TWENTY_YEARS,
            frequency_type=Client.PriceHistory.FrequencyType.DAILY,
            frequency=Client.PriceHistory.Frequency.DAILY)
    assert resp.status_code == httpx.codes.OK
    history = resp.json()

if __name__ == '__main__':
    import asyncio
    asyncio.run_until_complete(main())

For more examples, please see the examples/async directory in GitHub.

Calling Conventions

Function parameters are categorized as either required or optional. Required parameters, such as 'AAPL' in the example above, are passed as positional arguments. Optional parameters, like period_type and the rest, are passed as keyword arguments.

Parameters which have special values recognized by the API are represented by Python enums. This is because the API rejects requests which pass unrecognized values, and this enum wrapping is provided as a convenient mechanism to avoid consternation caused by accidentally passing an unrecognized value.

By default, passing values other than the required enums will raise a ValueError. If you believe the API accepts a value that isn’t supported here, you can use set_enforce_enums to disable this behavior at your own risk. If you do find a supported value that isn’t listed here, please open an issue describing it or submit a PR adding the new functionality.

Return Values

All methods return a response object generated under the hood by the HTTPX module. For a full listing of what’s possible, read that module’s documentation. Most if not all users can simply use the following pattern:

r = client.some_endpoint()
assert r.status_code == httpx.codes.OK, r.raise_for_status()
data = r.json()

The API indicates errors using the response status code, and this pattern will raise the appropriate exception if the response is not a success. The data can be fetched by calling the .json() method.

This data will be pure python data structures which can be directly accessed. You can also use your favorite data analysis library’s dataframe format using the appropriate library. For instance you can create a pandas dataframe using its conversion method.

Note: Because the author has no relationship whatsoever with TD Ameritrade, this document makes no effort to describe the structure of the returned JSON objects. TDA might change them at any time, at which point this document will become silently out of date. Instead, each of the methods described below contains a link to the official documentation. For endpoints that return meaningful JSON objects, it includes a JSON schema which describes the return value. Please use that documentation or your own experimentation when figuring out how to use the data returned by this API.

Creating a New Client

99.9% of users should not create their own clients, and should instead follow the instructions outlined in Authentication and Client Creation.

For users who want to disable the strict enum type checking on http client, just pass enforce_enums=False in any of the client creation functions described in Authentication and Client Creation. Just note that for most users, it is advised they stick with the default behavior.

For those brave enough to build their own, the constructor looks like this:

Client.__init__(api_key, session, *, enforce_enums=True, token_metadata=None)

Create a new client with the given API key and session. Set enforce_enums=False to disable strict input type checking.

Timeout Management

Timeouts for HTTP calls are managed under the hood by the httpx library. tda-api defaults to 30 seconds, which experience has shown should be more than enough to allow even the slowest API calls to complete. A different timeout specification can be set using this method:

Client.set_timeout(timeout)

Sets the timeout configuration for this client. Applies to all HTTP calls.

Parameters:

timeouthttpx timeout configuration. Passed directly to underlying httpx library. See here for examples.

Orders

Placing New Orders

Placing new orders can be a complicated task. The Client.place_order() method is used to create all orders, from equities to options. The precise order type is defined by a complex order spec. TDA provides some example order specs to illustrate the process and provides a schema in the place order documentation, but beyond that we’re on our own.

tda-api includes some helpers, described in Order Templates, which provide an incomplete utility for creating various order types. While it only scratches the surface of what’s possible, we encourage you to use that module instead of creating your own order specs.

Client.place_order(account_id, order_spec)

Place an order for a specific account. If order creation was successful, the response will contain the ID of the generated order. See tda.utils.Utils.extract_order_id() for more details. Note unlike most methods in this library, responses for successful calls to this method typically do not contain json() data, and attempting to extract it will likely result in an exception.

Official documentation.

Accessing Existing Orders

Client.get_orders_by_path(account_id, *, max_results=None, from_entered_datetime=None, to_entered_datetime=None, status=None, statuses=None)

Orders for a specific account. At most one of status and statuses may be set. Official documentation.

Parameters:
  • max_results – The maximum number of orders to retrieve.

  • from_entered_datetime – Specifies that no orders entered before this time should be returned. Date must be within 60 days from today’s date. toEnteredTime must also be set.

  • to_entered_datetime – Specifies that no orders entered after this time should be returned. fromEnteredTime must also be set.

  • status – Restrict query to orders with this status. See Order.Status for options.

  • statuses – Restrict query to orders with any of these statuses. See Order.Status for options.

Client.get_orders_by_query(*, max_results=None, from_entered_datetime=None, to_entered_datetime=None, status=None, statuses=None)

Orders for all linked accounts. At most one of status and statuses may be set. Official documentation.

Parameters:
  • max_results – The maximum number of orders to retrieve.

  • from_entered_datetime – Specifies that no orders entered before this time should be returned. Date must be within 60 days from today’s date. toEnteredTime must also be set.

  • to_entered_datetime – Specifies that no orders entered after this time should be returned. fromEnteredTime must also be set.

  • status – Restrict query to orders with this status. See Order.Status for options.

  • statuses – Restrict query to orders with any of these statuses. See Order.Status for options.

Client.get_order(order_id, account_id)

Get a specific order for a specific account by its order ID. Official documentation.

class tda.client.Client.Order
class Status(value)

Order statuses passed to get_orders_by_path() and get_orders_by_query()

AWAITING_PARENT_ORDER = 'AWAITING_PARENT_ORDER'
AWAITING_CONDITION = 'AWAITING_CONDITION'
AWAITING_MANUAL_REVIEW = 'AWAITING_MANUAL_REVIEW'
ACCEPTED = 'ACCEPTED'
AWAITING_UR_OUT = 'AWAITING_UR_OUT'
PENDING_ACTIVATION = 'PENDING_ACTIVATION'
QUEUED = 'QUEUED'
WORKING = 'WORKING'
REJECTED = 'REJECTED'
PENDING_CANCEL = 'PENDING_CANCEL'
CANCELED = 'CANCELED'
PENDING_REPLACE = 'PENDING_REPLACE'
REPLACED = 'REPLACED'
FILLED = 'FILLED'
EXPIRED = 'EXPIRED'

Editing Existing Orders

Endpoints for canceling and replacing existing orders. Annoyingly, while these endpoints require an order ID, it seems that when placing new orders the API does not return any metadata about the new order. As a result, if you want to cancel or replace an order after you’ve created it, you must search for it using the methods described in Accessing Existing Orders.

Client.cancel_order(order_id, account_id)

Cancel a specific order for a specific account. Official documentation.

Client.replace_order(account_id, order_id, order_spec)

Replace an existing order for an account. The existing order will be replaced by the new order. Once replaced, the old order will be canceled and a new order will be created. Official documentation.

Account Info

These methods provide access to useful information about accounts. An incomplete list of the most interesting bits:

  • Account balances, including available trading balance

  • Positions

  • Order history

See the official documentation for each method for a complete response schema.

Client.get_account(account_id, *, fields=None)

Account balances, positions, and orders for a specific account. Official documentation.

Parameters:

fields – Balances displayed by default, additional fields can be added here by adding values from Account.Fields.

Client.get_accounts(*, fields=None)

Account balances, positions, and orders for all linked accounts. Official documentation.

Parameters:

fields – Balances displayed by default, additional fields can be added here by adding values from Account.Fields.

class tda.client.Client.Account
class Fields(value)

Account fields passed to get_account() and get_accounts()

POSITIONS = 'positions'
ORDERS = 'orders'

Instrument Info

Note: symbol fundamentals (P/E ratios, number of shares outstanding, dividend yield, etc.) is available using the Instrument.Projection.FUNDAMENTAL projection.

Client.search_instruments(symbols, projection)

Search or retrieve instrument data, including fundamental data. Official documentation.

Parameters:

projection – Query type. See Instrument.Projection for options.

Client.get_instrument(cusip)

Get an instrument by CUSIP. Official documentation.

class tda.client.Client.Instrument
class Projection(value)

Search query type for search_instruments(). See the official documentation for details on the semantics of each.

SYMBOL_REGEX = 'symbol-regex'
DESC_REGEX = 'desc-regex'
FUNDAMENTAL = 'fundamental'

Option Chain

Unfortunately, option chains are well beyond the ability of your humble author. You are encouraged to read the official API documentation to learn more.

If you are knowledgeable enough to write something more substantive here, please follow the instructions in Contributing to tda-api to send in a patch.

Client.get_option_chain(symbol, *, contract_type=None, strike_count=None, include_quotes=None, strategy=None, interval=None, strike=None, strike_range=None, from_date=None, to_date=None, volatility=None, underlying_price=None, interest_rate=None, days_to_expiration=None, exp_month=None, option_type=None)

Get option chain for an optionable Symbol. Official documentation.

Parameters:
  • contract_type – Type of contracts to return in the chain. See Options.ContractType for choices.

  • strike_count – The number of strikes to return above and below the at-the-money price.

  • include_quotes – Include quotes for options in the option chain?

  • strategy – If passed, returns a Strategy Chain. See Options.Strategy for choices.

  • interval – Strike interval for spread strategy chains (see strategy param).

  • strike – Return options only at this strike price.

  • strike_range – Return options for the given range. See Options.StrikeRange for choices.

  • from_date – Only return expirations after this date. For strategies, expiration refers to the nearest term expiration in the strategy. Accepts datetime.date and datetime.datetime.

  • to_date – Only return expirations before this date. For strategies, expiration refers to the nearest term expiration in the strategy. Accepts datetime.date and datetime.datetime.

  • volatility – Volatility to use in calculations. Applies only to ANALYTICAL strategy chains.

  • underlying_price – Underlying price to use in calculations. Applies only to ANALYTICAL strategy chains.

  • interest_rate – Interest rate to use in calculations. Applies only to ANALYTICAL strategy chains.

  • days_to_expiration – Days to expiration to use in calculations. Applies only to ANALYTICAL strategy chains

  • exp_month – Return only options expiring in the specified month. See Options.ExpirationMonth for choices.

  • option_type – Types of options to return. See Options.Type for choices.

class tda.client.Client.Options
class ContractType(value)

An enumeration.

CALL = 'CALL'
PUT = 'PUT'
ALL = 'ALL'
class ExpirationMonth(value)

An enumeration.

JANUARY = 'JAN'
FEBRUARY = 'FEB'
MARCH = 'MAR'
APRIL = 'APR'
MAY = 'MAY'
JUNE = 'JUN'
JULY = 'JUL'
AUGUST = 'AUG'
SEPTEMBER = 'SEP'
OCTOBER = 'OCT'
NOVEMBER = 'NOV'
DECEMBER = 'DEC'
class Strategy(value)

An enumeration.

SINGLE = 'SINGLE'
ANALYTICAL = 'ANALYTICAL'
COVERED = 'COVERED'
VERTICAL = 'VERTICAL'
CALENDAR = 'CALENDAR'
STRANGLE = 'STRANGLE'
STRADDLE = 'STRADDLE'
BUTTERFLY = 'BUTTERFLY'
CONDOR = 'CONDOR'
DIAGONAL = 'DIAGONAL'
COLLAR = 'COLLAR'
ROLL = 'ROLL'
class StrikeRange(value)

An enumeration.

IN_THE_MONEY = 'ITM'
NEAR_THE_MONEY = 'NTM'
OUT_OF_THE_MONEY = 'OTM'
STRIKES_ABOVE_MARKET = 'SAK'
STRIKES_BELOW_MARKET = 'SBK'
STRIKES_NEAR_MARKET = 'SNK'
ALL = 'ALL'
class Type(value)

An enumeration.

STANDARD = 'S'
NON_STANDARD = 'NS'
ALL = 'ALL'

Price History

TDA provides price history for equities and ETFs. It does not provide price history for options, futures, or any other instruments.

In the raw API, fetching price history is somewhat complicated: the API offers a single endpoint Client.get_price_history() that accepts a complex variety of inputs, but fails to document them in any meaningful way.

Thankfully, we’ve reverse engineered this endpoint and built some helpful utilities for fetching prices by minute, day, week, etc. Each method can be called with or without date bounds. When called without date bounds, it returns all data available. Each method offers a different lookback period, so make sure to read the documentation below to learn how much data is available.

Client.get_price_history_every_minute(symbol, *, start_datetime=None, end_datetime=None, need_extended_hours_data=None)

Fetch price history for a stock or ETF symbol at a per-minute granularity. This endpoint currently appears to return up to 48 days of data.

Client.get_price_history_every_five_minutes(symbol, *, start_datetime=None, end_datetime=None, need_extended_hours_data=None)

Fetch price history for a stock or ETF symbol at a per-five-minutes granularity. This endpoint currently appears to return approximately nine months of data.

Client.get_price_history_every_ten_minutes(symbol, *, start_datetime=None, end_datetime=None, need_extended_hours_data=None)

Fetch price history for a stock or ETF symbol at a per-ten-minutes granularity. This endpoint currently appears to return approximately nine months of data.

Client.get_price_history_every_fifteen_minutes(symbol, *, start_datetime=None, end_datetime=None, need_extended_hours_data=None)

Fetch price history for a stock or ETF symbol at a per-fifteen-minutes granularity. This endpoint currently appears to return approximately nine months of data.

Client.get_price_history_every_thirty_minutes(symbol, *, start_datetime=None, end_datetime=None, need_extended_hours_data=None)

Fetch price history for a stock or ETF symbol at a per-thirty-minutes granularity. This endpoint currently appears to return approximately nine months of data.

Client.get_price_history_every_day(symbol, *, start_datetime=None, end_datetime=None, need_extended_hours_data=None)

Fetch price history for a stock or ETF symbol at a daily granularity. The exact period of time over which this endpoint returns data is unclear, although it has been observed returning data as far back as 1985 (for AAPL).

Client.get_price_history_every_week(symbol, *, start_datetime=None, end_datetime=None, need_extended_hours_data=None)

Fetch price history for a stock or ETF symbol at a weekly granularity. The exact period of time over which this endpoint returns data is unclear, although it has been observed returning data as far back as 1985 (for AAPL).

For the sake of completeness, here is the documentation for the raw price history endpoint, in all its complexity.

Client.get_price_history(symbol, *, period_type=None, period=None, frequency_type=None, frequency=None, start_datetime=None, end_datetime=None, need_extended_hours_data=None)

Get price history for a symbol. Official documentation.

Parameters:
  • period_type – The type of period to show.

  • period – The number of periods to show. Should not be provided if start_datetime and end_datetime.

  • frequency_type – The type of frequency with which a new candle is formed.

  • frequency – The number of the frequencyType to be included in each candle.

  • start_datetime – Start date.

  • end_datetime – End date. Default is previous trading day.

  • need_extended_hours_data – If true, return extended hours data. Default is true.

class tda.client.Client.PriceHistory
class Frequency(value)

An enumeration.

EVERY_MINUTE = 1
EVERY_FIVE_MINUTES = 5
EVERY_TEN_MINUTES = 10
EVERY_FIFTEEN_MINUTES = 15
EVERY_THIRTY_MINUTES = 30
DAILY = 1
WEEKLY = 1
MONTHLY = 1
class FrequencyType(value)

An enumeration.

MINUTE = 'minute'
DAILY = 'daily'
WEEKLY = 'weekly'
MONTHLY = 'monthly'
class Period(value)

An enumeration.

ONE_DAY = 1
TWO_DAYS = 2
THREE_DAYS = 3
FOUR_DAYS = 4
FIVE_DAYS = 5
TEN_DAYS = 10
ONE_MONTH = 1
TWO_MONTHS = 2
THREE_MONTHS = 3
SIX_MONTHS = 6
ONE_YEAR = 1
TWO_YEARS = 2
THREE_YEARS = 3
FIVE_YEARS = 5
TEN_YEARS = 10
FIFTEEN_YEARS = 15
TWENTY_YEARS = 20
YEAR_TO_DATE = 1
class PeriodType(value)

An enumeration.

DAY = 'day'
MONTH = 'month'
YEAR = 'year'
YEAR_TO_DATE = 'ytd'

Current Quotes

Client.get_quote(symbol)

Get quote for a symbol. Note due to limitations in URL encoding, this method is not recommended for instruments with symbols symbols containing non-alphanumeric characters, for example as futures like /ES. To get quotes for those symbols, use Client.get_quotes().

Official documentation.

Client.get_quotes(symbols)

Get quote for a symbol. This method supports all symbols, including those containing non-alphanumeric characters like /ES. Official documentation.

Other Endpoints

Note If your account limited to delayed quotes, these quotes will also be delayed.

Transaction History

Client.get_transaction(account_id, transaction_id)

Transaction for a specific account. Official documentation.

Client.get_transactions(account_id, *, transaction_type=None, symbol=None, start_date=None, end_date=None)

Transaction for a specific account. Official documentation.

Parameters:
  • transaction_type – Only transactions with the specified type will be returned.

  • symbol – Only transactions with the specified symbol will be returned.

  • start_date – Only transactions after this date will be returned. Note the maximum date range is one year. Accepts datetime.date and datetime.datetime.

  • end_date – Only transactions before this date will be returned Note the maximum date range is one year. Accepts datetime.date and datetime.datetime.

class tda.client.Client.Transactions
class TransactionType(value)

An enumeration.

ALL = 'ALL'
TRADE = 'TRADE'
BUY_ONLY = 'BUY_ONLY'
SELL_ONLY = 'SELL_ONLY'
CASH_IN_OR_CASH_OUT = 'CASH_IN_OR_CASH_OUT'
CHECKING = 'CHECKING'
DIVIDEND = 'DIVIDEND'
INTEREST = 'INTEREST'
OTHER = 'OTHER'
ADVISORY_FEES = 'ADVISORY_FEES'

Saved Orders

Client.create_saved_order(account_id, order_spec)

Save an order for a specific account. Official documentation.

Client.delete_saved_order(account_id, order_id)

Delete a specific saved order for a specific account. Official documentation.

Client.get_saved_order(account_id, order_id)

Specific saved order by its ID, for a specific account. Official documentation.

Client.get_saved_orders_by_path(account_id)

Saved orders for a specific account. Official documentation.

Client.replace_saved_order(account_id, order_id, order_spec)

Replace an existing saved order for an account. The existing saved order will be replaced by the new order. Official documentation.

Market Hours

Client.get_hours_for_multiple_markets(markets, date)

Retrieve market hours for specified markets. Official documentation.

Parameters:
  • markets – Market to return hours for. Iterable of Markets.

  • date – The date for which market hours information is requested. Accepts datetime.date and datetime.datetime.

Client.get_hours_for_single_market(market, date)

Retrieve market hours for specified single market. Official documentation.

Parameters:
  • markets – Market to return hours for. Instance of Markets.

  • date – The date for which market hours information is requested. Accepts datetime.date and datetime.datetime.

class tda.client.Client.Markets(value)

Values for get_hours_for_multiple_markets() and get_hours_for_single_market().

EQUITY = 'EQUITY'
OPTION = 'OPTION'
FUTURE = 'FUTURE'
BOND = 'BOND'
FOREX = 'FOREX'

Movers

Client.get_movers(index, direction, change)

Top 10 (up or down) movers by value or percent for a particular market. Official documentation.

Parameters:
class tda.client.Client.Movers
class Change(value)

Values for get_movers()

VALUE = 'value'
PERCENT = 'percent'
class Direction(value)

Values for get_movers()

UP = 'up'
DOWN = 'down'

User Info and Preferences

Client.get_preferences(account_id)

Preferences for a specific account. Official documentation.

Client.get_user_principals(fields=None)

User Principal details. Official documentation.

Client.update_preferences(account_id, preferences)

Update preferences for a specific account.

Please note that the directOptionsRouting and directEquityRouting values cannot be modified via this operation. Official documentation.

class tda.client.Client.UserPrincipals
class Fields(value)

An enumeration.

STREAMER_SUBSCRIPTION_KEYS = 'streamerSubscriptionKeys'
STREAMER_CONNECTION_INFO = 'streamerConnectionInfo'
PREFERENCES = 'preferences'
SURROGATE_IDS = 'surrogateIds'

Watchlists

Note: These methods only support static watchlists, i.e. they cannot access dynamic watchlists.

Client.create_watchlist(account_id, watchlist_spec)

‘Create watchlist for specific account.This method does not verify that the symbol or asset type are valid. Official documentation.

Client.delete_watchlist(account_id, watchlist_id)

Delete watchlist for a specific account. Official documentation.

Client.get_watchlist(account_id, watchlist_id)

Specific watchlist for a specific account. Official documentation.

Client.get_watchlists_for_multiple_accounts()

All watchlists for all of the user’s linked accounts. Official documentation.

Client.get_watchlists_for_single_account(account_id)

All watchlists of an account. Official documentation.

Client.replace_watchlist(account_id, watchlist_id, watchlist_spec)

Replace watchlist for a specific account. This method does not verify that the symbol or asset type are valid. Official documentation.

Client.update_watchlist(account_id, watchlist_id, watchlist_spec)

Partially update watchlist for a specific account: change watchlist name, add to the beginning/end of a watchlist, update or delete items in a watchlist. This method does not verify that the symbol or asset type are valid. Official documentation.