HOGSWAP SDK (Python)
The official Python SDK for the HOGSWAP v1 router API — one call to quote the best route across every integrated Algorand DEX (STAMM, Tinyman, Pact, Humble, AlgoFi, Folks, liquid-staking mints), one call to get unsigned transactions your user's wallet signs. The API never touches keys and never broadcasts — your app stays in full control.
- Zero dependencies. Pure standard library (
urllib), Python 3.9+. - Sync and async.
HogswapClientandAsyncHogswapClientexpose the identical surface; the async client runs requests in worker threads viaasyncio.to_thread— no extra deps, real concurrency. - Honest numbers.
expected_outandmin_out_at_slippageare what the contract actually delivers — routing fee included. Attach the user's wallet address and their HOG-holdings fee discount is priced in exactly. - Everything is atomic. A swap either delivers at least the quoted minimum or the whole transaction group reverts. No partial fills, no stuck funds.
- Typed. Ships
py.typedwith full type hints.
Base URL: https://hogswap-v1.liquihog.dev
60-second start
from hogswap import HogswapClient, from_base_units
hogswap = HogswapClient()
# How much USDC for 10 ALGO? (amounts are integer base units: 1 ALGO = 1_000_000)
quote = hogswap.swap_quote(
asset_in=0, # ALGO
asset_out=31566704, # USDC
amount_in=10_000_000,
slippage_bps=50, # 0.5% tolerance
)
print("expected:", from_base_units(quote["expected_out"], 6), "USDC")
print("worst case:", from_base_units(quote["min_out_at_slippage"], 6), "USDC")
print("route:", " + ".join(leg["dex_name"] for leg in quote["legs"]))
Async, same surface:
import asyncio
from hogswap import AsyncHogswapClient
async def main():
hogswap = AsyncHogswapClient()
quote = await hogswap.swap_quote(asset_in=0, asset_out=31566704,
amount_in=10_000_000)
print(quote["expected_out"])
asyncio.run(main())
The full trade loop
import base64
from algosdk import encoding # signing is YOUR side; any wallet works
from hogswap import HogswapClient
hogswap = HogswapClient(sender=user_address) # fee discount priced in
# 1. Quote
quote = hogswap.swap_quote(asset_in=0, asset_out=31566704, amount_in=10_000_000)
# 2. Build unsigned transactions (call this right before signing)
built = hogswap.execute(quote_id=quote["quote_id"], user_address=user_address)
# 3. Sign with the user's wallet / key (py-algorand-sdk shown)
signed = [encoding.msgpack_decode(b64).sign(private_key) for b64 in built["txns_b64"]]
# 4. Submit the group to any algod node
txid = algod.send_transactions(signed)
Or, for a key you manage yourself, the optional helper (the ONLY
key-touching code in the SDK, and it needs an explicit
pip install py-algorand-sdk):
from hogswap import account_signer
sign = account_signer(private_key) # -> sign(txns_b64, purpose)
signed_b64 = sign(built["txns_b64"], "swap")
Install
pip install hogswap-py-sdk
The import name is hogswap. Add local-signing support (optional) with
pip install hogswap-py-sdk[signing].
What you can quote
| Method | What it does |
|---|---|
swap_quote |
best route for amount_in of A → B |
exact_out_quote |
minimum input that guarantees an exact output |
multi_input_quote |
2-4 assets → one output, single atomic group (dust consolidation) |
basket_quote |
one input → primary + up to 3 exact-amount side outputs |
lp_mint_quote |
add liquidity to a STAMM pool tier — with pool assets or any asset (auto-converted) |
lp_redeem_quote |
burn LP tokens into any asset |
execute |
quote → unsigned transaction group |
swap |
quote + execute in one call |
Market data (all edge-cached ~5s): assets, asset, asset_pools, pools,
pool, stamm_pools, lp, pair, price, prices,
price_anchors, staking_assets, tvl, assets_tvl, health.
Routing fee, surfaced (1.3.0): every SWAP quote carries
router_fee_bps_nominal / _effective, router_fee_amount (already
deducted from expected_out — display it, don't subtract it),
router_fee_amount_undiscounted and router_fee_asset (always the
output asset). Pass sender= and you also get hog_holdings_micro
and hog_discount_pct; "you saved" is
router_fee_amount_undiscounted - router_fee_amount. The fee is 5 bps
of notional output, reduced proportionally by HOG held, fully waived
at 100 HOG.
Exact-out bound (1.3.0): exact-out responses add
max_in_at_slippage, the worst-case spend — in that mode min-out is
pinned to amount_out, so the input is the bound that matters.
MissingOptInError (1.3.0): 422s where the signer lacks an OUTPUT
asset opt-in now raise this instead of a bare ValidationError (which
it subclasses, so existing handlers still work). Ids are on
.assets; every error also gains .code, the API's stable
machine code, alongside the human .detail.
LP positions (1.2.0): lp(asset_id, amount=None) values a
liquidity-provider token — issuing pool and STAMM tier, what one WHOLE
token is backed by, USD value, and the redeemable amount of each
underlying for a holding (amount in LP base units). Find ids on
pools()[...]["lp_asset_id"] or each
stamm_pools()[...]["tier_breakdown"][...]["lp_asset_id"]. Values are
a proportional-share redemption at analytics prices — no slippage, no
exit fee, null rather than guessed when a price or supply is missing.
Batch lookups: assets(ids=[...]), prices([...]), assets_tvl([...]) —
up to 100 asset ids per call.
Bounded-complexity routes (1.1.0): pass max_legs (1-16) to
swap_quote to cap the TOTAL leg count, parallel pool splits
included — max_hops bounds depth only. Built for callers that
replay the session under their own resource budget (contract vaults,
composed groups): you get the best route that fits, at a slightly
worse price, instead of a rejection.
Watches: server-side arming over SSE (1.1.0)
Stop polling for prices: register a standing condition once and get pushed an event the block it arms. Free with any self-issued API key.
client = HogswapClient(api_key="hsk_...")
# "Tell me when swapping 100 ALGO would deliver >= 8.65 USDC."
client.put_watch(
client_key="my-bot:algo-usdc", kind="target",
asset_in=0, asset_out=31566704,
amount_in=100_000_000, min_out=8_650_000,
)
for ev in client.watch_events(): # blocking generator
if ev["type"] == "fired":
... # numbers only — re-quote NOW, then execute
# async client: same surface, `async for` over watch_events()
Edge-triggered with per-watch re-arm hysteresis and cooldown; TTL
auto-expiry; at-least-once delivery with seq-numbered replay. Events
are hints — fills stay gated by your quote's on-chain floor. Details:
docs/API.md.
Live block stream — the one endpoint without a wrapper method: it's
plain Server-Sent Events at GET /stream/blocks; use any SSE client.
Full request/response reference: docs/API.md.
Amounts, fees, and floors — the three things to know
- Everything is integer base units. 1 ALGO = 1,000,000 µALGO; each
ASA's
decimalscomes fromassets(). Useto_base_units("1.5", 6)/from_base_units(1500000, 6)to convert safely (exactdecimal.Decimalmath, no float drift). - Quotes are delivery-exact. The router's fee (5 bps, discounted by the
sender's HOG holdings, free at 100+ HOG) is already subtracted from
expected_outandmin_out_at_slippage. Don't subtract anything client-side. min_out_at_slippageis enforced on-chain. If market movement makes delivery fall below it, the group reverts atomically and the user only spends network fees.
x402: paid tier + pay any invoice with any asset
The SDK speaks x402 end to
end, strictly non-custodially — you provide sign and submit
callables; the SDK never sees keys.
from hogswap import HogswapClient, PaymentRequiredError
hogswap = HogswapClient(api_key="hsk_...")
# Self-issue a key (no signup, no human): sign a challenge locally.
ch = hogswap.register(address=address)
res = hogswap.register_verify(
address=address, challenge=ch["challenge"],
signature_b64=my_sign_bytes(ch["challenge"]), # e.g. algosdk.util.sign_bytes
)
hogswap.set_api_key(res["api_key"])
# Out of credits? Any call raises PaymentRequiredError whose `.offer`
# IS the x402 payment instruction. Top up with ANY 1-4 assets you
# hold (here: minimum ALGO, solved by the exact-out router):
try:
hogswap.swap_quote(...)
except PaymentRequiredError:
hogswap.topup_with_assets(
usdc_micro=1_000_000, user_address=address,
inputs=[{"asset_id": 0}], # pay 1 USDC worth of ALGO
sign=lambda txns_b64, purpose: wallet.sign_group(txns_b64),
submit=lambda signed, purpose: algod.send_transactions(signed),
) # returns when credits land
# retry the original call
# Or pay ANY third-party Algorand x402 invoice the same way:
hogswap.pay_invoice(invoice=offer["accepts"][0], user_address=address,
inputs=[{"asset_id": 0}], sign=sign, submit=submit)
Groups are submitted in order (swap first — its on-chain floor
guarantees the payment is funded). On the async client, sign and
submit may be coroutine functions. AI agents get the same tools over
MCP: hogswap-mcp; JavaScript
apps get hogswap-js-sdk.
Being a good API citizen (limits)
| Limit | Value | On violation |
|---|---|---|
| Quote rate | 30 requests / 10s / IP | HTTP 429 for ~10s |
| Concurrency | 4 in-flight requests / IP | immediate HTTP 429 |
| Execute budget | 5 builds per quote_id |
HTTP 429 — get a fresh quote |
| Quote lifetime | ~30 seconds | HTTP 404 on execute — get a fresh quote |
The SDK raises typed errors (RateLimitError, QuoteExpiredError,
ExecuteBudgetError, …) so handling these is an isinstance check.
Identical anonymous quotes within 5s are served from cache — repeats are
nearly free, so don't build your own quote cache.
Tips: debounce type-to-quote inputs (~300ms); request a quote when the
user is ready to see a price, execute right before signing; never hardcode
pool/app ids — everything you need is in the API responses. (The router's
on-chain app id may change between releases; transactions returned by
execute always target the current one.)
FAQ
Do I need an API key? No. Public, rate-limited per IP.
Which wallets work? Any Algorand wallet that signs standard transaction groups (Pera, Defly, Exodus, KMD, …). The API returns plain unsigned transactions; nothing wallet-specific.
Is my seed phrase ever involved? Never. The API builds unsigned
transactions; signing happens entirely in your app/wallet, and you submit
to algod yourself. The SDK core takes no keys anywhere; the optional
account_signer helper is the single, explicit opt-in for local keys.
Sync or async? Same surface, your call. AsyncHogswapClient wraps the
sync core with asyncio.to_thread, so there's exactly one implementation
to trust.
License
MIT — see LICENSE.
Metadata
Release files for hogswap-py-sdk 1.3.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| hogswap_py_sdk-1.3.0.tar.gz | 37.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| hogswap_py_sdk-1.3.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 66.7 kB
Release files / hogswap_py_sdk-1.3.0.tar.gz
| Download URL | hogswap_py_sdk-1.3.0.tar.gz |
|---|---|
| Size | 37.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
6b1d783c8c097cc16968e3aee6afff77dedc5b26644126577b25297faba2f09b
|
|
BLAKE2b-256 checksum How to use checksums |
2b021c542982162ab4fe438d34976358b4a1de3f7de50a5401bea7f02bb5c32f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.5
|
Release files / hogswap_py_sdk-1.3.0-py3-none-any.whl
| Download URL | hogswap_py_sdk-1.3.0-py3-none-any.whl |
|---|---|
| Size | 29.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
9e87db5ab5f317b968558bed2d9a499c7fd5a6c4a3bc6c75dd4e8ae81c053883
|
|
BLAKE2b-256 checksum How to use checksums |
48695387382347cdb2033ef46415039b00865795d137df627d83a51023ca48d3
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.5
|