Skip to content

Jayson-Fong/hood

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

13 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

🪶 hood



Purpose

This package seeks to provide a client for the official Robinhood Crypto API for viewing Robinhood crypto account and market details alongside performing trading activities. It does not incorporate endpoints to interact with Robinhood's undocumented brokerage API.

While Robinhood itself provides a sample client, it does not provide any type hinting for its API's possible return types, types for order configuration, or methods to handle pagination.

The development of this package is not affiliated with Robinhood.

Installation

The latest development release is installable from GitHub:

python3 -m pip install git+https://github.com/Jayson-Fong/hood

Usage

Authentication

Usage of the Crypto Trading API requires registering an Ed25519 key with your Robinhood account to acquire an API key. API keys are used in conjunction with the Ed25519 key to securely sign your requests to Robinhood and include a timestamp as part of the signed message to mitigate the risk of replay attacks.

Note

Signed requests to Robinhood are generally valid for up to 30 seconds following generation. While this package sends requests immediately following signature generation, poor network performance or time synchronization issues may lead to issues authenticating with Robinhood.

Caution

While Robinhood will generally reject signed messages 30 seconds after timestamp generation, this may allow for replay attacks. To mitigate this risk, only perform trading activities on networks you trust and do not share your API key or private key.

You can generate a private key using the following:

import base64
from hood.crypto_trading import auth

private_key = auth.Credential.generate()

private_key_base64 = base64.b64encode(private_key.encode()).decode()
public_key_base64 = base64.b64encode(private_key.verify_key.encode()).decode()

print("Private Key (Base64):", private_key_base64, sep="\n\t")
print("Public Key (Base64):", public_key_base64, sep="\n\t")

This will output in the following format:

Private Key (Base64):
        AmkImtzGG3lW5BKXhqvJTxvFfL3gFqrRsOI9U/6d6CA=
Public Key (Base64):
        XnMFEPYylcEYm64Z3S7B8JfrexlxHzP1p+eD/mJ4gSI=

Do not share your private key or provide it to Robinhood. If you intend to use hood on multiple devices, it is recommended that you generate a unique key pair for each device.

You will need to provide your public key to Robinhood to generate an API key and associate it with your account. To locate the form:

  1. Navigate to account settings
  2. Open the Crypto settings tab
  3. Under API Trading, click Add key

Note

To ensure your account's security, consider restricting permissions on a least-privilege basis. By granting API access, you should assume that your crypto account ID will always be accessible.

Keep your generated private key and the API key in a secure location as they are required for accessing the Crypto Trading API.

API Client

Requests go through a client instance. The built-in client is hood.crypto_trading:CryptoTradingClient. It can be instantiated as follows given a private key.

from hood.crypto_trading import CryptoTradingClient, auth

credential = auth.Credential("API_KEY_HERE", b"PRIVATE_KEY_HERE")
client = CryptoTradingClient(credential)

A hood.crypto_trading.auth:Credential instance can be instantiated using an API key and a private key. The private key may either be an instance of nacl.signing:SigningKey (from PyNaCl) or bytes. If you have your private key in the form of Base64, it can be converted into bytes:

import base64

private_key_seed = base64.b64decode("BASE64_PRIVATE_KEY_HERE")

When using hood"s dedicated methods to make requests against Robinhood"s Crypto Trading API, the function caller will receive an instance of hood.structures.APIResponse, containing the following attributes:

  • data (None or Dataclass Instance): Parsed response data.
  • response (None or requests.Response): Response from the Crypto Trading API.
  • error (BaseException): Exception if one was raised while processing the request.

Account Endpoints

Get Crypto Trading Account Details

Get the Robinhood Crypto Trading account details associated with the authenticated user.

from hood.crypto_trading import CryptoTradingClient, auth

credential = auth.Credential("API_KEY_HERE", b"PRIVATE_KEY_HERE")
client = CryptoTradingClient(credential)
client.accounts()

Expected output structure for response code 200:

import decimal
import requests
import hood.crypto_trading.structures
import hood.crypto_trading.schema.account

hood.crypto_trading.structures.APIResponse(
    data=hood.crypto_trading.schema.account.TradingAccountDetail(
        account_number="000000000000",
        status="active",
        buying_power=decimal.Decimal("000.0000"),
        buying_power_currency="USD",
    ),
    response=requests.Response(),
    error=None
)

Market Data Endpoints

Get Best Price

Fetch a single bid and ask price for each symbol specified. Multiple symbols may be specified as additional arguments.

from hood.crypto_trading import CryptoTradingClient, auth

credential = auth.Credential("API_KEY_HERE", b"PRIVATE_KEY_HERE")
client = CryptoTradingClient(credential)
client.best_bid_ask("BTC-USD", "ETH-USD")

Expected output structure for response code 200:

import decimal
import requests
import hood.crypto_trading.structures
import hood.crypto_trading.schema.market

hood.crypto_trading.structures.APIResponse(
    data=hood.crypto_trading.schema.market.BestBidAskResults(
        results=[
            hood.crypto_trading.schema.market.BestBidAsk(
                symbol="BTC-USD",
                price=decimal.Decimal("94200.49475077"),
                bid_inclusive_of_sell_spread=decimal.Decimal("93401.43950154"),
                sell_spread=decimal.Decimal("0.0084825"),
                ask_inclusive_of_buy_spread=decimal.Decimal("94999.55"),
                buy_spread=decimal.Decimal("0.0084825"),
                timestamp="2026-01-05T21:04:16.35583523Z",
            ),
            hood.crypto_trading.schema.market.BestBidAsk(
                symbol="ETH-USD",
                price=decimal.Decimal("3247.68922388"),
                bid_inclusive_of_sell_spread=decimal.Decimal("3220.746636"),
                sell_spread=decimal.Decimal("0.00829593"),
                ask_inclusive_of_buy_spread=decimal.Decimal("3274.63181176"),
                buy_spread=decimal.Decimal("0.00829593"),
                timestamp="2026-01-05T21:04:16.355835525Z",
            ),
        ],
    ),
    response=requests.Response(),
    error=None,
)

This endpoint may return an error response. For example, when providing an invalid symbol.

from hood.crypto_trading import CryptoTradingClient, auth

credential = auth.Credential("API_KEY_HERE", b"PRIVATE_KEY_HERE")
client = CryptoTradingClient(credential)
client.best_bid_ask("BTC-USD", "ETH-US")

Expected output structure for response code 400:

import requests
import hood.crypto_trading.structures
import hood.crypto_trading.schema.market

hood.crypto_trading.structures.APIResponse(
    data=hood.crypto_trading.schema.market.Errors(
    type="validation_error",
    errors=[
        hood.crypto_trading.schema.market.Error(
            detail="Invalid symbol: ETH-US",
            attr="symbol",
      ),
    ],
  ),
  response=requests.Response(), 
  error=None,
)
Get Estimated Price

Requests the estimated price for a symbol. A single symbol is required, to be followed by the side. Valid sides are: bid, ask, both. Quantities may be specified as additional arguments. The Robinhood Crypto Trading API accepts up to 10 quantities. The quantity may be passed as a float, string, integer, or decimal.Decimal. Quantities are converted to a string using str(quantity).

from hood.crypto_trading import CryptoTradingClient, auth

credential = auth.Credential("API_KEY_HERE", b"PRIVATE_KEY_HERE")
client = CryptoTradingClient(credential)
client.estimated_price("BTC-USD", "both", 0.1, "1")

Expected output structure for response code 200:

import decimal
import requests
import hood.crypto_trading.structures
import hood.crypto_trading.schema.market

hood.crypto_trading.structures.APIResponse(
    data=hood.crypto_trading.schema.market.MarketEstimateResults(
        results=[
            hood.crypto_trading.schema.market.MarketEstimate(
                symbol="BTC-USD",
                side="bid",
                price=decimal.Decimal("94110.38097125"),
                quantity=decimal.Decimal("0.1"),
                bid_inclusive_of_sell_spread=decimal.Decimal("93311.1019425"),
                sell_spread=decimal.Decimal("0.008493"),
                ask_inclusive_of_buy_spread=None,
                buy_spread=None,
                timestamp="2026-01-05T16:08:10.270830931-05:00",
            ),
            hood.crypto_trading.schema.market.MarketEstimate(
                symbol="BTC-USD",
                side="ask",
                price=decimal.Decimal("94110.38097125"),
                quantity=decimal.Decimal("0.1"),
                bid_inclusive_of_sell_spread=None,
                sell_spread=None,
                ask_inclusive_of_buy_spread=decimal.Decimal("94909.66"),
                buy_spread=decimal.Decimal("0.008493"),
                timestamp="2026-01-05T16:08:10.270830931-05:00",
            ),
            hood.crypto_trading.schema.market.MarketEstimate(
                symbol="BTC-USD",
                side="bid",
                price=decimal.Decimal("94109.17804723"),
                quantity=decimal.Decimal("1"),
                bid_inclusive_of_sell_spread=decimal.Decimal("93307.82609446"),
                sell_spread=decimal.Decimal("0.00851513"),
                ask_inclusive_of_buy_spread=None,
                buy_spread=None,
                timestamp="2026-01-05T16:08:10.270830931-05:00",
            ),
            hood.crypto_trading.schema.market.MarketEstimate(
                symbol="BTC-USD",
                side="ask",
                price=decimal.Decimal("94109.17804723"),
                quantity=decimal.Decimal("1"),
                bid_inclusive_of_sell_spread=None,
                sell_spread=None,
                ask_inclusive_of_buy_spread=decimal.Decimal("94910.53"),
                buy_spread=decimal.Decimal("0.00851513"),
                timestamp="2026-01-05T16:08:10.270830931-05:00",
            ),
        ]
    ),
    response=requests.Response(),
    error=None,
)

This endpoint may return an error response. For example, when providing an invalid symbol.

from hood.crypto_trading import CryptoTradingClient, auth

credential = auth.Credential("API_KEY_HERE", b"PRIVATE_KEY_HERE")
client = CryptoTradingClient(credential)
client.estimated_price("BTC-USD", "both", 0.1, "1")

Expected output structure for response code 400:

import requests
import hood.crypto_trading.structures
import hood.crypto_trading.schema.market

hood.crypto_trading.structures.APIResponse(
    data=hood.crypto_trading.schema.market.Errors(
        type="server_error",
        errors=[
            hood.crypto_trading.schema.market.Error(
                detail="Internal server error", 
                attr=None,
            ),
        ],
    ),
    response=requests.Response(),
    error=None,
)

Trading Endpoints

Get Crypto Trading Pairs

Fetch a list of trading pairs. Multiple symbols may be specified as additional arguments. You may pass an integer limit to limit the number of results in one page. If iterating through pages, a string cursor may be passed as a keyword argument. If no symbols are specified, all symbols will be returned.

from hood.crypto_trading import CryptoTradingClient, auth

credential = auth.Credential("API_KEY_HERE", b"PRIVATE_KEY_HERE")
client = CryptoTradingClient(credential)
client.trading_pairs("BTC-USD", "ETH-USD")

Expected output structure for response code 200:

import requests
import hood.crypto_trading.structures
import hood.crypto_trading.schema.trading

hood.crypto_trading.structures.APIResponse(
    data=hood.crypto_trading.schema.trading.TradingPairResults(
        next=None,
        previous=None,
        results=[
            hood.crypto_trading.schema.trading.TradingPair(
                asset_code="ETH", 
                quote_code="USD", 
                quote_increment="0.010000000000000000", 
                asset_increment="0.000001000000000000", 
                max_order_size="280.0000000000000000", 
                min_order_size="0.000100000000000000", 
                status="tradable", 
                symbol="ETH-USD",
            ),
            hood.crypto_trading.schema.trading.TradingPair(
              asset_code="BTC",
              quote_code="USD",
              quote_increment="0.010000000000000000",
              asset_increment="0.000000010000000000",
              max_order_size="20.0000000000000000",
              min_order_size="0.000001000000000000",
              status="tradable",
              symbol="BTC-USD",
            ),
        ],
    ),
    response=requests.Response(),
    error=None,
)

If paging, a next and previous URL may be provided in the form of a string.

This endpoint may return an error response. For example, when providing an invalid symbol.

from hood.crypto_trading import CryptoTradingClient, auth

credential = auth.Credential("API_KEY_HERE", b"PRIVATE_KEY_HERE")
client = CryptoTradingClient(credential)
client.trading_pairs("BTC-USD", "ETH-US")

Expected output structure for response code 400 and 404:

import requests
import hood.crypto_trading.structures
import hood.crypto_trading.schema.trading

hood.crypto_trading.structures.APIResponse(
    data=hood.crypto_trading.schema.trading.Errors(
        type="validation_error",
        errors=[
            hood.crypto_trading.schema.trading.Error(
                detail="Select a valid choice.",
                attr='symbol',
            ),
        ],
    ),
    response=requests.Response(),
    error=None,
)
Get Crypto Holdings

Details the holdings in your Crypto Trading account. Omit asset codes to list all holdings. Multiple asset codes may be passed as positional arguments, all uppercase. An integer limit keyword argument may be set to limit the number of results per page. If paging, the cursor may be passed in a cursor keyword argument.

from hood.crypto_trading import CryptoTradingClient, auth

credential = auth.Credential("API_KEY_HERE", b"PRIVATE_KEY_HERE")
client = CryptoTradingClient(credential)
# noinspection SpellCheckingInspection
client.holdings("USDC")

Expected output structure for response code 200:

import decimal
import requests
import hood.crypto_trading.structures
import hood.crypto_trading.schema.trading

# noinspection SpellCheckingInspection
hood.crypto_trading.structures.APIResponse(
    data=hood.crypto_trading.schema.trading.HoldingResults(
        next=None,
        previous=None,
        results=[
            hood.crypto_trading.schema.trading.Holding(
                account_number="000000000000",
                asset_code="USDC", 
                total_quantity=decimal.Decimal("1.000000000000000000"),
                quantity_available_for_trading=decimal.Decimal("1.000000000000000000"),
            ),
        ],
    ), 
  response=requests.Response(),
  error=None,
)

If paging, a next and previous URL may be provided in the form of a string.

This endpoint may return an error response. For example, when providing an invalid cursor.

from hood.crypto_trading import CryptoTradingClient, auth

credential = auth.Credential("API_KEY_HERE", b"PRIVATE_KEY_HERE")
client = CryptoTradingClient(credential)
client.holdings(cursor="0")

Expected output structure for response code 400 and 404:

import requests
import hood.crypto_trading.structures
import hood.crypto_trading.schema.trading

hood.crypto_trading.structures.APIResponse(
    data=hood.crypto_trading.schema.trading.Errors(
        type="client_error", 
        errors=[
            hood.crypto_trading.schema.trading.Error(
                detail="Not found.",
                attr=None
            ),
        ],
    ),
    response=requests.Response(), 
    error=None,
)
Get Crypto Orders

Multiple types of orders are supported with different parameters. The client supports the following order types, specified through the type parameter, along with their required parameters:

  • limit
    • quote_amount (float)
    • asset_quantity (float)
    • limit_price (float)
  • market
    • asset_quantity (float)
  • stop_limit
    • quote_amount (float)
    • asset_quantity (float)
    • limit_price (float)
    • stop_price (float)
    • time_in_force (str: gtc, gfd, gfw, gfm)
  • stop_loss
    • quote_amount (float)
    • asset_quantity (float)
    • limit_price (float)
    • stop_price (float)
    • time_in_force (str: gtc, gfd, gfw, gfm)

All order types require a currency pair symbol such as BTC-USD, accepted as a positional parameter. Only a single currency pair symbol is supported.

For order types that accept both a quote_amount and a asset_quantity, you may only specify either quote_amount or asset_quantity - not both simultaneously. All methods accept a side, which must be either buy or sell, and you may optionally provide a client_order_id, a UUID for client reference. If a client_order_id is not specified, one will be automatically generated.

If you intend on modifying the order using the library, it is suggested that you provide a client_order_id so that it is immediately consumable, such as for order cancellation. Otherwise, the automatically generated value is only available through the response.

from hood.crypto_trading import CryptoTradingClient, auth

credential = auth.Credential("API_KEY_HERE", b"PRIVATE_KEY_HERE")
client = CryptoTradingClient(credential)
client.orders(limit=1)

Expected output structure for response code 200:

import decimal
import requests
import hood.crypto_trading.structures
import hood.crypto_trading.schema.trading

hood.crypto_trading.structures.APIResponse(
    data=hood.crypto_trading.schema.trading.OrderResults(
        next="https://trading.robinhood.com/api/v1/crypto/trading/orders/?cursor=00000000000000000000000000000000000000000000000000000000&limit=1",
        previous=None,
        results=[
            hood.crypto_trading.schema.trading.Order(
                id="faceaa50-0756-4a5e-b4e4-34e7b550ecec",
                account_number="000000000000",
                symbol="BTC-USD",
                client_order_id="440f4b24-4bd8-4c80-97e2-003886963970",
                side="buy",
                executions=[],
                type="limit",
                state="canceled",
                average_price=None,
                filled_asset_quantity=decimal.Decimal("0E-18"),
                created_at="2026-01-10T21:03:29.896079-05:00",
                updated_at="2026-01-10T21:03:48.484410-05:00",
                market_order_config=None,
                limit_order_config=hood.crypto_trading.schema.trading.LimitOrderConfig(
                    quote_amount=None,
                    asset_quantity=decimal.Decimal("0.000100000000000000"),
                    limit_price=decimal.Decimal("90000.000000000000000000"),
                ),
                stop_loss_order_config=None,
                stop_limit_order_config=None,
            ),
        ],
    ),
    response=requests.Response(),
    error=None,
)

If paging, a next and previous URL may be provided in the form of a string.

This endpoint may return an error response. For example, when providing an invalid cursor.

from hood.crypto_trading import CryptoTradingClient, auth

credential = auth.Credential("API_KEY_HERE", b"PRIVATE_KEY_HERE")
client = CryptoTradingClient(credential)
client.orders(limit=1)

Expected output structure for response code 400 and 404:

import requests
import hood.crypto_trading.structures
import hood.crypto_trading.schema.trading

hood.crypto_trading.structures.APIResponse(
    data=hood.crypto_trading.schema.trading.Errors(
        type="client_error",
        errors=[
            hood.crypto_trading.schema.trading.Error(
                detail="Not found.",
                attr=None,
            ),
        ],
    ),
    response=requests.Response(),
    error=None,
)
Place New Crypto Order
from hood.crypto_trading import CryptoTradingClient, auth

credential = auth.Credential("API_KEY_HERE", b"PRIVATE_KEY_HERE")
client = CryptoTradingClient(credential)
client.order("USDC-USD", side="buy", type="market", asset_quantity=0.01)

Expected output structure for response code 200:

import decimal
import requests
import hood.crypto_trading.structures
import hood.crypto_trading.schema.trading

hood.crypto_trading.structures.APIResponse(
    data=hood.crypto_trading.schema.trading.Order(
        id="296217b3-febe-4f27-a6d4-7ee569564d4d",
        account_number="000000000000",
        symbol="USDC-USD",
        client_order_id="28055f9f-252e-4012-9e34-af93e1c00703",
        side="buy",
        executions=[],
        type="market",
        state="open",
        average_price=None,
        filled_asset_quantity=decimal.Decimal("0E-18"),
        created_at="2026-01-10T20:50:13.931262-05:00",
        updated_at="2026-01-10T20:50:14.362428-05:00",
        market_order_config=hood.crypto_trading.schema.trading.MarketOrderConfig(
            asset_quantity=decimal.Decimal("0.010000000000000000"),
        ),
        limit_order_config=None,
        stop_loss_order_config=None,
        stop_limit_order_config=None,
    ),
    response=requests.Response(),
    error=None,
)

This endpoint may return an error response. For example, when the asset quantity is outside the supported range.

from hood.crypto_trading import CryptoTradingClient, auth

credential = auth.Credential("API_KEY_HERE", b"PRIVATE_KEY_HERE")
client = CryptoTradingClient(credential)
client.order("USDC-USD", side="buy", type="market", asset_quantity=1000000)

Expected output structure for response code 400:

import requests
import hood.crypto_trading.structures
import hood.crypto_trading.schema.trading

hood.crypto_trading.structures.APIResponse(
    data=hood.crypto_trading.schema.trading.Errors(
        type="validation_error",
        errors=[
            hood.crypto_trading.schema.trading.Error(
                detail="must be less than or equal to 250000.0000000000000000",
                attr="market_order_config.asset_quantity",
            ),
        ],
    ),
    response=requests.Response(),
    error=None,
)
Cancel Open Crypto Order
from hood.crypto_trading import CryptoTradingClient, auth

credential = auth.Credential("API_KEY_HERE", b"PRIVATE_KEY_HERE")
client = CryptoTradingClient(credential)
client.cancel("cc9c89d6-dea4-4dab-8354-0994e1cd080a")

Expected output structure for response code 200:

import requests
import hood.crypto_trading.structures
import hood.crypto_trading.schema.trading

hood.crypto_trading.structures.APIResponse(
    data=hood.crypto_trading.schema.trading.Message(
        body='"Cancel request has been submitted for order cc9c89d6-dea4-4dab-8354-0994e1cd080a"',
    ),
    response=requests.Response(),
    error=None,
)

This endpoint may return an error response. For example, when it is not possible to cancel an order.

from hood.crypto_trading import CryptoTradingClient, auth

credential = auth.Credential("API_KEY_HERE", b"PRIVATE_KEY_HERE")
client = CryptoTradingClient(credential)
client.cancel("cc9c89d6-deaa-4dab-8354-0994e1cd080a")

Expected output structure for response code 400 and 404:

import requests
import hood.crypto_trading.structures
import hood.crypto_trading.schema.trading

hood.crypto_trading.structures.APIResponse(
    data=hood.crypto_trading.schema.trading.Errors(
        type="validation_error",
        errors=[
            hood.crypto_trading.schema.trading.Error(
                detail="This order cannot be canceled.",
                attr="non_field_errors",
            ),
        ],
    ),
    response=requests.Response(),
    error=None,
)

Disclaimers

The authors do not provide any guarantees pertaining to this software's fitness for use. You are responsible for evaluating the software and ensuring its proper functionality prior to use. For more information, please reference the license.

You could lose money using this software. While the author seeks to ensure its proper function, the author does not provide any guarantee that the software will function as expected.

Information contained within this document and its associated software do not provide legal, financial, or accounting advice.

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages