Agara
SDKs

Python

Install the Python client, place an order in dollars and shares, and choose between the sync, async and streaming clients.

agara-sdk is the quickest route from nothing to a working trading script. It takes dollars and shares, converts them to the wire format for you, and maps HTTP failures onto a small exception hierarchy. It needs Python 3.10 or newer.

The library concentrates on trading: the orderbook, orders, positions, portfolio and activity. It does not wrap market or event discovery, so find your token_id through Markets or the public API first.

Quickstart

Install the SDK

Not on PyPI yet, so install straight from the public repository. Both extras are optional: async adds an async-native client, streaming adds the WebSocket client.

pip install "agara-sdk[async,streaming] @ \
  git+https://github.com/Mirai-Tech-Inc/agara-sdks@main#subdirectory=python"

Once the package is published that becomes pip install 'agara-sdk[async,streaming]', and nothing else on this page changes.

Create a client

Create a token first, following Authentication:

export AGARA_TOKEN="agt_..."

The two clients mirror each other method for method, with the same arguments, return shapes and exceptions:

import os
from agara_sdk import AgaraClient

client = AgaraClient(
    token=os.environ["AGARA_TOKEN"],
    base_url=os.environ["AGARA_BASE_URL"],
)

Both implement the context-manager protocol, so with (or async with) closes the underlying connection cleanly.

Read a market

with AgaraClient(token=os.environ["AGARA_TOKEN"]) as client:
    book = client.get_orderbook(token_id)
    print(f"best bid {book.best_bid} / ask {book.best_ask}, mid {book.mid}")

Orderbook carries best_bid, best_ask, mid and spread so you do not have to index into the raw levels.

Place an order and wait for the outcome

accepted = client.place_order(
    token_id=token_id,
    side="BUY",
    price=0.60,   # dollars per share
    shares=1.0,   # shares
)

final = client.wait_for_terminal(accepted["order_id"], timeout=30.0)
print(final["status"])

Acceptance is not settlement. wait_for_terminal polls until the order reaches a terminal status, and that status still needs inspecting: a rejected or partially filled order is a normal terminal outcome.

Amounts

This is the biggest difference from the other two libraries. You write price=0.60 and the client sends 600000, then converts amounts back on the way in. micro_to_float is available when you hold a raw value from elsewhere:

from agara_sdk import micro_to_float

micro_to_float(600000)  # 0.6

Convenience has a cost: a float cannot represent every decimal exactly. For accounting, or anywhere a rounded cent matters, prefer exact strings or integer micro units and treat the float conversion as a display step. See Conventions.

Pagination

List methods take limit and cursor, and hand back the cursor for the next page. Loop until it comes back empty:

cursor = None
while True:
    page = client.list_trades(limit=500, cursor=cursor)
    for trade in page["trades"]:
        print(trade["fill_id"], trade["price_micro"])

    cursor = page["pagination"]["next_cursor"]
    if not cursor:
        break

Keep that cursor to resume a scan later rather than replaying from the start. list_orders, list_open_orders, list_positions and list_activities page the same way.

Error handling

Every failure inherits from AgaraError, with a subclass per HTTP status, so you can catch broadly or narrowly:

from agara_sdk import AgaraError, RateLimitedError, RejectedError

try:
    client.place_order(token_id=token_id, side="BUY", price=0.60, shares=1.0)
except RejectedError as error:      # 422, the order was refused on its merits
    print("rejected:", error)
except RateLimitedError as error:   # 429, back off before retrying
    print("slow down:", error)
except AgaraError:                  # anything else the server reported
    raise

BadRequestError (400), AuthError (401), ForbiddenError (403), NotFoundError (404), ConflictError (409), RejectedError (422), RateLimitedError (429) and ServerError (5xx) are the full set.

REST calls are never retried for you. A ServerError surfaces so you can choose your own backoff, and a request that fails in transit may still have taken effect, so read the state back before sending it again. Errors lists the problem codes and which are worth another attempt.

Realtime subscriptions

The streaming extra adds an async WebSocket client with typed frames, callback or iterator dispatch, and automatic reconnection. One client covers both the public market channels and your private account events:

import asyncio, os
from agara_sdk import streaming

async def main():
    client = streaming.AgaraStreamClient(token=os.environ["AGARA_TOKEN"])

    @client.on_trade
    async def _(trade: streaming.Trade):
        print(f"trade {trade.side} {trade.size} @ {trade.price}")

    @client.on_fill
    async def _(fill: streaming.Fill):
        print(f"my fill {fill.fill_id}")

    await client.subscribe([
        streaming.trades(condition_id),
        streaming.account_events(),
    ])
    await client.run()

asyncio.run(main())

Reconnection restores your subscriptions but does not replay what you missed while disconnected. Treat a reconnect as a prompt to take a fresh snapshot. Streaming covers the channels and Account stream the private one.

Next steps