DevelopersAXIS API
REST API
The AXIS API is a hosted HTTP service that serves quotes, depth, candles, tickers, orders and trade history for AXIS.
Base URLs#
| Network | REST | WebSocket |
|---|---|---|
| Testnet | https://demo-api.axis.markets |
wss://demo-api.axis.markets/ws |
| Mainnet | Not available yet | Not available yet |
Every endpoint is a GET request relative to the base URL. The push API is described in WebSocket API.
Contract addresses and token contracts per network are in Networks and deployments.
The AXIS API and the indexer#
The AXIS API embeds the open-source indexer and adds routing and market data on top of it. The order,
account and history endpoints come from the indexer, so you can serve them yourself by running the indexer with
apiPort. Quotes, depth, candles, the 24h ticker and the WebSocket API exist only in the hosted service.
| Endpoint | AXIS API | Self-hosted indexer |
|---|---|---|
GET /quote |
Yes | No |
GET /depth |
Yes | No |
GET /candles |
Yes | No |
GET /ticker/24h |
Yes | No |
GET /contract |
Yes | Yes |
GET /markets |
Yes | Yes |
GET /order/:id |
Yes | Yes |
GET /order |
Yes | Yes |
GET /account/:address |
Yes | Yes |
GET /backing |
Yes | Yes |
GET /order-history |
Yes | Yes |
GET /trades |
Yes | Yes |
GET /failures |
Yes | Yes |
WebSocket /ws |
Yes | No |
The API is not a Horizon drop-in. It has its own paths and response shapes: /depth replaces /order_book, /quote
replaces /paths/strict-send and /paths/strict-receive, and /candles replaces /trade_aggregations. The full
mapping is in Migrating from the Classic DEX.
Conventions#
Requests and responses#
- Responses are JSON (
Content-Type: application/json). CORS is open (Access-Control-Allow-Origin: *), so a browser app can call the API directly. - Add
pretty_printto any query string (?pretty_print) to get indented JSON. - There is no authentication. The Testnet service publishes no rate limits and gives no availability guarantee. Cache what you can and retry failed requests with a backoff.
Errors#
A failed request returns an HTTP error status and a JSON body with the same status:
{
"error": "Bad request. Invalid parameter: \"amount\". Invalid asset amount: 1.5",
"status": 400
}
| Status | When |
|---|---|
| 400 | A parameter is missing or invalid. The message usually names it, Invalid parameter: "<name>"., using source_asset and destination_asset for the quote assets and owner for /account/:address. A quote with the same asset on both sides answers Invalid parameter: "buyingAsset". Buying asset can't be the same as selling asset. /candles with to not after from answers Bad request. Parameter "to" should be larger than "from". |
| 404 | The order is not live, or the backing record is not tracked |
| 409 | /quote while the service is not ready (Trading error. Server currently is not ready to quote prices.) or when routing failed (Trading error. <message>) |
| 500 | Internal error: Internal server error |
An unknown path returns 404 with the plain-text body API endpoint was not found, not JSON.
Numbers, amounts and prices#
- Integers that may not fit a JSON number are strings: order IDs, amounts, prices, balances, allowances, trade IDs and
cursors. Parse them with
BigInt(). - Amounts in orders, trades, quotes and backing records are integer base units of the token. For a 7-decimal asset 1
token is
10000000base units (stroops), so"10000000"XLM is 1 XLM. - Order
priceand quoteworstPriceare contract prices in the maker's terms: the order's buying base units per 1 selling base unit, times 10^18, as integer strings. For a quote hop,worstPriceis units of the hop'ssellingasset per 1 unit of itsbuyingasset. The"4533027867695422232"of an XLM to USDC hop means 4.533 XLM per USDC, which is also the token price when both assets have 7 decimals. See Prices and rounding. rprice(orders) andprice(trades and swaps) are floating-point approximations for display. Never compute with them.- The market data endpoints
/depth,/candlesand/ticker/24hreturn decimal strings in token units, with prices in quote asset per base asset. They convert with 7 decimals, which covers every Testnet asset.
Assets#
- Input.
/quoteand themarketparameter of/depthand/candlesacceptXLM(alsoxlmornative),CODE:ISSUER,CODE-ISSUERor a token contract address (C...). Theassetandpairfilters of the order and history endpoints accept contract addresses only. - Output. Every asset in a response is a token contract address. A Classic asset appears as the address of its Stellar Asset Contract (SAC). The Testnet addresses are listed in Networks and deployments.
- Market keys. Markets use the contract's canonical pair order
base/quote: the base asset is the first of the pair, comparing the XDR bytes of the addresses, not their strings. On Testnet EURC (CCUU...) comes before CETES (CC72...)./ticker/24h,/marketsand/contractreport pairs asbase/quotein that order, while thepairfilters accept either order./depthand/candlesanswer in the orientation you request. Take the pair order from the API or fromcanonicalPairin the JS client instead of sorting strings.
Timestamps#
| Format | Example | Used by |
|---|---|---|
YYYY-MM-DD HH:MM:SS, UTC |
"2026-10-03 13:22:22" |
Order created, updated and expires, trade, swap and failure timestamp, backing updated and skipped, market created and refreshed |
| UNIX seconds, number | 1791033489 |
timestamp of /depth and /ticker/24h, candle times, the from and to parameters of /candles |
| ISO 8601, UTC | "2026-10-03T13:02:57Z" |
ts of GET / |
| Ledger sequence, number | 5001797 |
ledger, liveUntil |
The JS client exports parseApiDate, which turns the first format into UNIX milliseconds.
Pagination#
List endpoints take a limit and an exclusive cursor. To get the next page, pass the cursor field of the last row
you received: rows up to and including it are skipped. A missing, invalid or non-positive limit falls back to the
default, and a larger one is capped at the maximum without an error.
| Endpoint | Sort order | Cursor | Default limit | Max limit |
|---|---|---|---|---|
GET /markets |
Canonical pair order | cursor field, <base>-<quote> |
20 | 1000 |
GET /order |
Oldest first, by creation | cursor field |
20 | 200 |
GET /order-history |
Newest first, by creation | cursor field |
20 | 500 |
GET /trades |
Newest first | cursor field, equal to id |
20 | 500 |
GET /failures |
Newest first | cursor field, equal to id |
20 | 500 |
An order cursor is the position of the event that created the order, not the order ID. Order IDs are hashes and cannot
be used for paging. There is no order parameter on these endpoints.
Status#
GET /#
Service status with 24-hour activity totals. No parameters.
| Field | Type | Description |
|---|---|---|
status |
string | loading while the orderbook is empty, for example during startup. active otherwise. |
ts |
string | Server time, ISO 8601 UTC |
ledger |
number | Last ledger processed. Only when active. |
frozen |
boolean | Whether the safety admin froze the contract. Only when active. |
trades |
number | Fills in the last 24 hours across all markets. Only when active. |
volumes |
array | Per token: symbol, quoteVolume (tokens traded in 24 hours, summed over every market) and volume (indicative USD value of that amount, from fixed reference prices). Only when active. |
curl "https://demo-api.axis.markets/?pretty_print"
{
"status": "active",
"ts": "2026-10-03T13:02:57Z",
"ledger": 5001797,
"frozen": false,
"trades": 7407,
"volumes": [
{
"symbol": "XLM",
"volume": "337.8988296",
"quoteVolume": "1535.9037711"
},
{
"symbol": "USDC",
"volume": "176.8586151",
"quoteVolume": "176.8586151"
}
]
}
Notes:
tradesandvolumesare display statistics for the Testnet demo. UseGET /ticker/24hfor per-market numbers.- A self-hosted indexer answers
/with a different body. See Indexer.
Quotes#
GET /quote#
Finds the best routes to sell or buy an amount and returns, for every hop, the order IDs to list in the contract call. Routes cross AXIS orders only, with up to 3 hops and at most 20 orders per route.
| Parameter | Type | Default | Description |
|---|---|---|---|
sellingAsset |
asset | required | Asset the trader pays. Aliases selling_asset, source_asset. |
buyingAsset |
asset | required | Asset the trader receives. Aliases buying_asset, destination_asset. |
amount |
integer | required | Positive integer in base units, decimals are rejected. With strict_send the amount to sell, with strict_receive the amount to buy. Aliases source_amount, destination_amount. |
direction |
string | strict_send |
strict_send (exact input) or strict_receive (exact output) |
direct |
boolean | false |
true or 1 limits routing to the direct market of the two assets (one hop) |
maxPrice |
integer | none | Requires direct. The highest maker price to cross, as a contract price (18 decimals) in the units of worstPrice: sellingAsset per 1 unit of buyingAsset. Quotes the crossing of a limit order: orders priced above it are left out, and the route may trade less than amount. Alias max_price. |
| Field | Type | Description |
|---|---|---|
id |
string | Quote ID, informational |
status |
string | success, unfeasible or rejected |
direction |
string | Direction used |
sellingAsset, buyingAsset |
string | Token contract addresses |
ledger |
number | Ledger of the orderbook the quote was computed on |
paths |
array | Up to 10 alternative routes, best first. Empty unless success. |
paths[].sold |
string | Amount the trader pays, base units |
paths[].bought |
string | Amount the trader receives, base units |
paths[].path |
array | Hops in execution order |
paths[].path[].selling |
string | Asset paid at this hop |
paths[].path[].buying |
string | Asset received at this hop |
paths[].path[].orders |
string array | Maker order IDs to list for this hop, in matching order |
paths[].path[].worstPrice |
string | Highest maker price among the listed orders (maker buying per selling, 18 decimals). A limit at this price crosses every listed order. |
error |
string | Reason, when status is not success |
Best first means the largest bought for strict_send and the smallest sold for strict_receive.
Sell 1 XLM for USDC:
curl "https://demo-api.axis.markets/quote?sellingAsset=XLM&buyingAsset=CBIELTK6YBZJU5UP2WWQEUCYKLPU6AUNZ2BQ4WWFEIE3USCIHMXQDAMA&amount=10000000&pretty_print"
The response had five routes. The first two are shown: the direct market, then a route through CETES.
{
"id": "11SeGAvxf58UkqTUmFqYVug4kFP1C5b",
"status": "success",
"direction": "strict_send",
"sellingAsset": "CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC",
"buyingAsset": "CBIELTK6YBZJU5UP2WWQEUCYKLPU6AUNZ2BQ4WWFEIE3USCIHMXQDAMA",
"ledger": 5002005,
"paths": [
{
"sold": "9999997",
"bought": "2214341",
"path": [
{
"selling": "CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC",
"buying": "CBIELTK6YBZJU5UP2WWQEUCYKLPU6AUNZ2BQ4WWFEIE3USCIHMXQDAMA",
"orders": [
"137718931818025766751180795007231811965",
"184698105807936084996517827088767859297",
"299866619951502789575135247958256408905"
],
"worstPrice": "4533027867695422232"
}
]
},
{
"sold": "10000000",
"bought": "2155581",
"path": [
{
"selling": "CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC",
"buying": "CC72F57YTPX76HAA64JQOEGHQAPSADQWSY5DWVBR66JINPFDLNCQYHIC",
"orders": [
"175245839230035109306050965350899990272",
"89852153929209626508254613737130759476",
"232378621713006620607546476115974255717"
],
"worstPrice": "306278225835597445"
},
{
"selling": "CC72F57YTPX76HAA64JQOEGHQAPSADQWSY5DWVBR66JINPFDLNCQYHIC",
"buying": "CBIELTK6YBZJU5UP2WWQEUCYKLPU6AUNZ2BQ4WWFEIE3USCIHMXQDAMA",
"orders": [
"313816668188901862683843128148848998865",
"129296712244998878869388841874484106102",
"114712043550087556363802578784900576007"
],
"worstPrice": "15385141000000000000"
}
]
}
]
}
Buy exactly 0.1 USDC with XLM on the direct market:
curl "https://demo-api.axis.markets/quote?sellingAsset=XLM&buyingAsset=CBIELTK6YBZJU5UP2WWQEUCYKLPU6AUNZ2BQ4WWFEIE3USCIHMXQDAMA&amount=1000000&direction=strict_receive&direct=true&pretty_print"
{
"id": "11SeGB56R1VGqHNnQr4AB4ruhZcTjWy",
"status": "success",
"direction": "strict_receive",
"sellingAsset": "CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC",
"buyingAsset": "CBIELTK6YBZJU5UP2WWQEUCYKLPU6AUNZ2BQ4WWFEIE3USCIHMXQDAMA",
"ledger": 5002015,
"paths": [
{
"sold": "4505409",
"bought": "1000000",
"path": [
{
"selling": "CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC",
"buying": "CBIELTK6YBZJU5UP2WWQEUCYKLPU6AUNZ2BQ4WWFEIE3USCIHMXQDAMA",
"orders": [
"137718931818025766751180795007231811965",
"184698105807936084996517827088767859297"
],
"worstPrice": "4515608426305947824"
}
]
}
]
}
An amount the book cannot fill returns HTTP 200 with unfeasible:
{
"id": "11SeGB58Ds24A7Zr7LyyZ5WvdV9qDrK",
"status": "unfeasible",
"direction": "strict_send",
"sellingAsset": "CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC",
"buyingAsset": "CBIELTK6YBZJU5UP2WWQEUCYKLPU6AUNZ2BQ4WWFEIE3USCIHMXQDAMA",
"ledger": 5002016,
"paths": [],
"error": "Not enough liquidity on the DEX."
}
Notes:
- The quote mirrors contract settlement. It admits a fill only when the maker's remaining backing covers all of it, and
skips a partially backed order otherwise (see Effective depth). It rounds
like the contract (the taker's output down, the cost up) and leaves out orders the contract would skip: makers without
backing, makers that cannot receive the asset you pay, makers whose trustline has no room for the payment (which would
fail the whole call), orders close to expiration, dust orders and makers the contract skipped recently. Multi-hop
routes, executed as a strict
swap, also leave out makers whose backing record still awaits confirmation. unfeasiblemeans no route fills the whole amount with backed liquidity within 20 orders. WithmaxPrice, it means that no backed order is priced within the limit.- With
maxPrice, the quote is the crossing of a limit order rather than a swap: one hop over the orders priced up to the limit, best first, at most 20 of them. A partial fill is a valid result, sosoldandboughtcan be well belowamount. The rest of the limit order goes on the book. For a limit sell at contract pricep, passmaxPrice = floor(10^36 / p). For a limit buy, passpitself. These are the thresholds the contract compares maker prices with. - With
strict_send,soldcan be slightly belowamount. Input worth less than one base unit of the output at the route's prices stays with the trader. Above, the direct route uses9999997of the10000000stroops. directionacceptsstrict_receiveas the only alternative. Any other value, including a typo, silently becomesstrict_send.- While the contract is frozen, every quote returns
status: "rejected", emptypathsand theerrorTrading is suspended: the AXIS contract is frozen. - A quote whose
buyingAssetrequires issuer authorization the AXIS contract does not have isrejectedtoo, with anerrorthat explains it. The makers' assets pass through the contract, so buying such an asset fails withIntermediaryCannotReceive(712) until the issuer authorizes the contract. No route buys such an asset at an intermediate hop either. - HTTP 409 means the service is not ready, for example while its orderbook is still empty after a restart, or that
routing failed. While the service is not ready, every
/quotecall returns 409, even an invalid one. Retry later. - Routes are alternatives computed independently and can share orders. Use one route per transaction.
- A quote reserves nothing. Orders can be filled, changed or removed before your transaction lands, and the contract skips the ones that are gone. Re-quote right before you ask for a signature. See Using a quote in a contract call.
Market data#
GET /depth#
Aggregated orderbook of one market around the mid price, counting only backed amounts.
| Parameter | Type | Default | Description |
|---|---|---|---|
market |
string | required | BASE/QUOTE, each side in any asset format. The book is returned in this orientation. |
depth |
number | 20 |
Band around the mid price in percent, greater than 0 and at most 100 |
step |
decimal | automatic | Price step of the levels, for example 0.001. Positive, at most 18 fractional digits. By default 5 significant digits of the mid price. |
limit |
integer | 100 |
Levels per side, 1 to 500 |
| Field | Type | Description |
|---|---|---|
ledger |
number | Ledger of the orderbook state |
timestamp |
number | Server time, UNIX seconds |
step |
string or null | Step used. null when the book is empty and no step was requested. |
bestBid, bestAsk |
string or null | Best prices before rounding to levels, 8 significant digits |
bids |
array | [price, quantity] levels, highest price first |
asks |
array | [price, quantity] levels, lowest price first |
Prices are in quote asset per base asset and quantities in base asset tokens, both decimal strings.
curl "https://demo-api.axis.markets/depth?market=XLM/CBIELTK6YBZJU5UP2WWQEUCYKLPU6AUNZ2BQ4WWFEIE3USCIHMXQDAMA&limit=3&pretty_print"
{
"ledger": 5002320,
"timestamp": 1791035191,
"step": "0.00001",
"bestBid": "0.22259541",
"bestAsk": "0.22630216",
"bids": [
[
"0.22259",
"0.3322822"
],
[
"0.22108",
"0.3362188"
],
[
"0.21885",
"0.11365"
]
],
"asks": [
[
"0.22631",
"0.5910125"
],
[
"0.22682",
"0.5362911"
],
[
"0.22689",
"0.8402956"
]
]
}
Notes:
- Levels are conservative. A bid is rounded down and an ask up to its level, so a limit order at a level price crosses
every order in that level. With
step=0.05, a bid at 0.33333 shows at 0.3 and an ask at 0.342 at 0.35. - Only backed amounts count. A maker's budget,
min(balance, allowance)in the token they sell, is shared by their orders in price order within the market. Orders the contract would skip are left out here too. Unlike a quote, depth does not check the maker's trustline room for the payment or whether the AXIS contract can hold the asset, so a quote can list fewer orders than depth shows. - AXIS does not match on-chain, so the book can be crossed. In a crossed book, levels on the wrong side of the mid price
are left out of
bidsandasks, whilebestBidandbestAskstill report the best prices.
GET /candles#
OHLCV candles of one market, built from indexed fills.
| Parameter | Type | Default | Description |
|---|---|---|---|
market |
string | required | BASE/QUOTE, each side in any asset format. Candles are returned in this orientation. |
from |
UNIX seconds | 0 |
Window start. Always set it. |
to |
UNIX seconds | from plus 200 candles |
Window end, exclusive. Must be greater than from. |
resolution |
seconds or alias | chosen from the window | 300, 900, 1800, 3600, 7200, 14400, 43200, 86400, 259200, 604800, 1209600, or 5m, 15m, 30m, 1h, 2h, 4h, 12h, 1d, 3d, 1w, 2w |
order |
string | desc |
asc or desc by candle time |
The response is an array of tuples:
| Index | Value | Type |
|---|---|---|
| 0 | Bucket start, UNIX seconds | number |
| 1 | Open price | decimal string |
| 2 | High price | decimal string |
| 3 | Low price | decimal string |
| 4 | Close price | decimal string |
| 5 | Base volume, tokens | decimal string |
| 6 | Quote volume, tokens | decimal string |
| 7 | Number of fills | number |
Hourly XLM/USDC candles since a given hour:
curl "https://demo-api.axis.markets/candles?market=XLM/CBIELTK6YBZJU5UP2WWQEUCYKLPU6AUNZ2BQ4WWFEIE3USCIHMXQDAMA&resolution=1h&from=1791018000&pretty_print"
The response had five candles. The two newest are shown:
[
[
1791032400,
"0.2230679",
"0.2250866",
"0.2220688",
"0.2221951",
"3.2746726",
"0.7308724",
13
],
[
1791028800,
"0.2201979",
"0.2259326",
"0.2159081",
"0.2223594",
"9.6849475",
"2.1374418",
36
]
]
Notes:
- Always pass
from. It defaults to0, so a request without it covers a window in 1970 and returns[]. - A window holds at most 200 candles. A longer one is coarsened to the smallest standard resolution that fits: 24 hours
at
5m(288 candles) comes back as15mcandles. Check the spacing of the timestamps when you request wide windows. - Without
resolution, the service picks one from the window. With neitherresolutionnorto, it uses2w. - Buckets are aligned to multiples of the resolution since the UNIX epoch, and
fromandtoapply at 5-minute granularity. Alignfromto the resolution to get a complete first candle. - A period without fills has no candle. Fill the gaps in your chart.
- Candles count fills (
traderecords) only. The fills of a multi-hop swap count in the market of each hop, and theswaprecord itself is not counted. The taker order fill of acrossfill(crossfill: true) is left out too, since its amounts mirror the makers' fills. - The reverse orientation inverts the prices (high and low swap places) and swaps the two volumes.
GET /ticker/24h#
24-hour statistics of every market. No parameters.
| Field | Type | Description |
|---|---|---|
ledger |
number | Last ledger processed |
timestamp |
number | End of the window, UNIX seconds |
ticker |
array | One entry per market, most fills first |
ticker[].symbol |
string | Market base/quote in canonical order |
ticker[].openPrice, highPrice, lowPrice, lastPrice |
string | Prices in quote per base |
ticker[].volume |
string | base tokens traded |
ticker[].quoteVolume |
string | quote tokens traded |
ticker[].change |
number | Change from open to last price, percent with 3 decimals |
ticker[].avgPrice |
string | Volume-weighted average price, quoteVolume / volume |
ticker[].trades |
number | Fills in the window |
curl "https://demo-api.axis.markets/ticker/24h?pretty_print"
The response had six entries. The first two are shown:
{
"ledger": 5001979,
"timestamp": 1791033489,
"ticker": [
{
"symbol": "CC72F57YTPX76HAA64JQOEGHQAPSADQWSY5DWVBR66JINPFDLNCQYHIC/CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC",
"openPrice": "0.3077308",
"highPrice": "0.3140691",
"lowPrice": "0.2879428",
"lastPrice": "0.3046354",
"volume": "2732.610373",
"quoteVolume": "819.7259556",
"change": -1.006,
"avgPrice": "0.2999791",
"trades": 2890
},
{
"symbol": "CCUUDM434BMZMYWYDITHFXHDMIVTGGD6T2I5UKNX5BSLXLW7HVR4MCGZ/CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC",
"openPrice": "5.450887",
"highPrice": "5.715252",
"lowPrice": "5.274388",
"lastPrice": "5.555403",
"volume": "66.5128764",
"quoteVolume": "364.2570939",
"change": 1.917,
"avgPrice": "5.47649",
"trades": 1248
}
]
}
The first entry is CETES/XLM: 2,732.6 CETES traded at a last price of 0.3046 XLM per CETES.
Notes:
- The window is the last 24 hours, exact to the fill.
- Every known market is listed: the markets opened on-chain and every pair with live orders. A market without fills in
the window reports
"0"prices and volumes andtrades: 0. - Entries are sorted by
tradesdescending, then bysymbol. - Symbols use the canonical orientation, which may not be the one you display. Invert the prices to show
quote/base. The JS client exportsinvertTickerfor that. - Like candles, the ticker counts fills only, never
swaprecords or the taker order fill of acrossfill.
Contract and markets#
GET /contract#
Contract state rebuilt from contract events: the freeze switch, the configuration and the markets opened on-chain. No parameters.
| Field | Type | Description |
|---|---|---|
address |
string | AXIS contract address |
frozen |
boolean | Whether the contract is frozen |
config |
object | Absent until the indexer has seen a config event |
config.safetyAdmin |
string | Safety admin address |
config.oracle |
string | Reflector oracle contract |
config.listingMinDays |
number | Days of oracle price feeds a new market must buy. 0 opens markets without a fee. |
config.marketListingFee |
string | XRF base units burned to open a market: the oracle's daily fee times listingMinDays |
config.minTradeSize |
string | Minimum order value in USD with 7 decimals (10000 is 0.001 USD). 0 disables it. |
config.ledgerTime |
number | Expected ledger close time in seconds, which the contract uses to turn lifetimes into ledgers |
markets |
array | Markets opened with subsidize, from refresh events, in the order they were first seen |
markets[].base, markets[].quote |
string | Market assets in canonical order |
markets[].created |
string | Time of the first refresh event seen for the market |
markets[].refreshed |
string | Time of the last oracle check (requote or subsidize) |
curl "https://demo-api.axis.markets/contract?pretty_print"
The response listed six markets. The first two are shown:
{
"address": "CA6P26K4QNNIMTYP22ILTSCXQQDEKNWJIZJPC34YEZYOLBNT7YUPX7XS",
"frozen": false,
"config": {
"safetyAdmin": "GBDCULE53LUPK4XHUCXBI35MAZFQHENMZ3JRKAJS2PPYBV646M6XKVHG",
"oracle": "CDEIQLTE3Y3XQMYPSZFJC2OXIS67F4MURGP73NCE2C2NZZ22RNSPV7ZI",
"listingMinDays": 90,
"marketListingFee": "180000000000",
"minTradeSize": "10000",
"ledgerTime": 5
},
"markets": [
{
"base": "CBIELTK6YBZJU5UP2WWQEUCYKLPU6AUNZ2BQ4WWFEIE3USCIHMXQDAMA",
"quote": "CCUUDM434BMZMYWYDITHFXHDMIVTGGD6T2I5UKNX5BSLXLW7HVR4MCGZ",
"created": "2026-10-05 21:12:47",
"refreshed": "2026-10-05 21:12:47"
},
{
"base": "CBIELTK6YBZJU5UP2WWQEUCYKLPU6AUNZ2BQ4WWFEIE3USCIHMXQDAMA",
"quote": "CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC",
"created": "2026-10-05 21:12:52",
"refreshed": "2026-10-05 21:12:52"
}
]
}
Notes:
- This is the market registry. A market can exist without live orders.
GET /marketslists only pairs that have live orders. - The same data is pushed on the WebSocket
contractchannel. See WebSocket API.
GET /markets#
Pairs that currently have live orders.
| Parameter | Type | Default | Description |
|---|---|---|---|
cursor |
string | none | cursor of the last market received, <base>-<quote> |
limit |
integer | 20 |
Maximum 1000 |
| Field | Type | Description |
|---|---|---|
baseAsset |
string | Base asset of the canonical pair |
quoteAsset |
string | Quote asset of the canonical pair |
cursor |
string | <base>-<quote>, the cursor for the next page |
orderTypes |
string array | Always ["LIMIT"] |
curl "https://demo-api.axis.markets/markets?limit=2&pretty_print"
[
{
"baseAsset": "CBIELTK6YBZJU5UP2WWQEUCYKLPU6AUNZ2BQ4WWFEIE3USCIHMXQDAMA",
"quoteAsset": "CCUUDM434BMZMYWYDITHFXHDMIVTGGD6T2I5UKNX5BSLXLW7HVR4MCGZ",
"cursor": "CBIELTK6YBZJU5UP2WWQEUCYKLPU6AUNZ2BQ4WWFEIE3USCIHMXQDAMA-CCUUDM434BMZMYWYDITHFXHDMIVTGGD6T2I5UKNX5BSLXLW7HVR4MCGZ",
"orderTypes": [
"LIMIT"
]
},
{
"baseAsset": "CBIELTK6YBZJU5UP2WWQEUCYKLPU6AUNZ2BQ4WWFEIE3USCIHMXQDAMA",
"quoteAsset": "CC72F57YTPX76HAA64JQOEGHQAPSADQWSY5DWVBR66JINPFDLNCQYHIC",
"cursor": "CBIELTK6YBZJU5UP2WWQEUCYKLPU6AUNZ2BQ4WWFEIE3USCIHMXQDAMA-CC72F57YTPX76HAA64JQOEGHQAPSADQWSY5DWVBR66JINPFDLNCQYHIC",
"orderTypes": [
"LIMIT"
]
}
]
Notes:
- The list is cached for 30 minutes. A new pair can take up to 30 minutes to appear, and a pair whose last order is gone stays listed until the cache refreshes.
- A pair is listed when it has at least one live order, backed or not. Use
GET /contractfor the markets opened on-chain. - A cursor in the
<base>-<quote>form that matches no listed pair restarts the list from the first market.
Orders and accounts#
GET /order/:id#
One live order by ID.
| Parameter | Type | Description |
|---|---|---|
id |
path, decimal integer | Order ID (u128) |
The response is an Order.
curl "https://demo-api.axis.markets/order/44605595615823183937087476473264348631?pretty_print"
{
"id": "44605595615823183937087476473264348631",
"status": "ACTIVE",
"buying": "CC72F57YTPX76HAA64JQOEGHQAPSADQWSY5DWVBR66JINPFDLNCQYHIC",
"selling": "CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC",
"price": "3479416000000000000",
"rprice": 3.479416,
"quote": "2908352",
"amount": "2908352",
"owner": "GACVXELMTE6JXAQCMR4OJOYBZFD6VUDLV4YZGUR2ONN77EGZVY4YCLY5",
"backed": "2908352",
"backing": {
"balance": "69249433628",
"allowance": "373054724",
"liveUntil": 5520444,
"authorized": true,
"updated": "2026-10-03 13:23:38"
},
"created": "2026-10-03 09:42:42",
"updated": "2026-10-03 09:42:42",
"cursor": "1407204591193443794944"
}
Notes:
- Only live orders are returned. An unknown, filled, removed or expired order returns 404 with
Not found. Order not found. Look up finished orders inGET /order-history. - An ID that is not a non-negative integer returns 400 with
Bad request. Invalid order ID. - An expired order keeps its entry on-chain until its owner removes or revives it, but the API treats it as gone, like
the contract's
orderview.
GET /order#
Live orders, oldest first, optionally filtered by owner and assets.
| Parameter | Type | Default | Description |
|---|---|---|---|
owner |
address | none | Maker address, G... or C... |
asset |
contract address | none | Repeat the key for two assets. Every listed asset must be one of the order's two assets, so two values select a pair in both directions. |
cursor |
string | none | cursor of the last order received |
limit |
integer | 20 |
Maximum 200 |
The response is an array of Orders.
curl "https://demo-api.axis.markets/order?owner=GACVXELMTE6JXAQCMR4OJOYBZFD6VUDLV4YZGUR2ONN77EGZVY4YCLY5&limit=1&pretty_print"
[
{
"id": "153390214124585565809379122683319801444",
"status": "ACTIVE",
"buying": "CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC",
"selling": "CC72F57YTPX76HAA64JQOEGHQAPSADQWSY5DWVBR66JINPFDLNCQYHIC",
"price": "319006000000000000",
"rprice": 0.319006,
"quote": "10341628",
"amount": "10341628",
"owner": "GACVXELMTE6JXAQCMR4OJOYBZFD6VUDLV4YZGUR2ONN77EGZVY4YCLY5",
"backed": "10341628",
"backing": {
"balance": "489427040739",
"allowance": "589201950",
"liveUntil": 5520714,
"authorized": true,
"updated": "2026-10-03 13:46:13"
},
"created": "2026-10-03 10:04:52",
"updated": "2026-10-03 10:04:52",
"cursor": "1407279463540738490369"
}
]
Notes:
- The
assetfilter compares contract addresses as given.XLMorCODE:ISSUERmatch nothing here. - Expired orders are not listed.
GET /account/:address#
Every live order of an account and the backing tracked for it, per token.
| Parameter | Type | Description |
|---|---|---|
address |
path | Account address, G... or C... |
curl "https://demo-api.axis.markets/account/GACVXELMTE6JXAQCMR4OJOYBZFD6VUDLV4YZGUR2ONN77EGZVY4YCLY5?pretty_print"
The account had 99 live orders and four backing records. One order and one backing record are shown:
{
"address": "GACVXELMTE6JXAQCMR4OJOYBZFD6VUDLV4YZGUR2ONN77EGZVY4YCLY5",
"ledger": 5002051,
"orders": [
{
"id": "44605595615823183937087476473264348631",
"status": "ACTIVE",
"buying": "CC72F57YTPX76HAA64JQOEGHQAPSADQWSY5DWVBR66JINPFDLNCQYHIC",
"selling": "CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC",
"price": "3479416000000000000",
"rprice": 3.479416,
"quote": "2908352",
"amount": "2908352",
"owner": "GACVXELMTE6JXAQCMR4OJOYBZFD6VUDLV4YZGUR2ONN77EGZVY4YCLY5",
"backed": "2908352",
"backing": {
"balance": "69249433628",
"allowance": "373054724",
"liveUntil": 5520444,
"authorized": true,
"updated": "2026-10-03 13:23:38"
},
"created": "2026-10-03 09:42:42",
"updated": "2026-10-03 09:42:42",
"cursor": "1407204591193443794944"
}
],
"backing": {
"CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC": {
"owner": "GACVXELMTE6JXAQCMR4OJOYBZFD6VUDLV4YZGUR2ONN77EGZVY4YCLY5",
"asset": "CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC",
"balance": "69249433628",
"allowance": "373054724",
"liveUntil": 5520444,
"authorized": true,
"budget": "373054724",
"updated": "2026-10-03 13:23:38"
}
}
}
Notes:
backingcovers the tokens of the account's live orders, both the token each order sells and the one it buys. While the account is subscribed on the WebSocketaccountchannel, it covers every token of the open markets, orders or not.- Records that have not loaded yet are left out. An account without orders or subscription returns empty
ordersandbacking.
GET /backing#
The tracked balance and allowance of one owner in one token.
| Parameter | Type | Default | Description |
|---|---|---|---|
owner |
address | required | G... or C... |
asset |
contract address | required | Token contract address |
The response is a Backing record.
curl "https://demo-api.axis.markets/backing?owner=GACVXELMTE6JXAQCMR4OJOYBZFD6VUDLV4YZGUR2ONN77EGZVY4YCLY5&asset=CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC&pretty_print"
{
"owner": "GACVXELMTE6JXAQCMR4OJOYBZFD6VUDLV4YZGUR2ONN77EGZVY4YCLY5",
"asset": "CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC",
"balance": "69249433628",
"allowance": "373054724",
"liveUntil": 5520444,
"authorized": true,
"budget": "373054724",
"updated": "2026-10-03 13:23:38"
}
Notes:
- The service tracks a pair only while the owner has a live order in that token (on either side) or an open WebSocket
accountsubscription. Otherwise the endpoint returns 404 withNot found. Backing is not tracked for the owner and asset. - Right after tracking starts, the record can be returned before it has loaded, with zero amounts,
authorized: falseand noupdated. Treat a record withoutupdatedas unknown. - The record is an indexed view. When a decision must be exact, read the allowance from the token contract and the
spendable balance from the account or trustline entry. The token's
balance()includes amounts locked by selling liabilities and, for XLM, the reserve.
History#
GET /order-history#
Archived orders: filled, removed and expired.
| Parameter | Type | Default | Description |
|---|---|---|---|
owner |
address | none | Maker address, G... or C... |
pair |
contract address | none | Exactly two token contract addresses as repeated keys, pair=<x>&pair=<y>, in either order |
cursor |
string | none | cursor of the last order received |
limit |
integer | 20 |
Maximum 500 |
The response is an array of Orders without backed and backing, newest first by creation time, not by the
time they were archived.
curl "https://demo-api.axis.markets/order-history?owner=GACVXELMTE6JXAQCMR4OJOYBZFD6VUDLV4YZGUR2ONN77EGZVY4YCLY5&limit=1&pretty_print"
[
{
"id": "244077557362344635759726310078879391963",
"status": "FILLED",
"buying": "CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC",
"selling": "CC72F57YTPX76HAA64JQOEGHQAPSADQWSY5DWVBR66JINPFDLNCQYHIC",
"price": "296915534537630660",
"rprice": 0.29691553453763064,
"quote": "24390422",
"amount": "0",
"owner": "GACVXELMTE6JXAQCMR4OJOYBZFD6VUDLV4YZGUR2ONN77EGZVY4YCLY5",
"created": "2026-10-03 13:43:12",
"updated": "2026-10-03 13:44:47",
"cursor": "1408016927976499183616"
}
]
Notes:
FILLEDorders haveamount"0".CANCELEDorders (removed by their owner) keep the amount that was still open.EXPIREDorders keep the unfilled amount.- An
EXPIREDorder goes back to the live orders if its owner revives it withupdate. - Order IDs can be reused once an order is gone, so the archive can hold several records with the same
id. Theircursortells them apart. - Use repeated keys for
pair. The bracket formpair[]=is not recognized, and the request returns unfiltered results.
GET /trades#
Fills and swaps, newest first.
| Parameter | Type | Default | Description |
|---|---|---|---|
trader |
address | none | Matches the taker or the maker of a fill, or the trader of a swap |
pair |
contract address | none | Exactly two token contract addresses as repeated keys, in either order |
cursor |
string | none | cursor of the last record received |
limit |
integer | 20 |
Maximum 500 |
The response is an array of Trade and Swap records, told apart by type.
curl "https://demo-api.axis.markets/trades?limit=2&pretty_print"
[
{
"type": "trade",
"id": "1407946559234737438722",
"order": "33373580251613825955093796938150226715",
"taker": "GB32JP5GNOQLW4NUMV6H2UBOSMB4DROL7YNDBWAHYXMVFLLJOH3LXKHK",
"maker": "GB32JP5GNOQLW4NUMV6H2UBOSMB4DROL7YNDBWAHYXMVFLLJOH3LXKHK",
"soldAsset": "CBIELTK6YBZJU5UP2WWQEUCYKLPU6AUNZ2BQ4WWFEIE3USCIHMXQDAMA",
"boughtAsset": "CCUUDM434BMZMYWYDITHFXHDMIVTGGD6T2I5UKNX5BSLXLW7HVR4MCGZ",
"sold": "974786",
"bought": "832170",
"price": 0.8536950674301846,
"cursor": "1407946559234737438722",
"timestamp": "2026-10-03 13:22:22",
"left": "368016"
},
{
"type": "trade",
"id": "1407945433334293725188",
"order": "263834615734212946979442271082514120154",
"taker": "GBDCULE53LUPK4XHUCXBI35MAZFQHENMZ3JRKAJS2PPYBV646M6XKVHG",
"maker": "GB32JP5GNOQLW4NUMV6H2UBOSMB4DROL7YNDBWAHYXMVFLLJOH3LXKHK",
"soldAsset": "CCUUDM434BMZMYWYDITHFXHDMIVTGGD6T2I5UKNX5BSLXLW7HVR4MCGZ",
"boughtAsset": "CBIELTK6YBZJU5UP2WWQEUCYKLPU6AUNZ2BQ4WWFEIE3USCIHMXQDAMA",
"sold": "113052",
"bought": "132097",
"price": 1.1684623005342674,
"cursor": "1407945433334293725188",
"timestamp": "2026-10-03 13:22:02",
"left": "1124020"
}
]
Notes:
- There is one
traderecord per fill. Aswapcall adds oneswaprecord on top of the fills of its hops, so never add swap amounts to fill volumes. - A swap is filed under the pair of its first and last asset, for example EURC/XLM, in canonical order, for an XLM to EURC route through USDC.
- In a multi-hop swap, the fills after the first hop list the AXIS contract as
taker, because the contract pays those hops with what the previous hop delivered. - A
crossfilladds, after its maker fills, the fill of its taker order withcrossfill: true. Itstakeris the caller, who keeps the surplus, and its amounts mirror the makers' fills, so leave it out of volume sums. - Trades and swaps have no on-chain ID. Their
idis the position of the event (ledger, transaction and event index), which also orders them. - Use repeated keys for
pair, as for/order-history.
GET /failures#
Failed AXIS calls, newest first. The records are diagnostics only: a failed call changes nothing on-chain, so it never counts against a maker or a taker, and the AXIS API does not use these records for routing.
| Parameter | Type | Default | Description |
|---|---|---|---|
account |
address | none | Matches the caller and both parties of the failed token transfer |
fn |
string | none | Contract function called, for example trade, swap or crossfill. Lowercase letters and underscores, at most 32. |
cursor |
string | none | cursor of the last record received |
limit |
integer | 20 |
Maximum 500 |
The response is an array of Failure records.
curl "https://demo-api.axis.markets/failures?account=GB32JP5GNOQLW4NUMV6H2UBOSMB4DROL7YNDBWAHYXMVFLLJOH3LXKHK&limit=1&pretty_print"
An illustrative record of a trade whose payment to a maker failed:
[
{
"type": "failure",
"id": "1408023584926158962688",
"txHash": "5d0c2bb6e4a1f7a3c9e85b1d2f4a6c8e0b3d5f7a9c1e3b5d7f9a1c3e5b7d9f1a",
"ledger": 5002410,
"timestamp": "2026-10-03 14:02:17",
"fn": "trade",
"caller": "GB32JP5GNOQLW4NUMV6H2UBOSMB4DROL7YNDBWAHYXMVFLLJOH3LXKHK",
"orders": [
"33373580251613825955093796938150226715"
],
"result": "invokeHostFunctionTrapped",
"reason": "transfer",
"error": {
"contract": "CBIELTK6YBZJU5UP2WWQEUCYKLPU6AUNZ2BQ4WWFEIE3USCIHMXQDAMA",
"code": 10
},
"transfer": {
"token": "CBIELTK6YBZJU5UP2WWQEUCYKLPU6AUNZ2BQ4WWFEIE3USCIHMXQDAMA",
"fn": "transfer_from",
"from": "GB32JP5GNOQLW4NUMV6H2UBOSMB4DROL7YNDBWAHYXMVFLLJOH3LXKHK",
"to": "GACVXELMTE6JXAQCMR4OJOYBZFD6VUDLV4YZGUR2ONN77EGZVY4YCLY5",
"amount": "974786"
},
"cursor": "1408023584926158962688"
}
]
Here the taker's USDC payment to a maker failed with the token's BalanceError (#10): either the taker could not pay or
the maker's trustline had no room for the payment. The record names both parties and blames neither.
Notes:
- The AXIS API streams every network transaction and reads the call tree of the ones touching the contract from their
diagnostic events. A failed AXIS call is recorded whether the transaction called the contract or another contract did
(a router), and whether the transaction failed or a calling contract caught the failure (
caught). A transaction that fails in another contract after its AXIS call returned is not recorded: the AXIS call itself succeeded. - When no failed AXIS call shows in the call tree, for example because the RPC server returns no diagnostic events, a
failed transaction calling the contract directly is still recorded from its envelope. Its
reasonthen comes from the result code:resourcesfor a resource problem,unknownotherwise. reasontells what failed:contract(a contract error: an AXIS error named inerror.name, or an error of another contract the call invoked, such as the oracle),transfer(a token transfer failed, seetransfer),resources(a resource limit, the refundable fee or an archived entry),auth(an authorization failure) orunknown(the cause could not be determined).- A
crossfilllists its taker order intakerOrder. Its payments to the makers come from the taker order's owner, so a failed transfer there involves the owner and a maker, never the caller.
Data shapes#
Order#
| Field | Type | Description |
|---|---|---|
id |
string | Order ID, a decimal u128 derived from the owner and a nonce. Not sequential. |
status |
string | ACTIVE, FILLED, CANCELED (removed by its owner) or EXPIRED |
selling |
string | Token the maker sells |
buying |
string | Token the maker receives |
price |
string | buying base units per 1 selling base unit, times 10^18 |
rprice |
number | price / 10^18 as a float, for display |
quote |
string | Selling amount when the order was created. Updates do not change it. |
amount |
string | Selling amount left |
owner |
string | Maker address |
backed |
string | Part of amount the maker's budget covers. Orders with a tracked backing only, "0" once the order is no longer live. |
backing |
object | The maker's backing in the selling token, see below. Orders with a tracked backing only. |
expires |
string | Expiration time, UTC. Absent when the order never expires. |
created |
string | Creation time, UTC |
updated |
string | Time of the last change, UTC. For an expired order, its expiration time. |
cursor |
string | Position of the creation event, the pagination cursor |
The embedded backing object:
| Field | Type | Description |
|---|---|---|
balance |
string | Amount the maker can transfer: for Classic accounts, the balance minus Classic DEX selling liabilities and, for XLM, the reserve |
allowance |
string | Allowance the maker granted to the AXIS contract |
liveUntil |
number | Ledger sequence the allowance lives until, 0 without an allowance |
authorized |
boolean | Whether the maker can send and receive the token |
updated |
string | When the values were last loaded, UTC. Absent until the record has loaded. |
pending |
boolean | Present and true while a reload confirming the latest change is still due. The values may predate that change. |
backed comes from the maker's budget in the token, min(balance, allowance), or zero when the maker is not authorized
or the allowance has expired. The budget is split across the maker's live orders selling that token, oldest first. The
contract has no priority between these orders, so backed estimates which of them a fill would skip. See
Settlement and allowances.
A live order:
{
"id": "44605595615823183937087476473264348631",
"status": "ACTIVE",
"buying": "CC72F57YTPX76HAA64JQOEGHQAPSADQWSY5DWVBR66JINPFDLNCQYHIC",
"selling": "CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC",
"price": "3479416000000000000",
"rprice": 3.479416,
"quote": "2908352",
"amount": "2908352",
"owner": "GACVXELMTE6JXAQCMR4OJOYBZFD6VUDLV4YZGUR2ONN77EGZVY4YCLY5",
"backed": "2908352",
"backing": {
"balance": "69249433628",
"allowance": "373054724",
"liveUntil": 5520444,
"authorized": true,
"updated": "2026-10-03 13:23:38"
},
"created": "2026-10-03 09:42:42",
"updated": "2026-10-03 09:42:42",
"cursor": "1407204591193443794944"
}
This order sells 0.2908352 XLM for CETES at 3.479416 CETES per XLM, and the maker's budget covers all of it.
Backing#
Returned by GET /backing and as the values of backing in GET /account/:address.
| Field | Type | Description |
|---|---|---|
owner |
string | Account address |
asset |
string | Token contract address |
balance |
string | Amount the account can transfer: for Classic accounts, the balance minus Classic DEX selling liabilities and, for XLM, the reserve |
allowance |
string | Allowance granted to the AXIS contract |
liveUntil |
number | Ledger sequence the allowance lives until, 0 without an allowance |
authorized |
boolean | Whether the account can send and receive the token |
budget |
string | min(balance, allowance), or 0 when not authorized or once the allowance has expired |
headroom |
string | Amount the account can still receive in the token: the trustline limit minus the balance and buying liabilities. Present for trustline assets, absent for XLM, contract balances and when unknown. |
updated |
string | When the values were last loaded, UTC. Absent until the record has loaded. |
skipped |
string | Time of the last skip event of an order of this owner selling this token, UTC. Absent when there was none. |
pending |
boolean | Present and true while a reload confirming the latest change is still due |
{
"owner": "GACVXELMTE6JXAQCMR4OJOYBZFD6VUDLV4YZGUR2ONN77EGZVY4YCLY5",
"asset": "CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC",
"balance": "69249433628",
"allowance": "373054724",
"liveUntil": 5520444,
"authorized": true,
"budget": "373054724",
"updated": "2026-10-03 13:23:38"
}
Trade#
One fill of a maker order.
| Field | Type | Description |
|---|---|---|
type |
string | trade |
id |
string | Trade ID, the event position |
order |
string | Maker order ID |
taker |
string | The trader for trade and the first hop of a swap, the AXIS contract for later swap hops, the taker order's owner for crossfill maker fills, and the caller for the taker order's own fill |
maker |
string | Order owner |
soldAsset |
string | Token the taker paid, which the maker received |
boughtAsset |
string | Token the taker received, which the maker delivered |
sold |
string | Amount of soldAsset, base units |
bought |
string | Amount of boughtAsset, base units |
left |
string | Amount the maker order has left after the fill. "0" means filled and removed. |
crossfill |
boolean | true on the fill of a crossfill taker order, absent otherwise. Candles and the ticker leave such fills out. |
price |
number | bought / sold as a float, for display |
cursor |
string | Same as id |
timestamp |
string | Ledger close time, UTC |
{
"type": "trade",
"id": "1407946559234737438722",
"order": "33373580251613825955093796938150226715",
"taker": "GB32JP5GNOQLW4NUMV6H2UBOSMB4DROL7YNDBWAHYXMVFLLJOH3LXKHK",
"maker": "GB32JP5GNOQLW4NUMV6H2UBOSMB4DROL7YNDBWAHYXMVFLLJOH3LXKHK",
"soldAsset": "CBIELTK6YBZJU5UP2WWQEUCYKLPU6AUNZ2BQ4WWFEIE3USCIHMXQDAMA",
"boughtAsset": "CCUUDM434BMZMYWYDITHFXHDMIVTGGD6T2I5UKNX5BSLXLW7HVR4MCGZ",
"sold": "974786",
"bought": "832170",
"price": 0.8536950674301846,
"cursor": "1407946559234737438722",
"timestamp": "2026-10-03 13:22:22",
"left": "368016"
}
AXIS allows self-trades, so taker and maker can be the same address, as here.
Swap#
One swap call, recorded once on top of the fills of its hops.
| Field | Type | Description |
|---|---|---|
type |
string | swap |
id |
string | Swap ID, the event position |
trader |
string | Address that called swap |
soldAsset |
string | Token the trader paid |
boughtAsset |
string | Token the trader received |
sold |
string | Amount paid, base units |
bought |
string | Amount received, base units |
price |
number | bought / sold as a float, for display |
cursor |
string | Same as id |
timestamp |
string | Ledger close time, UTC |
{
"type": "swap",
"id": "1407466362922858446860",
"trader": "GB7TZS65DOB3KJLRPT2EDTMZQSCORUIQGCKP7XVNZ77VNKOTKQEY7F23",
"soldAsset": "CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC",
"boughtAsset": "CCUUDM434BMZMYWYDITHFXHDMIVTGGD6T2I5UKNX5BSLXLW7HVR4MCGZ",
"sold": "9999996",
"bought": "1844614",
"price": 0.18446147378458952,
"cursor": "1407466362922858446860",
"timestamp": "2026-10-03 11:00:12"
}
This swap sold 0.9999996 XLM for 0.1844614 EURC through USDC.
Failure#
One failed AXIS call.
| Field | Type | Description |
|---|---|---|
type |
string | failure |
id |
string | Position of the transaction (ledger and application order) plus the index of the failed call, which also orders the records |
txHash |
string | Transaction hash |
ledger |
number | Ledger sequence |
timestamp |
string | Ledger close time, UTC |
fn |
string | Contract function called |
caller |
string | Address that authorized the call (its trader or sponsor argument), otherwise the transaction source |
orders |
string array | Order IDs the call listed: maker orders for trade, swap and crossfill, the updated orders for update, none for subsidize |
takerOrder |
string | Taker order ID of a crossfill. Absent for other functions. |
result |
string | Operation or transaction result code, for example invokeHostFunctionTrapped. invokeHostFunctionSuccess for a caught failure. |
caught |
boolean | true when the transaction succeeded because a calling contract caught the failed AXIS call. Absent otherwise. |
reason |
string | contract, transfer, resources, auth or unknown |
error |
object | {contract, code, name?}: the contract error that failed the call, when known. name is set for AXIS errors. |
transfer |
object | {token, fn, from, to, amount}: the token transfer that failed, when the diagnostic events show it. fn is transfer or transfer_from. |
cursor |
string | Same as id |
A failed payment fails with the token's own error whether the payer could not pay or the recipient could not be
credited, for example on a full trustline. The contract does not tell the two apart, so neither party is at fault from a
transfer record alone. See Failure modes.
Using a quote in a contract call#
A quote tells you which orders to list. Your transaction calls the AXIS contract directly, through Stellar RPC, and the contract checks every price and backing again. The AXIS API never signs or submits anything.
| Quote | Contract call | Arguments from the quote |
|---|---|---|
| Any route | swap |
One TradeStep per hop: asset is the hop's buying, orders its orders. Sell takes sold as the exact input and your minimum output. Buy takes bought as the exact output and your maximum input. |
Direct market (direct=true), strict_send |
trade with Sell, kind Fill |
orders of the hop. Limit price = floor(10^36 / worstPrice), the minimum buying per 1 selling. |
Direct market (direct=true), strict_receive |
trade with Buy, kind Fill |
orders of the hop. Limit price = worstPrice, the maximum selling per 1 buying. |
Direct market with maxPrice |
trade with kind Limit |
orders of the hop and your own limit price. The contract fills the listed orders and stores the remainder as an order. |
The second and third rows are market orders: they cross the listed orders at no worse than the worst quoted price and
place nothing on the book. With kind FillOrKill the call fails with NotFilled (709) unless the amount fills
completely. Code for both conversions is in API and stream clients, and the full
swap flow with slippage and approval planning in Wallet integration.
Before you build the call:
- The trader needs an allowance to the AXIS contract on the token they pay, covering this spend plus what their open orders still sell, see Wallet integration.
- The trader must be able to receive the bought asset, see Wallet integration.
- Orders that are gone by the time the transaction lands are skipped. A market order then fills less, while Fill-or-Kill
and
swapfail withNotFilled(709) when their bounds are not met. See Failure modes.
The JS client wraps all of this: AxisAccount.swap and the market orders of AxisAccount.sell and AxisAccount.buy
quote, plan the approval and submit for you (see Axis, markets and accounts).