Categorising and calculating spend-based emissions
Introduction
One of CarbonAPI's main features is the ability to categorise arbitrary expense lines, have them categorised into an emissions category.
For example, consider the following expense line item:
Date: 05/05/2019
Supplier: Air New Zealand
Description: Company retreat flights
Subtotal: 499.00
Currency: NZDThere are a few steps required to turn this into an emissions estimate:
- Understand the context of the expense
- Match the expense to a taxonomy, for example:
urn:ef:spend:commodity:transport-postal-warehousing:scheduled-passenger-air-transport - Search for an appropriate emission factor, taking into account reporting needs
- Apply inflationary and potentially FX calculations
- Depending on the emission factor, either subtract margins from the final price or use the final purchaser price, multiply by the emission factor, and you have your answer!
CarbonAPI handles all of these for you. Let's try with a practical example.
Step-by-step guide
First, we need to sign up for CarbonAPI. You can do this and get free evaluation credits by heading to the Account Portal and signing up.
For this guide, we'll use polling to check for the results of our call, but to reduce overhead, we strongly reccomend using our Webhooks approach, which allows you to be notified once processing has completed.
Now, you can either review our API Reference, or drop in our TypeScript SDK, which will allow you to get started quickly.
Okay, so we're ready to start making calls. Let's make some some expenses to get started with:
import { CarbonAPIClient } from "@carbonapi/typescript-sdk";
// Initialize the client with your API key
const client = new CarbonAPIClient({
apiKey: "your-api-key-here", // Get this from the Account Portal
});
// Create a batch of transactions
const { batchIds } = await client.createTransactionBatch({
transactions: [
{
id: "123", // Important - we'll use this later to match our response to your transaction.
date: "2025-05-13T03:52:52Z",
tax: 10,
total: 100,
subtotal: 90,
description: "Purchase of new laptop",
supplierName: "Mighty Ape",
sourceAccount: "Office Expenses",
currency: "NZD",
},
],
countryCode: "NZL",
factorClass: "commodity",
});Important
See the Create Transaction
Batch for the full
type requirements, but the most important thing to note here is that the id
parameter must be unique within a batch, as this is what is used to match
up to results later.
Okay, so we have now created a categorisation job. At the time of writing, only an asynchronous approach is supported, but we plan to add sync calls in the future.
Now, let's poll for changes in the batch. As mentioned, this is not the preferred approach, we strongly suggest using Webhook calls to reduce the complexity of your application, this is just for demo purposes.
Note, this is queue-based service. Your request will be processed as soon as possible, but you may need to wait for a few minutes when we're busy. Again, a good reason to use Webhooks!
const poll = setInterval(() => {
// Remember don't do this in production!
const batchId = transactionBatchResponse.batchIds[0];
const transactionBatch = await client.getTransactionBatch(batchId);
if (transactionBatch.status === "Completed") {
console.log(transactionBatch.transactions); // This will contain your categories and emissions information.
clearInterval(poll); // Don't forget to do this.
}
}, 10_000); // Call every 10 seconds.Supplier-specific factors
CarbonAPI contains over 10,000 custom emission factors for various leading worldwide brands, with more being added regularly. By default, if we directly match a supplier name to one in our data set, we'll return a custom factor that will likely improve the accuracy of your reporting.
You can control this behaviour in two ways:
Require, prefer or ignore supplier factors
The optional parameter supplierFactorPolicy, sent when creating a batch, dictates how CarbonAPI applies supplier factors to your results.
PREFER_SUPPLIER_FACTOR: If there is a matching supplier factor, we will use it for you. If we cannot find a supplier factor, we will attempt to find an emission factor that matches your expense.
REQUIRE_SUPPLIER_FACTOR: Require that a supplier factor is present. If not, we will return an error. Please note, you will still be charged even if we cannot find a factor for you.
IGNORE_SUPPLIER_FACTOR (default): Do not attempt to match to supplier factors.
Require a certain level of assurance
You may want to ensure that the factors that CarbonAPI uses adhere to your own policies regarding assurance. We capture three levels of assurance in our factor set - Reasonable, Limited, and Unknown.
The optional parameter, supplierFactorAssurancePolicy, controls the behaviour of CarbonAPI:
NO_ASSURANCE_PREFERENCE (default): Do not specify a required level of assurance.
REQUIRE_REASONABLE_ASSURANCE: Require that supplier factors meet a reasonable level of assurance. If used in conjunction with REQUIRE_SUPPLIER_FACTOR, and our factor set cannot find a factor that meets this level, we will return an error. Note you will still be charged for this.
REQUIRE_LIMITED_ASSURANCE: Require that supplier factors meet a limited level of assurance. If used in conjunction with REQUIRE_SUPPLIER_FACTOR, and our factor set cannot find a factor that meets this level, we will return an error. Note you will still be charged for this.
Emission Factor Heirarchy
CarbonAPI will select the most specific factor that aligns to our taxonomy. We follow a built-in heirarchy to ensure the factors chosen are the most specific possible for a given country and activity.
As of the time of writing, the heirarchy is as follows (lowest numbers have priority):
- Emission factors specific to a given country (e.g.
thinkstep(NZ),footprintlab(AU)) - Generic factor sets with specific factors close to the taxonomy (e.g.
exiobase,gloria) - Generic factor sets with generic/broad factors that match the taxonomy (e.g.
gloria,gloria)
In the future you will be able to request a specific dataset and dataset version. As of now, we select the most recent factor that corresponds to the above heirarchy.
Inflationary Adjustments
We automatically perform FX calculations and CPI adjustments on the data you pass us. Details of these adjustments are available in the "steps" array.
Foreign Exchange calculations are performed based on the transaction date, and convert the transaction currency to the closest appropriate emission factor currency for the batch country code.
Inflation adjustment calculations are based on the World Bank's CPI index data for the Transaction year and the emission factor sourceDataYear, for the batch country code.
If CPI index data is not available for those exact years, we will use the closest previous year's data. In the rare event that either date predates all available CPI index data, we will use the earliest available index.
Details of which years, country and index values were used are provided in the CPI_ADJUSTMENT step block.
Inflation adjustment is calculated as:
AdjustedAmount = originalAmount * (factorYearCPI / transactionYearCPI)In the future you will be able to opt-out of this calculation, but that is not currently supported.
Understanding the results
Good to know
CarbonAPI is powered by AI, which can sometimes make mistakes when categorising expenses. It is important that you review the outputs of this service.
Let's take a look an example expense result.
The top level object has a status and an array of the transactions in the batch.
{
"id": "12345",
"status": "Completed",
"createdAt": "2025-12-07T21:40:22.847Z",
"updatedAt": "2025-12-07T21:41:12.382Z",
"transactions": []
}The status will be one of "Pending", "Processing", "Completed" or "Error". Each transaction object will look something like the following.
{
"id": "8a18a14a-70fb-4b70-ab49-9bdd3f409c76",
"taxonomy": {
"status": "SUCCESS",
"name": "Manufacture of computers and peripheral equipment",
"urn": "urn:ef:spend:commodity:manufacturing:manufacture-of-computers-peripheral-equipment",
"sicCode": "26200",
"confidence": 0.9996292591094971
},
"emissionFactor": {
"status": "SUCCESS",
"source": "EXIOBASE",
"sourceUrl": "https://zenodo.org/record/5589597",
"sourceVersion": "3.8.2",
"sourceDataYear": 2019,
"valuationMethod": "BP",
"lcaActivity": "cradle_to_shelf",
"scope": 3,
"externalLocator": "office_machinery_and_computers",
"matchType": "PreviousYear",
"notes": null,
"factorCurrency": "EUR"
},
"measurement": {
"status": "SUCCESS",
"co2e": 7.045340982501475,
"constituents": {
"co2": 0,
"n2o": 0,
"ch4": 0,
"scope1": 0,
"scope2": 0,
"scope3": 0
}
},
"steps": [
{
"id": "PREDICT_CATEGORY",
"meta": {
"predictionMethod": "AI",
"alternativeCategories": [
{
"urn": "urn:ef:spend:commodity:information-media-telecommunications:online-software-subscriptions-streaming-services",
"confidence": 0.0004971720045432448
},
{
"urn": "urn:ef:spend:commodity:manufacturing:manufacture-of-office-machinery",
"confidence": 0.00006632102304138243
},
{
"urn": "urn:ef:spend:commodity:other-services:repair-of-computers-peripheral-equipment",
"confidence": 0.00003219033897039481
},
{
"urn": "urn:ef:spend:commodity:administrative-support-services:photocopying-document-preparation-other-specialised-office-support",
"confidence": 0.00002649668022058904
}
]
}
},
{
"id": "BASIC_PRICE_ADJUSTMENT",
"meta": "Basic Price factors use the subtotal, excluding taxes, as the quantity base. No other margins are applied"
},
{
"id": "FOREIGN_EXCHANGE",
"meta": {
"note": "FX conversion applied: NZD -> EUR at rate 0.5401446291258948 for 2021-01-01. ",
"source": "exchangeratesapi.io",
"sourceURL": "https://api.exchangeratesapi.io/v1/",
"exchangeRate": 0.5401446291258948,
"factorCurrency": "EUR",
"expenseCurrency": "NZD"
}
},
{
"id": "CPI_ADJUSTMENT",
"meta": {
"note": "Inflation adjustment applied using closest available data: country: NZL, factor year 2023 (CPI index 136.86287449004), transaction year 2020 (CPI index 116.19964268334), correction factor 1.178.",
"source": "World Bank",
"sourceURL": "https://data.worldbank.org/indicator/FP.CPI.TOTL",
"countryCode": "NZL",
"factorYear": 2023,
"factorIndex": 136.86287449004,
"transactionYear": 2020,
"transactionIndex": 116.19964268334,
"correctionFactor": 1.177825261158592
}
}
]
}Output objects and descriptions
id - This is the ID you sent in the createTransactionBatch call. Use this to match back up on your end.
taxonomy
This represents the predicted taxonomy from our dataset. You can explore our dataset and taxonomies at https://carbonapi.io/dataset.
taxonomy.status - whether we were able to predict a taxonomy.
taxonomy.name - the name of our taxonomy prediction.
taxonomy.urn - the unique taxonomy reference in our database
taxonomy.sicCode - the Standard Industry Classification code for this predicted taxonomy.
taxonony.confidence - a number beween 0 and 1 representing our confidence in this prediction relative to others in our dataset.
emissionFactor
If we were able to match a taxonomy, the next step is to choose an appropriate emission factor.
Good to know
While we have a taxonomy collection of over 600 spend categories, not all countries have an emission factor for them. This is often due to incomplete or missing data from our underluing providers.
Head to the dataset explorer to see if there are relevant emission factors for your country and use case.
emissionFactor.status - whether we were able to select an emission factor. One of SUCCESS or ERROR.
emissionFactor.source - the name of the underlying emission factor source, for example EXIOBASE.
emissionFactor.sourceUrl - the location of the source material. Useful for audit purposes.
emissionFactor.sourceVersion - the version of the dataset. At the time of writing we return from the most recent, though we may in the future allow you to select a specific version.
emissionFactor.sourceDataYear - the underlying source data year. We use this when calculating FX and CPI.
emissionFactor.valuationMethod - whether this emission factor is in basic prices (BP) or purchaser prices (PP).
emissionFactor.lcaActivity - the underlying LCA activity for this factor
emissionFactor.scope - a suggested GHG Protocol scope
emissionFactor.externalLocator - a string that you can use to match to underlying source data. Useful for audit purposes.
emissionFactor.matchType - whether this emissionFactor was in the same or a previous year (and therefore needed to be modelled)
emissionFactor.notes - any notes that are useful when implementing the results calculated from this emisison factor.
measurement
If we were able to find an emission factor, we will create a measurement.
measurement.status - whether we were able to create a measurement. One of SUCCESS or ERROR.
measurement.co2e - the calculated CO2e based on the emission factor and the incoming data. For basic prices, we use the subtotal that you provide, for purchaser prices, the total is used.
measurement.constituents - if we have them, we will share the minor cases and constituent scope 1, 2 and 3 proportions. Not available in all classifications/countries.
steps
We will also return a full audit trail for your audit purposes, showing the steps we went through to calculate the emissions for this expense.
Errors
When processing batches, you may encounter specific error scenarios. This section outlines common errors and standard system responses.
Supplier Match Failure
This error occurs when supplierFactorPolicy is set to REQUIRE_SUPPLIER_FACTOR but no matching supplier is found in our database.
{
"id": "1",
"taxonomy": {
"status": "ERROR",
"error": "Unable to find any matching supplier, and supplier factor policy prevents AI matching"
},
"emissionFactor": {
"status": "ERROR",
"error": "Unable to find any matching supplier, and supplier factor policy prevents AI matching"
},
"measurement": {
"status": "ERROR",
"error": "Unable to create a measurement without an emission factor"
},
"steps": []
}Billing Note
Usage credits will be deducted for this transaction attempt as the processing occurred as requested.
Categorisation Failure
This error occurs when the AI engine is unable to predict a suitable category from the taxonomy with sufficient confidence.
{
"id": "1",
"taxonomy": {
"status": "ERROR",
"error": "Unable to predict a URN with sufficient confidence"
},
"emissionFactor": {
"status": "ERROR",
"error": "Unable to source an emission factor without a prediction"
},
"measurement": {
"status": "ERROR",
"error": "Unable to create a measurement without an emission factor"
},
"steps": []
}Billing Note
Usage credits will be deducted for this transaction attempt.
Emission Factor Not Found
This error occurs when a category is predicted, but no corresponding emission factor exists for the requested country or region.
We are currently working to limit categorisation to available factors. In the meantime, you may receive the following response:
{
"id": "1",
"taxonomy": {
"status": "SUCCESS",
"urn": "urn:ef:spend:commodity:water-sewerage-waste-services:water-collection-treatment-supply",
"confidence": 0.9992782473564148
},
"emissionFactor": {
"status": "ERROR",
"error": "Could not find an emission factor with URN: urn:ef:spend:commodity:water-sewerage-waste-services:water-collection-treatment-supply, country: LBY"
},
"measurement": {
"status": "ERROR",
"error": "Unable to create a measurement without an emission factor"
},
"steps": []
}Billing Note
Usage credits will be deducted for this transaction attempt.
Internal Server Error
This status indicates an unexpected system failure unrelated to the provided transaction data.
{
"id": "12345",
"status": "Error",
"createdAt": "2025-12-07T21:40:22.847Z",
"updatedAt": "2025-12-07T21:41:12.382Z",
"transactions": []
}Billing Note
Usage credits will not be deducted for system errors.