greencalculus-com Factors API
Emission-factor lookup and browse
Emission-factor lookup and browse
Every API here is available over the APIs.io API and to AI agents over MCP.
One button, every client — Claude, Cursor, VS Code and the rest.
https://apis.io/mcp
find_apisBrowse and filter every API in the catalog.get_api_artifactsOne API's artifacts, grouped by type.get_openapiThe primary OpenAPI for this API.find_similar_apisAPIs that look like this one.apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.resolveTurn a domain, URL or GitHub org into the provider it belongs to.find_cohortsEvery scored population of providers in the catalog.curl "https://apis.io/api/v1/apis/greencalculus-com:greencalculus-com-factors-api"
curl "https://apis.io/api/v1/apis?limit=25"
Discovery needs no key. Ratings and market analysis are Pro.
Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.
A second provider on the same verified email joins the account you already have.
openapi: 3.2.0
info:
title: GreenCalculus Factors API
version: v1
description: 'Sourced greenhouse-gas emission factors and audit-traced carbon calculations. Every value returns with its exact source reference and data version — reproducible and citable. Free key at https://greencalculus.com/developers/.
SCOPE: this document describes every endpoint we publish a callable example for — the data, calculation, discovery and watch endpoints. It deliberately excludes the control plane behind greencalculus.com (billing, sign-in, key management, admin), which is not a customer contract and is free to change, and /v1/account/webhook, whose configuration is session-only by design and therefore cannot be exercised with the bearer token this document describes. Those exclusions are enumerated and enforced by scripts/check-openapi-drift.mjs, so an endpoint is absent here because it was excluded, never because it was forgotten.'
contact:
name: GreenCalculus
url: https://greencalculus.com/developers/
email: jeremiah@greencalculus.com
license:
name: Attribution required except on Enterprise
url: https://greencalculus.com/developers/docs/#attribution
servers:
- url: https://api.greencalculus.com
description: Production
security:
- bearerAuth: []
tags:
- name: Factors
description: Emission-factor lookup and browse
paths:
/v1/health:
get:
tags:
- Factors
summary: Service health
security: []
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/Health'
operationId: getV1Health
x-operation-id-source: derived
/v1/factors/{key}:
get:
tags:
- Factors
summary: Resolve one factor by canonical key (value + full provenance)
parameters:
- name: key
in: path
required: true
schema:
type: string
example: grid.gbr.electricity.location_based
- name: as_of
in: query
required: false
schema:
type: string
example: '2026.175'
description: 'Serve this factor as it stood at an ARCHIVED data version. On a hit you get the archived row, served_version is the version you asked for, and version_pin reports matched: true, reproducible: true. Two misses, and they behave differently. (1) The version is NOT in the archive: 200 carrying the CURRENT row, with version_pin.matched: false naming as_of_requested and listing every archived version available — unlike POST /v1/calculate/*?as_of=, which 400s instead. So read version_pin before treating a response as pinned; do not infer it from the status code. (2) The version IS archived but holds no row for this key — it did not exist yet, or was withdrawn at or before it: 404 not_found, naming the key and the version, and saying whether the key is in the CURRENT corpus so a real absence is distinguishable from a mistyped key. GET /v1/factors/{key}/history lists every version that recorded it. Case (2) returned 200 with the current value in a browse envelope until 2026-09-10; it now refuses, because serving a current value under a past version label is the one answer this endpoint must never give.'
security:
- bearerAuth: []
responses:
'200':
description: Factor with value, source id, source reference, data version
content:
application/json:
schema:
$ref: '#/components/schemas/FactorLookup'
'401':
description: Missing/invalid key
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'404':
description: Unknown key, or a key with no row at the requested ?as_of= version
content:
application/json:
schema:
$ref: '#/components/schemas/FactorMiss'
operationId: getV1FactorsByKey
x-operation-id-source: derived
/v1/factors/{key}/history:
get:
tags:
- Factors
summary: Version history — what this factor has been
description: Every version the reproducibility archive holds for one key, with the value each version served and whether it was a change or a withdrawal. Derived from the same per-key archive index and the same read that answers ?as_of=, so it cannot disagree with the archive. Requires a key (any tier). History begins at the delta watershed; a short list means a stable factor, and the payload says so.
parameters:
- name: key
in: path
required: true
schema:
type: string
example: grid.gbr.electricity.location_based
responses:
'200':
description: Current row + per-version history entries
content:
application/json:
schema:
$ref: '#/components/schemas/FactorHistory'
'401':
description: Missing or invalid API key
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'404':
description: Key unknown to both the corpus and the archive
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
operationId: getV1FactorsByKeyHistory
x-operation-id-source: derived
/v1/factors:
get:
tags:
- Factors
summary: Browse/search the corpus (open — no key, values included)
parameters:
- name: search
in: query
schema:
type: string
example: UK electricity
description: Free text. Every whitespace-separated word must appear in the row name or canonical key (dots/underscores/hyphens read as separators, so a pasted canonical key finds its own row). Results are relevance-ranked.
- name: section
in: query
schema:
type: string
example: grid
- name: key_prefix
in: query
schema:
type: string
example: grid.gbr
description: Anchors at the START of the dotted key — "electricity" matches nothing; use search for words appearing anywhere.
- name: family
in: query
schema:
type: string
example: grid-electricity
description: Taxonomy family slug (hyphenated). An unknown value returns 200 with an empty result and the valid families listed in meta.warnings.
- name: limit
in: query
schema:
type: integer
default: 100
maximum: 500
- name: offset
in: query
schema:
type: integer
- name: cursor
in: query
schema:
type: string
description: base64(canonical_key) from meta.next_cursor. Preferred over offset — stable across data-version bumps, and required for paging relevance-ordered search results.
security: []
responses:
'200':
description: Matching factor keys, names, sections. meta.warnings explains any filter that matched nothing.
content:
application/json:
schema:
$ref: '#/components/schemas/FactorList'
'400':
description: 'gc_unknown_param — a query parameter this endpoint does not accept. The message names the offender and lists the accepted set. Other 400s: gc_limit_too_large, gc_offset_invalid, gc_cursor_invalid, gc_section_conflict.'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
operationId: getV1Factors
x-operation-id-source: derived
components:
schemas:
Error:
type: object
description: The single error envelope, on every non-2xx response from every endpoint. Stated on /developers/docs and enforced at the gateway for origin-raised errors too — read the code from error.code.
required:
- error
additionalProperties: true
properties:
error:
type: object
required:
- code
- message
additionalProperties: true
properties:
code:
type: string
description: 'Stable machine-readable code. Transport-level: unauthorized, forbidden_tier, not_found, rate_limited, quota_exceeded, overage_cap_reached, busy, origin_unavailable, invalid_request. Validation codes are namespaced gc_* and named per endpoint (gc_unknown_param, gc_limit_too_large, gc_offset_invalid, gc_cursor_invalid, gc_section_conflict, and the per-engine gc_<engine>_* set).'
example: gc_unknown_param
message:
type: string
description: Human-readable and specific — a 4xx names the offending parameter and lists the accepted set.
example: 'Unknown query parameter: bogus. This endpoint accepts: offset, cursor, limit, section, key_prefix, family, search. Free-text lookup is ?search=.'
docs:
type: string
format: uri
example: https://greencalculus.com/developers/docs/#errors
details:
type: object
additionalProperties: true
description: Present only when the failing endpoint attached structured detail beyond the HTTP status.
SourceRef:
type: object
description: Provenance to the line. Carried on 100% of rows — where in the source document the value was read, not just which document. A workbook cell where the publisher ships a workbook (DEFRA); otherwise the table, annex clause or page they did ship (IPCC, CBAM, ECCC).
additionalProperties: true
properties:
id:
type: string
example: DEFRA_2026
cell_ref:
type: string
example: '''UK electricity''!E25'
retrieved:
type: string
format: date
example: '2026-06-18'
FactorList:
type: object
description: Browse/search results. Open — no key, no quota. Values ARE returned on this route. Use /v1/factors/{key} when you want one row plus its audit envelope (provenance, licence, verification) or a version pin via ?as_of=.
required:
- meta
- factors
additionalProperties: true
properties:
meta:
type: object
additionalProperties: true
properties:
gc_version:
type: string
example: '2026.180'
gc_updated:
type: string
format: date
example: '2026-08-19'
total:
type: integer
description: Rows matching the filter.
example: 82
canonical_total:
type: integer
description: Rows in the whole corpus at this version.
example: 16554
returned:
type: integer
offset:
type: integer
next_cursor:
type:
- string
- 'null'
description: base64(canonical_key). Prefer this over offset — stable across data-version bumps, and required to page relevance-ordered search results.
filters:
type: object
additionalProperties: true
description: The filters as the endpoint understood them — echoed back so a silent mismatch is visible.
warnings:
type: array
items:
type: string
description: Why a filter matched nothing. A 200 with an empty array and a warning is the normal way this endpoint says "no such thing".
licences:
type: object
description: One record per DISTINCT source on this page, keyed by source id — so the licence detail is carried once rather than repeated on every row. Join via factors[].source.id. `attribution` is present ONLY where the licence grants redistribution and requires a credit; its absence on a non-redistributable row is deliberate.
additionalProperties:
type: object
additionalProperties: true
properties:
name:
type: string
redistributable:
type: boolean
basis:
type:
- string
- 'null'
publisher:
type:
- string
- 'null'
url:
type:
- string
- 'null'
attribution:
type: string
factors:
type: array
items:
$ref: '#/components/schemas/FactorRow'
FactorCitation:
type: object
description: 'The canonical citation for one factor row, built by the API (src/citation.ts) and printed VERBATIM by every official client (Sheets add-in, JS and Python SDKs, MCP). Format: "<factor name>. <source name> — <publisher>, cell <cell_ref>, retrieved <date>. via GreenCalculus data version <version>, factor <key>. <proof URL>". On GET /v1/factors/{key} the version is the one actually SERVED, so a ?as_of= lookup cites the pin. Present on rows from 2026-09-09. The format is a published specification, free for anyone to implement against their own data: https://greencalculus.com/developers/citation/'
externalDocs:
description: The factor citation specification
url: https://greencalculus.com/developers/citation/
additionalProperties: true
properties:
text:
type: string
example: UK grid electricity — location-based (generation). UK Government GHG Conversion Factors 2026 — Department for Energy Security and Net Zero (DESNZ), cell 'UK electricity'!E25, retrieved 2026-06-18. via GreenCalculus data version 2026.186, factor grid.gbr.electricity.location_based. https://verify.greencalculus.com/grid.gbr.electricity.location_based@2026.186
source_id:
type:
- string
- 'null'
example: DEFRA_2026
cell_ref:
type:
- string
- 'null'
example: '''UK electricity''!E25'
version:
type:
- string
- 'null'
example: '2026.186'
proof_url:
type:
- string
- 'null'
example: https://verify.greencalculus.com/grid.gbr.electricity.location_based@2026.186
ProofUrls:
type: array
items:
type: string
description: Every factor used by this answer, as a public proof URL pinned to the data version. A portfolio calculation may list many; cite per line from here. Contains no request data — each URL is a fact about the GreenCalculus corpus and is identical for every caller.
Attribution:
type:
- object
- 'null'
description: The credit the licence requires, or null on a plan that waives it. Was a bare STRING before 2026-08-25; the original string is preserved unchanged at `text`. `html` is a ready-to-paste anchor so that complying is less work than not complying, and `url` points at the public proof page for the exact factor and data version this answer used — your own audit evidence, not a marketing link.
properties:
text:
type: string
description: The plain credit line. Identical to the string this field used to be.
html:
type: string
description: A single anchor, no classes and no styling, ready to paste.
url:
type:
- string
- 'null'
description: 'Proof page for the factor this answer used, pinned to the data version — e.g. https://verify.greencalculus.com/grid.gbr.electricity.location_based@2026.181. Null where no single factor identifies the answer. The version is deliberate: an unversioned link would silently re-point at whatever the figure becomes.'
required:
type: boolean
description: Whether this plan must display the credit. Reports the licence position, not a technical control.
Uncertainty:
type:
- object
- 'null'
additionalProperties: true
description: The publisher's own stated range, never derived by us. null means NOT RECORDED — explicitly not a claim that no uncertainty exists.
properties:
low:
type:
- number
- 'null'
high:
type:
- number
- 'null'
basis:
type:
- string
- 'null'
example: published
published_as:
type:
- string
- 'null'
description: The publisher's exact wording, e.g. "±30-50%".
FactorRow:
type: object
description: 'One corpus row as returned by browse/search. Carries the full row INCLUDING factor.value — the browse endpoint is open and unmetered by design, because our data is overwhelmingly open-licensed at source and gating it would make this API worse than the original publisher. What a paid plan adds is the right to REDISTRIBUTE, not the ability to read. (Note: the MCP search_factors tool DOES project rows down to key/name/section — that projection is deliberate and specific to that tool.)'
additionalProperties: true
properties:
key:
type: string
example: grid.gbr.electricity.location_based
name:
type:
- string
- 'null'
section:
type:
- string
- 'null'
example: grid
activity_type:
type:
- string
- 'null'
example: purchased_energy
factor:
type: object
additionalProperties: true
properties:
value:
type:
- number
- 'null'
unit:
type:
- string
- 'null'
example: kg CO2e per kWh
gas:
type:
- string
- 'null'
example: CO2e
basis:
type:
- string
- 'null'
gwp_set:
type:
- string
- 'null'
example: AR5_100
gwp_set_basis:
type:
- string
- 'null'
description: How we know the GWP set — "published" where the source states it, "inferred" where we deduced it. null means not recorded, which is deliberately NOT the same as published.
country_iso3:
type:
- string
- 'null'
example: gbr
uncertainty:
$ref: '#/components/schemas/Uncertainty'
uncertainty_basis:
type:
- string
- 'null'
description: '"none_published" means the publisher states plainly that no range exists. null means not recorded — a different fact from silence.'
source:
$ref: '#/components/schemas/SourceRef'
scope:
type:
- object
- 'null'
additionalProperties: true
properties:
ghg_protocol:
type:
- string
- 'null'
example: scope2
category:
type:
- string
- 'null'
components:
type:
- object
- 'null'
description: Per-gas decomposition keyed by gas (co2, ch4, n2o…), each with its own cell_ref. Lets a consumer re-base AR5↔AR6 without us.
additionalProperties:
type: object
additionalProperties: true
note:
type:
- string
- 'null'
description: Includes what the publisher did NOT provide, stated verbatim.
citation:
$ref: '#/components/schemas/FactorCitation'
licence:
type: object
additionalProperties: true
description: 'The licence this row is published under, and whether the Business redistribution grant covers it. Present on the open browse from MB v2026.181 — the full terms, the required credit and the publisher are listed once per source under meta.licences. Absent (not empty) where no licence is on record: an empty value would read as "no licence applies".'
properties:
name:
type: string
example: Open Government Licence v3.0
redistributable:
type: boolean
Verification:
type: object
description: What was actually checked — computed at serve time, not asserted.
additionalProperties: true
properties:
components_sum_to_value:
type:
- boolean
- 'null'
description: Whether the per-gas components sum to the headline. null where the row carries no components — the absence of a check is not a failed check.
gwp_set:
type:
- string
- 'null'
gwp_set_basis:
type:
- string
- 'null'
uncertainty:
$ref: '#/components/schemas/Uncertainty'
uncertainty_basis:
type:
- string
- 'null'
source_note:
type:
- string
- 'null'
FactorHistory:
type: object
additionalProperties: true
properties:
key:
type: string
current:
type:
- object
- 'null'
additionalProperties: true
description: null when the key is withdrawn from the live corpus.
history:
type: array
items:
type: object
additionalProperties: true
properties:
version:
type: string
date:
type:
- string
- 'null'
value:
type:
- number
- 'null'
unit:
type:
- string
- 'null'
kind:
type: string
enum:
- recorded
- withdrawn
meta:
type: object
additionalProperties: true
FactorLookup:
type: object
description: One factor with its value and the full audit envelope. Served from the edge KV store.
additionalProperties: true
properties:
meta:
type: object
additionalProperties: true
value:
type:
- number
- 'null'
description: Convenience top-level copy of factor.factor.value.
unit:
type:
- string
- 'null'
gas:
type:
- string
- 'null'
factor:
$ref: '#/components/schemas/FactorRow'
description: The full row. Its `citation` is built at served_version, so a pinned lookup cites the pin.
provenance:
$ref: '#/components/schemas/Provenance'
verification:
$ref: '#/components/schemas/Verification'
served_from:
type: string
example: edge
served_version:
type: string
example: '2026.180'
version_pin:
type: object
additionalProperties: true
description: 'Present only when ?as_of= was sent — and not even then if the key did not exist at an archived version you asked for, which falls back to the browse envelope instead. `matched: false` means the requested version is not in the archive and the CURRENT value was served — check it before treating a response as pinned.'
attribution:
$ref: '#/components/schemas/Attribution'
proof_urls:
$ref: '#/components/schemas/ProofUrls'
FactorMiss:
type: object
description: A key that does not exist. Carries candidate keys rather than a bare 404 — most misses are a naming difference, not a coverage gap. The documented error envelope is present alongside the bespoke fields.
additionalProperties: true
properties:
error:
$ref: '#/components/schemas/Error'
found:
type: boolean
example: false
requested_key:
type: string
reason:
type: string
suggestions:
type: array
items:
type: object
additionalProperties: true
properties:
key:
type: string
name:
type:
- string
- 'null'
section:
type:
- string
- 'null'
docs:
type: string
format: uri
Provenance:
type: object
description: Where the number came from and whether you may republish it. Built from the source registry — src/envelope.ts.
additionalProperties: true
properties:
source_id:
type:
- string
- 'null'
source_name:
type:
- string
- 'null'
publisher:
type:
- string
- 'null'
description: The organisation, in one spelling (MB `publisher`).
issuer:
type: string
description: The issuing descriptor of this edition (MB `body`), present only when it differs from publisher.
url:
type:
- string
- 'null'
cell_ref:
type:
- string
- 'null'
retrieved:
type:
- string
- 'null'
format: date
licence:
type:
- string
- 'null'
redistributable:
type:
- boolean
- 'null'
description: Whether the Business redistribution grant covers this row. Fails closed — a source whose licence has not been assessed is false, not null.
redistributable_basis:
type:
- string
- 'null'
update_cadence:
type:
- string
- 'null'
overrides_source:
type: object
description: Present only when this row carries a licence its source does not (a publisher republishing third-party data under its own terms, e.g. CBAM Annex II). licence, redistributable and redistributable_basis above are then the ROW's; this block keeps the source's own answer beside them.
properties:
source_redistributable:
type:
- boolean
- 'null'
since:
type:
- string
- 'null'
description: Data version at which the row-level answer was recorded.
attribution_required:
type:
- string
- 'null'
source_ref:
type:
- string
- 'null'
description: Where the restricting text was read.
Health:
type: object
additionalProperties: true
properties:
ok:
type: boolean
status:
type: string
securitySchemes:
bearerAuth:
type: http
scheme: bearer
description: Your API key, e.g. "Bearer gc_live_…". Get one free at https://greencalculus.com/developers/.
externalDocs:
description: The factor citation specification — the line that travels with every number
url: https://greencalculus.com/developers/citation/