Portfolios
A portfolio is a group of companies that one company, the portfolio’s owner, reports on together, such as a parent company and its subsidiaries. Each member has an inclusion share: how much of its emissions count towards the portfolio.
Both operations need the portfolio:read scope, and take the owner’s company id in the path.
List portfolios
GET /v3/company/{companyId}/portfolios returns every portfolio the company owns, oldest first, with its member companies: the parent first, then by name.
curl
curl --max-time 60 "https://api.energi.ai/v3/company/1042/portfolios" \
-H "Authorization: Bearer $ENERGI_API_KEY"curl
curl --max-time 60 "https://api.us.energi.ai/v3/company/1042/portfolios" \
-H "Authorization: Bearer $ENERGI_API_KEY"{
"data": [
{
"id": "189",
"name": "Energi.AI Group",
"companies": [
{
"companyId": "1042",
"name": "Energi.AI AS",
"orgNumber": "927611074",
"inclusionPercent": 100,
"dataSharingConsent": "ACTIVE"
},
{
"companyId": "2311",
"name": "Example Subsidiary AS",
"orgNumber": "900000001",
"inclusionPercent": 60,
"dataSharingConsent": "MISSING"
}
]
}
],
"meta": { "nextCursor": null }
}inclusionPercentis the share of the member’s emissions included in the portfolio, from 0 to 100.dataSharingConsentsays whether the member has granted the owner data-sharing consent:ACTIVEorMISSING. The owner itself is alwaysACTIVE. Members withMISSINGare left out of the portfolio’s emissions, so this list tells you in advance who will be.meta.nextCursoris alwaysnulltoday: the list isn’t paginated yet (see Lists).
Read a portfolio’s emissions
GET /v3/company/{companyId}/portfolios/{portfolioId}/emissions/summary returns the portfolio’s total emissions and each included member’s contribution, on the same grid of periods, scopes and categories as a company summary. It takes the same from, to, granularity and emissionsUnit, with the same rules, plus a required reportType.
Report types
reportType | Meaning |
|---|---|
PORTFOLIO | Each member’s emissions, multiplied by its inclusionPercent, added up. |
CONSOLIDATION | The same, without intra-group transactions: data points whose vendor’s organisation number belongs to any company in the portfolio are left out, in every direction. This avoids counting the same emissions twice when members buy from each other. |
curl
curl --max-time 60 -G "https://api.energi.ai/v3/company/1042/portfolios/189/emissions/summary" \
-H "Authorization: Bearer $ENERGI_API_KEY" \
-d from=2025-01 -d to=2025-12 -d granularity=QUARTER -d emissionsUnit=t \
-d reportType=CONSOLIDATIONcurl
curl --max-time 60 -G "https://api.us.energi.ai/v3/company/1042/portfolios/189/emissions/summary" \
-H "Authorization: Bearer $ENERGI_API_KEY" \
-d from=2025-01 -d to=2025-12 -d granularity=QUARTER -d emissionsUnit=t \
-d reportType=CONSOLIDATIONThe response, with periods shortened:
{
"data": {
"companyId": "1042",
"portfolio": { "id": "189", "name": "Energi.AI Group" },
"reportType": "CONSOLIDATION",
"emissionsUnit": "tCO2e",
"granularity": "QUARTER",
"from": "2025-01",
"to": "2025-12",
"emissions": 4821.55,
"periods": [],
"companies": [
{
"companyId": "1042",
"name": "Energi.AI AS",
"orgNumber": "927611074",
"inclusionPercent": 100,
"emissions": 4821.55,
"periods": []
}
],
"excludedCompanies": [
{ "companyId": "2311", "reason": "NO_DATA_SHARING_CONSENT" }
]
},
"meta": { "status": "UPDATING", "asOf": "2026-09-16T09:12:04.000Z" }
}data.periodshas the same shape as in a company summary, for the portfolio as a whole. In a real response it is never empty.companieslists every included member, the parent first, then by name. Each one’semissionsandperiodsare its contribution, afterinclusionPercent.excludedCompanieslists the members left out of every figure, with thereason. Today the one reason isNO_DATA_SHARING_CONSENT; accept others you don’t know. Both lists are[]when empty.- A portfolio without members returns the grid with every value
null. - A portfolio id that doesn’t exist, or belongs to another company, is a
404 NOT_FOUND.
Freshness
meta works as for a company summary, across all included members: status is UPDATING if any of them is still being calculated, and asOf is then the oldest asOf among those. A member that isn’t updating reflects every change, so it never holds asOf back. When none is updating, asOf is the newest among them.