Skip to main content

Investor Portfolio

Show an investor their Wefunder holdings: a summary of totals and a paginated list of positions with per-security detail. Requires the read:investments scope on a user-authorized token — the investor grants your app access through the OAuth consent screen, and the endpoints return that investor's portfolio.

Beta

These endpoints are in beta. Field additions are possible; breaking changes will follow the deprecation policy.

User-authorized tokens only

Portfolio data is only available for the investor who authorized the token — an app cannot read another user's portfolio. App-only (client credentials) tokens receive 403.

Conventions​

Monetary values

Monetary values are integer cents (215000 = $2,150.00) in the currency next to them (lower-case ISO 4217: usd, or eur for an ECSP round). Per-share prices and return multiples are decimal strings ("14.4632", "1.3454").

Summary totals are in one currency only — usd when the investor holds any USD position, otherwise the portfolio's only currency. Positions in any other currency are named in other_currencies and left out of the four total_*_cents figures (they still count in position_counts); read them, each with its own currency, from the positions endpoint.

Portfolio values are recalculated periodically rather than on each request, so recent investments and valuation changes can take a few minutes to appear. as_of is the timestamp of the oldest calculation included in the response.

current_value_cents equals cost_basis_cents until a later priced round, a recorded valuation mark, or an exit marks the position. Most active positions in a young portfolio read at cost, and that is the correct value, not missing data. In sandbox, the seeded investor's growth deal carries a 1.5× mark so you can see the field move; every other seeded position reads at cost.

Get portfolio summary​

GET /portfolio
curl "https://api.wefunder.com/portfolio" \
-H "Authorization: Bearer USER_ACCESS_TOKEN"
{
"data": {
"type": "portfolio_summary",
"attributes": {
"currency": "usd",
"other_currencies": [],
"total_cost_basis_cents": 915000,
"total_current_value_cents": 1499264,
"total_unrealized_gain_cents": 264264,
"total_realized_gain_cents": 320000,
"return_multiple": "1.6385",
"position_counts": { "active": 2, "exited": 1, "sold": 0, "failed": 0 },
"as_of": "2026-07-26T03:15:00Z"
}
}
}

The summary accepts the same status and company query parameters as the positions endpoint, to total a slice of the portfolio — for example, ?status=exited for exited positions only.

List portfolio positions​

GET /portfolio/positions

One position per offering (fundraise), newest first, cursor-paginated. The position sums everything the investor holds in that offering; securities breaks the total down.

curl "https://api.wefunder.com/portfolio/positions?per_page=25" \
-H "Authorization: Bearer USER_ACCESS_TOKEN"
{
"data": [
{
"id": "ofr_ozbvZ63cOy0CFao8Nv3SeEVU",
"type": "portfolio_position",
"attributes": {
"offering_id": "ofr_ozbvZ63cOy0CFao8Nv3SeEVU",
"company": {
"id": "co_2bt9eJn6X8C9gnShl63qlzkK",
"name": "Acme Robotics",
"slug": "acme-robotics",
"url": "https://wefunder.com/acme-robotics"
},
"asset_type": "company",
"security": { "type": "safe", "label": "SAFE" },
"status": "active",
"exit_reason": null,
"exited_on": null,
"invested_at": "2025-03-14T00:00:00Z",
"currency": "usd",
"cost_basis_cents": 215000,
"current_value_cents": 289264,
"unrealized_gain_cents": 74264,
"realized_gain_cents": 0,
"return_multiple": "1.3454",
"shares_held": "200.0",
"current_share_price": "14.4632",
"securities": [
{
"security": { "type": "safe", "label": "SAFE" },
"early_bird": true,
"terms": { "currency": "usd", "share_price": "10.0", "note_cap_cents": 800000000, "uncapped": false, "note_discount": null, "premoney_valuation_cents": null },
"cost_basis_cents": 100000,
"current_value_cents": 144632,
"unrealized_gain_cents": 44632,
"realized_gain_cents": 0,
"shares_held": "100.0",
"current_share_price": "14.4632",
"invested_at": "2025-03-14T00:00:00Z",
"owner": { "kind": "individual" }
},
{
"security": { "type": "safe", "label": "SAFE" },
"early_bird": false,
"terms": { "currency": "usd", "share_price": "12.0", "note_cap_cents": 1000000000, "uncapped": false, "note_discount": null, "premoney_valuation_cents": null },
"cost_basis_cents": 115000,
"current_value_cents": 144632,
"unrealized_gain_cents": 29632,
"realized_gain_cents": 0,
"shares_held": "100.0",
"current_share_price": "14.4632",
"invested_at": "2025-04-02T00:00:00Z",
"owner": { "kind": "entity", "name": "Blue Sky Holdings LLC" }
}
],
"holdings": [],
"as_of": "2026-07-27T14:02:11Z"
}
}
],
"meta": { "has_more": false, "page_count": 1 }
}

Query parameters​

ParameterTypeDescription
cursorintegernext_cursor from the previous page's meta. Omit for the first page.
per_pageintegerPage size, 1–100. Defaults to 25.
statusstringFilter by active, exited, sold, or failed.
companystringFilter to one company by its id (co_...). Unknown ids return 404.

Reading securities​

Each entry is one security offering held by one legal owner:

  • Multiple tiers — a round that sold early bird tiers alongside regular terms produces one entry per tier, each with its own terms (issue price, cap, discount).
  • Multiple legal owners — stakes held personally and stakes held through the investor's own entity (IRA, LLC) appear as separate entries, tagged "owner": {"kind": "individual"} or {"kind": "entity", "name": "..."}. The position-level numbers already sum all entries.

terms follows the same rules as an offering's security.terms. An uncapped SAFE or convertible note has "uncapped": true and a null note_cap_cents; a capped one has "uncapped": false and the cap. A security with no discount, including one recorded as zero, has a null note_discount. uncapped is null for securities that never carry a cap (equity, revenue share, debt).

Linking to the company​

company.url is the company's page on wefunder.com. Wherever you show a position, link it there: it is the link target the attribution rule asks for. The investor's Wefunder portfolio page is not a substitute, and you do not need to build the URL from slug.

Average cost per share​

cost_basis_cents is in integer cents, while shares_held, current_share_price and terms.share_price are decimal strings in major units of the position's currency. An average cost per share that compares with those prices needs the cents converted first, using decimal arithmetic (not floats):

average_cost_per_share = decimal(cost_basis_cents) / 100 / decimal(shares_held)

For the position above: 215000 cents / "200.0" shares = 1075 cents per share = 10.75 USD per share, against a current_share_price of "14.4632". Dividing cost_basis_cents by shares_held without the / 100 gives cents per share — 100× the prices shown — so label it as cents if you keep it unconverted.

Skip the calculation when shares_held is null or zero. A revenue share or debt security, or a position whose latest valuation is not per-share, has no average cost per share: report it as unavailable rather than coercing the missing share count to a number or dividing by zero. The same applies per entry in securities, whose shares_held can be null even when the position-level value is not.

Funds, SPVs, and exits​

  • asset_type: "fund" marks SPV and fund vehicles. The companies the vehicle invested in appear in holdings; some older vehicles have no recorded holdings and return an empty array.
  • status: "sold" means the stake was transferred away in a secondary sale; proceeds show in realized_gain_cents.
  • exited_on is the date the position ended (YYYY-MM-DD), set with exit_reason on exited and failed positions. Older exits recorded before Wefunder kept exit dates have an exit_reason and a null exited_on.
  • exit_reason (ipo, acquisition, buyback, failed, winding_down) says why a position ended. It is set only for exited and failed positions — a sold position ended with the transfer itself — and some older positions have no recorded ending. ipo means the company went public — it does not mean proceeds have been distributed. realized_gain_cents reflects actual distributions.
  • shares_held and current_share_price are null when the investment is not share-denominated (revenue share, debt) or the latest valuation is not per-share; current_value_cents is always present.