0Bit Documentation

List 0Pools trades

GET /pools/trades - Cursor-paginated, partner-scoped trade history for support and reconciliation.

0Pools API pages are for approved headless partners. They cover the partner-visible quote, transact, status, trade, and balance lifecycle only.

Return this partner's trades, newest first, as a cursor-paginated list. This is a pure read for back-office views, support tooling, and reconciliation — it does not advance settlement (unlike the status poll). For a single trade by id, use Retrieve a trade; to progress a reserved trade, use the status poll.

Endpoint

FieldValue
MethodGET
Path/pools/trades
AreaTrades
Operation idlistTrades
Auth boundarySecret key from your server.

Base URL: https://pools-api.0bit.app/v1. Authenticate with your secret key (sk_*) as Authorization: Bearer sk_...; never call this from the browser. Results are scoped to your partner organization — another partner's trades are invisible, and an id outside your scope returns 404 (never 403).

Use it for

List partner-scoped trades for support and reconciliation.

Use this endpoint only for the partner-scoped resource it describes. Store your own reference id, the returned 0Bit object id, the request id, timestamps, and the current status so support and reconciliation do not depend on browser callbacks alone.

Pagination model: cursor, not offset

0Pools paginates with an opaque forward cursor, not page numbers or offsets. There is no page or offset parameter, and there is no total count. You walk pages by feeding the previous page's nextCursor back in as ?cursor=.

Cursor is the last transactId of the prior page

The cursor is a transactId. The first call omits cursor and returns the newest rows; each response hands back a nextCursor you pass to fetch the page after that trade. Stop when the page reports hasMore: false (and nextCursor is null). Cursor pagination is stable as new trades arrive — you never skip or double-read rows the way an offset can — which is why it is the right model for an append-only, time-ordered trade ledger.

Query paramTypeDefaultNotes
limitinteger50Page size, 1–100. Out-of-range values are rejected with 400.
cursorstringA transactId from a prior page's nextCursor. Returns the rows after it. Omit on page one.
sidestringFilter by on_ramp or off_ramp.
statusstringFilter by trade status (see trade status).
created_afterstringInclusive lower bound on createdAt, ISO-8601 (e.g. 2026-06-01T00:00:00Z).
created_beforestringInclusive upper bound on createdAt, ISO-8601.

Filters compose: combining side, status, and a created_after/created_before window narrows the same ordered set. Filters are also carried implicitly across pages — keep the same filter set when you follow nextCursor.

Response shape

Every call returns HTTP 200 with this envelope:

FieldTypeNotes
dataarrayA page of trade rows, newest first.
nextCursorstring | nullThe last transactId in data; pass as ?cursor=. null when exhausted.
hasMorebooleantrue when another page exists; false on the final page.

Drive your loop off hasMore. When it is false, nextCursor is null and you have read the full set under the current filters.

Trade row

Each item in data is the full trade object. Money and rate values are decimal strings; *Bps values are integers. The same object is returned by Retrieve a trade.

FieldTypeNotes
transactIdstringThe trade id. Also the value you use as a cursor.
quoteIdstringThe quote this trade was executed against.
poolIdstringPool the trade belongs to (e.g. EUR-USDT).
pairstringHuman-readable pair (e.g. EUR/USDT).
sidestringon_ramp (buy) or off_ramp (sell).
statusstringTrade status — see trade status.
fiatCurrencystringFiat leg currency (e.g. EUR).
cryptoCurrencystringCrypto leg asset (e.g. USDT).
cryptoNetworkstringChain the trade transacts on, locked at quote time (tron, ethereum, bsc, polygon, or solana). Read /pools/{id}/capabilities for the allowlist rather than hard-coding it.
fiatAmountstring (decimal)Fiat leg amount.
cryptoAmountstring (decimal)Crypto leg amount.
quotedRatestring (decimal)All-in rate locked on the quote (output per input).
spreadBpsintegerFX pool spread, basis points.
feeBpsintegerPer-tier draw fee, basis points.
totalBpsintegerDerived: spreadBps + feeBps.
engineFillTxIdstring | nullOpaque internal fill reference for support correlation; null until set.
createdAtstringISO-8601 creation time. List ordering key (newest first).
settledAtstring | nullISO-8601 settlement time; null until the trade settles.

Trade status

The status filter and the status field share the trade lifecycle:

StatusMeaning
quotedA quote exists; no trade has been reserved against it yet.
reservedThe trade was created from a quote and is in flight. Poll the status endpoint to progress it.
settledThe trade completed; the delivery/payout leg succeeded.
releasedThe reservation was released without settling.
failedThe trade did not complete.

A settled trade can later flip to 'returned'

After settlement a trade may move to returned — for example a fiat payout returned by the receiving bank, or a downstream crypto delivery that failed. This triggers an idempotent auto-refund of the trade value to your partner balance. returned is surfaced only through this read and the status poll; there is no webhook for it. Re-read trades (or poll) for terminal trades when reconciling, rather than assuming settled is final.

Examples

A first page, then a follow-up page, then a filtered window.

# Page one (newest 25 trades)
curl -s "https://pools-api.0bit.app/v1/pools/trades?limit=25" \
  -H "Authorization: Bearer $POOLS_KEY"

# Next page — pass the prior nextCursor
curl -s "https://pools-api.0bit.app/v1/pools/trades?limit=25&cursor=ptx_8f3c1d2e4b5a6c7d" \
  -H "Authorization: Bearer $POOLS_KEY"

# Filtered: settled buys in a date window
curl -s "https://pools-api.0bit.app/v1/pools/trades?side=on_ramp&status=settled&created_after=2026-06-01T00:00:00Z&created_before=2026-06-28T00:00:00Z" \
  -H "Authorization: Bearer $POOLS_KEY"
// Walk every page under one filter set. Server-side only — sk_* never ships to the browser.
async function listAllTrades(filters = {}) {
  const trades = [];
  let cursor = null;

  do {
    const params = new URLSearchParams({ limit: '100', ...filters });
    if (cursor) params.set('cursor', cursor);

    const res = await fetch(`https://pools-api.0bit.app/v1/pools/trades?${params}`, {
      headers: { Authorization: `Bearer ${process.env.POOLS_KEY}` },
    });
    if (!res.ok) throw new Error(`trades ${res.status} (request ${res.headers.get('X-Request-Id')})`);

    const page = await res.json();
    trades.push(...page.data);
    cursor = page.nextCursor; // null on the final page
    if (!page.hasMore) break;
  } while (cursor);

  return trades;
}

const settledSells = await listAllTrades({ side: 'off_ramp', status: 'settled' });
import os, requests

def list_all_trades(**filters):
    trades, cursor = [], None
    while True:
        params = {"limit": 100, **filters}
        if cursor:
            params["cursor"] = cursor
        res = requests.get(
            "https://pools-api.0bit.app/v1/pools/trades",
            params=params,
            headers={"Authorization": f"Bearer {os.environ['POOLS_KEY']}"},
        )
        res.raise_for_status()  # request id is in res.headers["X-Request-Id"]
        page = res.json()
        trades.extend(page["data"])
        cursor = page["nextCursor"]  # None on the final page
        if not page["hasMore"]:
            break
    return trades

settled_buys = list_all_trades(side="on_ramp", status="settled")

Example response

{
  "data": [
    {
      "transactId": "ptx_8f3c1d2e4b5a6c7d",
      "quoteId": "pq_2a9b4c6d8e0f1a3b",
      "poolId": "EUR-USDT",
      "pair": "EUR/USDT",
      "side": "on_ramp",
      "status": "settled",
      "fiatCurrency": "EUR",
      "cryptoCurrency": "USDT",
      "cryptoNetwork": "ethereum",
      "fiatAmount": "100.00",
      "cryptoAmount": "107.31",
      "quotedRate": "1.0731",
      "spreadBps": 50,
      "feeBps": 30,
      "totalBps": 80,
      "engineFillTxId": "fill_5c7e9a1b3d2f4e60",
      "createdAt": "2026-06-27T14:02:11Z",
      "settledAt": "2026-06-27T14:03:48Z"
    }
  ],
  "nextCursor": "ptx_8f3c1d2e4b5a6c7d",
  "hasMore": true
}

Placeholder ids and amounts above are illustrative only.

Errors

Every response carries an X-Request-Id header — quote it on support tickets. Error bodies use the unified envelope { type, code, message, request_id, doc_url, statusCode }.

StatustypeWhen
400invalid_requestBad limit (outside 1–100), malformed cursor, or unparseable date filter.
401unauthorizedMissing or invalid key.
403forbiddenWrong key mode (key_mode_mismatch) — e.g. a pk_* used on this secret-only route. No pool-access denial codes apply here.
429rate_limitedThrottled. Back off and retry.
{
  "type": "invalid_request",
  "code": "invalid_limit",
  "message": "limit must be between 1 and 100",
  "request_id": "req_4d6f8a0b2c1e3f5a",
  "doc_url": null,
  "statusCode": 400
}

Because this is a partner-scoped read, there is no cross-tenant 403 (the 403 above is only a key-mode mismatch, never a data-access denial): data outside your organization simply does not appear, and a cursor that is not one of your trades yields an empty, well-formed page rather than an error.

Production rules

  • Keep secret keys on your server.
  • Validate environment, mode, entitlement, asset, network, and amount before the call.
  • Page with cursor + hasMore; never assume an unbounded list read or invent offset/page parameters.
  • Branch on machine-readable status, error code, object id, and request id.
  • Re-read terminal trades when reconciling — a settled trade can still flip to returned (poll-only).
  • Treat examples and placeholder ids as fake data only.

Public boundary

This reference covers partner-visible discovery, quote, transact, status, trade, and balance behavior. Liquidity operations, routing internals, provider details, reserve logic, and runbooks are outside the public API contract.

On this page