X Enterprises
fastify-xplaid

investments.getHoldings(accessToken, options?)

Get current investment holdings and security metadata for a Plaid Item.

investments.getHoldings(accessToken, options?)

Returns the current investment portfolio — positions held across brokerage, retirement, and other investment accounts — along with security metadata for each holding. Requires the investments product to have been enabled in the Link token.

Signature

fastify.xplaid.investments.getHoldings(
  accessToken: string,
  options?: {
    accountIds?: string[]
  }
): Promise<{
  accounts: PlaidAccount[]
  holdings: Holding[]
  securities: Security[]
  item: PlaidItem
  requestId: string
}>

Params

NameTypeRequiredDescription
accessTokenstringYesThe Item's permanent access token.
options.accountIdsstring[]NoRestrict results to specific account IDs.

Returns

Promise<{ accounts, holdings, securities, item, requestId }>

FieldDescription
accountsInvestment accounts for the Item.
holdingsCurrent positions. Each holding links to a security_id and includes quantity, institution_value, and cost_basis.
securitiesSecurity metadata keyed by security_id — includes name, ticker_symbol, type, close_price.

Throws

  • [xPlaid] accessToken is required and must be a non-empty string
  • Plaid API errors — shape: { message, statusCode, plaidError }.
  • PRODUCTS_NOT_SUPPORTED Plaid error if investments was not enabled for this Item.
  • ITEM_LOGIN_REQUIRED Plaid error if the Item needs re-authentication.

Examples

Basic — portfolio value

const { holdings, securities } = await fastify.xplaid.investments.getHoldings(accessToken);

const securityMap = Object.fromEntries(securities.map((s) => [s.security_id, s]));

const portfolio = holdings.map((h) => ({
  name: securityMap[h.security_id]?.name,
  ticker: securityMap[h.security_id]?.ticker_symbol,
  quantity: h.quantity,
  value: h.institution_value,
  costBasis: h.cost_basis,
}));

Realistic — portfolio route with gain/loss calculation

fastify.get("/investments/portfolio", async (request, reply) => {
  const item = await db.plaidItems.findFirst({ where: { userId: request.user.id } });
  if (!item) return reply.status(404).send({ error: "No linked account" });

  try {
    const { holdings, securities } = await fastify.xplaid.investments.getHoldings(
      decrypt(item.accessToken)
    );

    const securityMap = Object.fromEntries(securities.map((s) => [s.security_id, s]));
    const totalValue = holdings.reduce((sum, h) => sum + (h.institution_value ?? 0), 0);

    return {
      totalValue,
      positions: holdings.map((h) => ({
        name: securityMap[h.security_id]?.name,
        ticker: securityMap[h.security_id]?.ticker_symbol,
        type: securityMap[h.security_id]?.type,
        quantity: h.quantity,
        value: h.institution_value,
        costBasis: h.cost_basis,
        gainLoss:
          h.institution_value != null && h.cost_basis != null
            ? h.institution_value - h.cost_basis
            : null,
      })),
    };
  } catch (error) {
    if (error.plaidError) {
      request.log.error({ code: error.plaidError.error_code }, "Holdings fetch failed");
      return reply.status(error.statusCode ?? 500).send({ error: error.message });
    }
    throw error;
  }
});

See also


AI Context

package: "@xenterprises/fastify-xplaid"
method: fastify.xplaid.investments.getHoldings(accessToken, options?)
use-when: Get current investment portfolio positions and security metadata — requires "investments" product
returns: { holdings, securities, accounts, item, requestId }
Copyright © 2026