API Reference

Programmatic read-only access to your treasury data. Requires an active Enterprise plan.

Generate an API key from the Billing page.


Authentication

All API requests require a Bearer token in the Authorization header. Generate an API key from the Billing page.

curl https://portfi.io/api/v1/balances \
  -H "Authorization: Bearer tbops_xxxx"

The organization is derived from the API key automatically. You do not need to pass an orgId parameter.


Endpoints

GET /api/v1/balances

Returns the latest token balance for each wallet/asset pair in your organization.

Query parameters

ParameterTypeRequiredDefaultDescription
limitintegerNo50Max results per page. Maximum: 100.
offsetintegerNo0Pagination offset.

Example request

curl https://portfi.io/api/v1/balances?limit=50 \
  -H "Authorization: Bearer tbops_xxxx"

Example response

{
  "balances": [
    {
      "walletId": "abc123",
      "walletName": "Primary Treasury",
      "address": "0x1234...abcd",
      "chain": "ETHEREUM",
      "assetId": "ousg-ethereum",
      "symbol": "OUSG",
      "decimals": 18,
      "rawBalance": "1000000000000000000000",
      "formattedBalance": "1000.000000000000000000",
      "usdValue": "1050.00",
      "referencePrice": "1.05",
      "fetchedAt": "2025-01-15T10:30:00.000Z"
    }
  ],
  "pagination": {
    "limit": 50,
    "offset": 0,
    "count": 1,
    "nextOffset": null
  },
  "generatedAt": "2025-01-15T10:30:00.000Z"
}

GET /api/v1/transfers

Returns transfer events (inflows and outflows) for all wallets in your organization.

Query parameters

ParameterTypeRequiredDefaultDescription
limitintegerNo100Max results. Maximum: 500.
fromstringNoStart date filter (YYYY-MM-DD).
tostringNoEnd date filter (YYYY-MM-DD, inclusive).

Example request

curl "https://portfi.io/api/v1/transfers?from=2025-01-01&to=2025-01-31" \
  -H "Authorization: Bearer tbops_xxxx"

Example response

{
  "transfers": [
    {
      "chain": "ETHEREUM",
      "txHash": "0xabc123...",
      "logIndex": 0,
      "walletId": "abc123",
      "walletName": "Primary Treasury",
      "address": "0x1234...abcd",
      "assetId": "ousg-ethereum",
      "symbol": "OUSG",
      "direction": "IN",
      "rawAmount": "500000000000000000000",
      "formattedAmount": "500.000000000000000000",
      "blockNumber": 19500000,
      "timestamp": "2025-01-10T14:22:00.000Z"
    }
  ],
  "count": 1,
  "generatedAt": "2025-01-15T10:30:00.000Z"
}

GET /api/v1/snapshots

Returns daily treasury snapshots showing aggregate balances and per-wallet breakdowns.

Query parameters

ParameterTypeRequiredDefaultDescription
limitintegerNo30Max results. Maximum: 90.
fromstringNoStart date (YYYY-MM-DD).
tostringNoEnd date (YYYY-MM-DD, inclusive).

Example request

curl "https://portfi.io/api/v1/snapshots?limit=7" \
  -H "Authorization: Bearer tbops_xxxx"

Example response

{
  "snapshots": [
    {
      "date": "2025-01-15",
      "totalsJson": {
        "OUSG": {
          "formatted": "1000.00",
          "usdValue": "1050.00"
        }
      },
      "perWalletJson": {},
      "createdAt": "2025-01-15T01:00:00.000Z"
    }
  ],
  "count": 1,
  "generatedAt": "2025-01-15T10:30:00.000Z"
}

Error codes

StatusDescription
401Missing or invalid API key.
402Active Enterprise plan required. Upgrade your plan to access the API.
403Access denied. Your key does not have permission for this resource.
422Invalid query parameter (e.g. non-integer limit, malformed date).
429Rate limit exceeded. See the Retry-After response header.
500Internal server error.

All error responses return a JSON body with an error string field describing the issue.


Rate limits

60 requests per minute per API key. Requests exceeding this limit receive HTTP 429 with a Retry-After response header indicating seconds until the next request is allowed.

Higher limits are available — contact us.


Code examples

curl

curl https://portfi.io/api/v1/balances?limit=50 \
  -H "Authorization: Bearer tbops_xxxx"

TypeScript / JavaScript

async function fetchBalances(offset = 0) {
  const res = await fetch(
    `https://portfi.io/api/v1/balances?limit=50&offset=${offset}`,
    {
      headers: {
        Authorization: "Bearer tbops_xxxx",
      },
    }
  );

  if (!res.ok) {
    const err = await res.json().catch(() => ({}));
    throw new Error(`API error ${res.status}: ${err.error ?? "unknown"}`);
  }

  return res.json();
}

// Fetch first page
try {
  const data = await fetchBalances();
  console.log(data.balances);

  // Paginate if there are more results
  if (data.pagination.nextOffset !== null) {
    const nextData = await fetchBalances(data.pagination.nextOffset);
    console.log(nextData.balances);
  }
} catch (err) {
  console.error("Failed to fetch balances:", err);
}