Part 1: Substantive Insights

1. Overview

This dataset provides longitudinal financial data — balance sheet items, profit and loss aggregates, employee counts, and CIC34 community-impact narratives — for nonprofit companies registered at Companies House. It was produced by the UK Third and Civil Society Sector Database project, which collects, processes, and links public administrative data on civil society organisations across the United Kingdom.

The data is drawn from the annual accounts that nonprofit companies file at Companies House. Two complementary sources are used: structured XBRL filings (machine-readable accounts) and PDF accounts processed through a structured-output LLM extraction pipeline. The merged dataset gives broader coverage than either source alone, particularly for smaller filings such as Community Interest Company (CIC) abridged accounts.

The dataset spans financial years from 1981 to 2026 and contains 1,355,663 financial-year records linked to 218,296 nonprofit organisations. Each row represents one company’s accounts for a single financial year.

1,355,663Financial Records
218,296Linked Nonprofit Organisations
2Account Sources
1981–2026Financial Years

2. What are Nonprofit Financial Records?

Companies registered at Companies House are required to file annual accounts. The level of detail filed varies by company size — micro-entities and small companies file abridged or filleted accounts, while medium and large companies file full statutory accounts. CICs additionally file a CIC34 community-impact report alongside their accounts.

XBRL Accounts (Companies House monthly extracts)

Companies House publishes monthly bulk extracts of accounts filed in machine-readable XBRL format. The extracts contain structured tagged values for balance sheet items, profit and loss aggregates, and employee counts. These are downloaded from the Companies House accounts download site.

PDF Accounts (LLM-extracted)

For older filings and CIC abridged accounts that pre-date or were filed outside the XBRL stream, the project extracts financial line items from the original PDF accounts using a structured-output LLM pipeline. The extracted values are mapped onto the same column schema used for XBRL, so the merged dataset has a uniform shape regardless of source.

CIC34 Community Interest Reports

Community Interest Companies file a CIC34 alongside their accounts. The CIC34 contains four narrative sections: the company’s activities and impact, stakeholder consultation, directors’ remuneration, and any asset transfers. These narratives are extracted from the same PDF filings.


3. Dataset Contents

The published file has 62 columns, organised into the groups below. Each row represents one company-year record. Where a value is missing, the cell is left blank. Every column in the file is described in this section.

Identifiers and Time Period

FieldDescriptionType
uid The organisation’s identifier in the project Spine, and the key that links every row for the same organisation across years and sources (for example GB-COH-08411754, GB-CHC-1000019) Text
coyno Companies House registered number (8-digit, zero-padded) Text
entity_current_legal_name Company name as filedText
fy Financial year, taken as the calendar year of the financial year end. It is the year part of the row’s key together with uid.Numeric
period_end The end of the reporting period exactly as tagged in the filing. This is the raw field; fye is derived from it.Date
balance_sheet_date The date the balance sheet in the filing was drawn up to. Blank on a recovered comparative row (one with from_prior_year TRUE): the only balance-sheet date available there belongs to the donor filing a year later, not to the year the row reports on, so publishing it against the recovered year would invite exactly the misreading that the withdrawal of source_filing_date in v1.2.1 was meant to end. The donor is identified by prior_source_file and prior_source_fy instead. Date
fye_bs The balance-sheet date of the filing the row was read from, held as a date. It is the fallback for fye when the filing’s tagged period end is missing or unparseable, and it is how a current-year row is told apart from a comparative one (on a current-year row the two agree). Blank on a recovered comparative row, for the same reason as balance_sheet_date.Date
fye Financial year end. Taken from period_end where that is a usable date, otherwise from fye_bs. Where neither is available it is imputed: the company’s most common financial-year-end month and day are applied to the row’s fy year; if the company has no other filing to learn a month-day from, the month and day of balance_sheet_date are used instead. (29 February in a non-leap year becomes 28 February.) Rows with neither source are left blank.Date
fys Financial year start. This is derived, not filed. Within each organisation, sorted by fye, it is the previous financial year end plus one day — but only where that implies an ordinary annual period of 397 days (13 months) or less, which allows for a normal change of accounting reference date. Where a filing gap would otherwise produce a multi-year period, and for the first year of a company’s sequence, it falls back to fye minus one year plus one day. It is blank when fye is blank.Date

Because fys is derived rather than filed, do not treat it as evidence of the period the company actually reported on. Use it to place a record in time, and use fye or period_end when the exact reporting period matters.

Aggregate Financials

FieldDescription
turnover_gross_operating_revenueTotal turnover / operating income
other_operating_income Operating income reported separately from turnover — for example grants, donations, or investment income that the filer did not include in its turnover figure. It is an addition to turnover_gross_operating_revenue, not a component of it, so do not treat the two as alternatives for the same amount.
operating_profit_lossOperating profit or loss
profit_loss_for_periodProfit or loss for the period
Charity accounts use commercially-named columns. The column schema follows the Companies House commercial taxonomy, but charitable companies file under the Charities SORP taxonomy. Their SORP totals are mapped onto the nearest commercial column: char:TotalIncome (and char:TotalIncomingResources) into turnover_gross_operating_revenue, char:TotalExpenditure (and char:TotalResourcesExpended) into administrative_expenses, and char:FixedAssets into tangible_fixed_assets. So for a charity, “turnover” means total incoming resources, “administrative expenses” means total expenditure, and “tangible fixed assets” may include fixed assets that are not tangible. Read those three columns as sector-appropriate totals rather than by their commercial names, and use csotype to separate the populations before comparing them.

Balance Sheet

Balance-sheet line items including tangible_fixed_assets, debtors, cash_bank_in_hand, current_assets, creditors_due_within_one_year, creditors_due_after_one_year, net_current_assets_liabilities, total_assets_less_current_liabilities, net_assets_liabilities_including_pension_asset_liability, called_up_share_capital, profit_loss_account_reserve, and shareholder_funds.

Profit & Loss Detail

Detailed P&L items including cost_sales, gross_profit_loss, administrative_expenses, raw_materials_consumables, staff_costs, depreciation_other_amounts_written_off_tangible_intangible_fixed_assets, other_operating_charges_format2, profit_loss_on_ordinary_activities_before_tax, tax_on_profit_or_loss_on_ordinary_activities, wages_salaries, dividends_paid, and government_grant_income.

All monetary columns are signed: a negative value means a liability, a deficit, or a loss. Ten expense and liability columns are the exception on the PDF side, where they are stored as positive magnitudes; they are listed in Annex A3, which also explains why XBRL-sourced values keep the signs the filer used. One of the ten, tax_on_profit_or_loss_on_ordinary_activities, keeps its direction: a tax charge is positive and a tax credit is negative, because the two are different facts and collapsing them would turn a refund into a liability.

Staff

average_number_employees_during_period: average number of persons employed during the financial year.

What the Filer Declared

FieldDescription
company_dormant The filer’s own dormancy declaration for the period: TRUE where the accounts state the company was dormant, FALSE where they state it was not. It is blank when the filing does not say either way — blank is unknown, not “not dormant”. It is always blank on a row recovered from a following filing’s comparatives (from_prior_year TRUE). A dormancy declaration is made about the year the filing covers, so the donor’s declaration says nothing about the earlier year the recovered row reports on, and it is removed rather than carried across. On such a row, blank therefore carries no information about whether the company was dormant. Section 6 reports all-zero balance sheets separately for declared-dormant, declared-non-dormant, and unknown-dormancy rows for this reason.
principal_activities The principal-activities narrative from the directors’ report, as free text, where the filing carried one. Blank on most rows: micro-entity and filleted accounts frequently omit it.

CIC34 Narratives (CICs only)

Four free-text fields extracted from the CIC34 form: cic34_activities_impact, cic34_stakeholder_consultation, cic34_directors_remuneration, cic34_asset_transfer.

Where Each Row and Figure Came From

These columns describe the origin of the row and of the individual figures on it. They are a record of how the data was assembled, not measurements of the organisation. Use them to judge how much weight to put on a value.

FieldDescription
source_dataset Which stream the row was built from: xbrl for a machine-readable Companies House filing, or pdf_extraction for a PDF filing read by the extraction pipeline.
source_file, file_type, taxonomy The archive the row was read from, the filing format, and the accounting taxonomy the original document was tagged against.
run_code Which processing run produced the row. For an XBRL row this is the Companies House monthly extract it was parsed from (for example Prod224_2506); for a PDF row it is pdf_extraction_v1 or pdf_extraction_v2, naming which extraction pass supplied it.
company_id The company number exactly as it appeared in the source document, before it was normalised to the zero-padded coyno. Use coyno for joining; this column exists so an unexpected match can be traced back to what the document actually said.
from_prior_year TRUE when the row’s figures were taken from the comparative (prior-year) column printed in the following year’s filing, rather than from a filing made for that year itself. It covers two cases. (i) Comparative-period rows: the organisation made no filing of its own for that year — or none survives in the archives — so the year is reconstructed from the comparatives in the next filing. 111,100 rows in this release were recovered this way. (ii) Gap rows: a year that the organisation’s own filing sequence skips, filled from the same comparative evidence. FALSE for an ordinary row taken from a filing made for that year.

A TRUE row is a genuine observation of that year’s figures, but it is a secondary one: it was printed a year later, for comparison, and is more likely to be rounded, restated, or abbreviated than a primary filing.

What such a row does and does not carry. Figures that the donor filing tagged to the comparative period are kept, as are the period columns (period_end, fys, fye), entity_current_legal_name, and the columns saying where the row was read from (source_file, run_code, taxonomy, file_type, company_id).

average_number_employees_during_period, wages_salaries, dividends_paid and government_grant_income are the comparative period’s values wherever the source reports them: on a PDF-sourced row they are read from the comparative column of the donor document, and on an XBRL-sourced row they are refilled by the prior-period step (and then listed in prior_backfilled_fields). Where no source reports them for that period they are blank.

Left blank on every recovered row are the two donor dates (balance_sheet_date, fye_bs), company_dormant, principal_activities, and the four cic34_ narratives. These describe the donor document rather than the comparative period — a dormancy declaration is a statement about the year the filing covers — so carrying them across would attribute to the earlier year a statement nobody made about it.

When the back-fill step refills a cell, the row lists it in prior_backfilled_fields and names that donor in prior_backfill_source_fy and prior_backfill_source_file. So a single row can legitimately carry both provenance families at once: the row-level one (prior_source_*) saying where the year itself came from, and the cell-level one (prior_backfill_*) saying where individual refilled cells came from.
prior_source_fy For a recovered row, the financial year of the later filing that supplied the figures.
prior_source_file For a recovered row, the filing that supplied the figures. Every recovered row names its donor: an XBRL row through this column, a PDF row through this column and source_file, which for a recovered PDF row are the same filing.
prior_backfilled_fields A comma-separated list of the fields whose cells were blank on the row and were filled from the comparative figures in the following year’s filing. Blank when nothing was filled in this way. It appears both on ordinary rows and on recovered rows (from_prior_year TRUE), so a row may carry this cell-level provenance alongside the row-level prior_source_* columns.
prior_backfill_source_fy The financial year of the later filing that supplied those filled cells.
prior_backfill_source_file The archive that filing was read from.
financial_cell_sources A cell-level record of which source supplied the financial figures on the row. On a row built from an XBRL filing it names, comma-separated, the financial fields whose value was taken from the PDF extraction because the XBRL cell was blank, and is itself blank when every figure came from the XBRL filing. On a row with no XBRL filing behind it, it reads pdf_extraction, meaning every figure was extracted from the PDF.
derivation_source The origin of any value worked out arithmetically from other cells rather than read from a filing. No such derivations are enabled in this release (see Section 6), so this column is blank throughout.
no_monetary_values True when every one of the 28 financial-amount columns on the row (the balance-sheet and profit-and-loss fields, wages_salaries, dividends_paid and government_grant_income) is blank; False otherwise. An explicit 0 counts as a value, so a row of stated zeros is False. Such rows are kept because they record that the organisation filed accounts for that year and may still carry a dormancy flag and an employee count (see Section 6); filter on this column to keep only rows with financial figures.

Organisation Attributes from the Spine

These columns are attached to each row from the project Spine, the register of organisations described in Annex A2.

FieldDescription
uid The organisation’s Spine identifier, as above. It is the join key between this dataset and any other product built on the Spine.
normalised_name The organisation’s name after standardising case, punctuation, and common abbreviations. It exists to make names comparable between registers; the name exactly as filed is in entity_current_legal_name.
is_cic TRUE when the Spine records the organisation as a Community Interest Company.
csotype The organisation’s broad civil-society type, assigned in priority order: CIC when the Spine records it as a Community Interest Company; otherwise Charity when its own Spine record names one of the three charity regulators (the Charity Commission for England and Wales, the Scottish Charity Register, or the Charity Commission for Northern Ireland); otherwise Co-operative / Mutual when the Spine links it to the mutuals or co-operatives register; otherwise Other.

Other means “Companies House only” — the organisation appears on the Companies House register with no charity or mutual register link recorded in the Spine. It is not a residual “miscellaneous nonprofit” category, and it is not evidence that the organisation is not a charity.

The two tests are not symmetric, and this matters when counting. A co-operative or mutual is identified only through a cross-register link, because those registers’ own identifiers never appear in accounts data. A charity is identified only from the register named on its own Spine record, and not from a cross-register link. A company-registered charity whose Spine record names Companies House therefore falls to Other even where a link to a charity register exists. Treat the Charity count as a floor rather than a complete count of charitable companies, and use uid against the Spine directly if you need the authoritative charity population.

4. Coverage & Completeness

The combined dataset draws from XBRL and PDF sources. Coverage varies by financial year, by company size, and by individual line item. The charts and tables below summarise where the data is fullest and where it is sparsest.

Coverage is reported as a diagnostic description of this release. It is not a guarantee that every filing or line item is present, and the release policy does not impose arbitrary absolute coverage floors.

Across every record in this release, the four line items the release gate measures are populated at these rates: net current assets 63.110%; creditors due within one year 55.646%; current assets 56.113%; employee numbers 45.042%. These are the exact figures recorded in the release manifest for this candidate. They are whole-file rates: an item can be much better covered within a particular year, source, or company size than the headline suggests.

The year-by-year coverage rate below compares the organisations with accounts data in a year against the organisations that the release Spine — the same Spine this release was built from (Annex A2) — records as active at the end of that year, that is registered on or before 31 December and not yet removed.

The coverage denominator is wider than this dataset can ever be. The Spine counts every organisation on it, including unincorporated charities, trusts, and unincorporated associations, which have no Companies House registration and therefore cannot file accounts there. Those organisations are in the denominator and can never be in the numerator, so the rate below understates how completely this dataset covers the population it can actually reach — nonprofit companies. Read it as a coverage rate against the whole civil-society register, not as a filing-capture rate.

Records by Source and Year

Records by source and year
Figure: Records by source and year. Earlier years are XBRL-dominated; recent CIC filings come predominantly via the PDF extraction stream.

Source Overlap

Overlap between XBRL and PDF sources
Figure: Most records are uniquely sourced. Where both sources cover the same company-year they are combined into one row: 49,007 blank XBRL financial cells across 29,631 rows were filled from the PDF twin.

Variable Availability

Variable fill rates
Figure: Fill rates for the financial columns across all records.

Headline Coverage

Dataset Summary
Combined accounts dataset headline statistics
Metric Value
Total Records 1,355,663
Unique Organisations 218,296
Unique CICs 43,679
Columns in the Published File 62
Earliest Financial Year 1981
Latest Financial Year 2026
XBRL Records 1,207,667
PDF Extraction Records 147,996

Fill Rates by Source

Fill Rates by Source
Share of rows with a value, within each source stream. The two columns have different denominators: each is a share of that stream's own rows, not of the whole file.
Field XBRL Rows PDF Rows
Admin Expenses 5.6% 23.7%
Employees 46.5% 33.5%
Share Capital 2.8% 18.1%
Cash at Bank 36.0% 63.0%
cost_sales 2.9% 16.9%
Creditors (> 1 Year) 19.8% 15.1%
Creditors (< 1 Year) 53.3% 74.7%
Current Assets 58.5% 36.4%
Debtors 24.2% 44.7%
depreciation_other_amounts_written_off_tangible_intangible_fixed_assets 0.7% 8.5%
gross_profit_loss 3.6% 20.9%
Net Assets 83.3% 58.5%
Net Current Assets 61.6% 75.1%
operating_profit_loss 4.0% 18.1%
other_operating_charges_format2 1.6% 8.5%
other_operating_income 2.1% 11.2%
P&L Reserve 31.0% 63.6%
profit_loss_for_period 7.1% 28.4%
profit_loss_on_ordinary_activities_before_tax 5.9% 18.0%
raw_materials_consumables 0.8% 4.6%
Shareholder Funds 87.1% 38.2%
Staff Costs 1.0% 6.6%
Tangible Fixed Assets 38.8% 47.8%
tax_on_profit_or_loss_on_ordinary_activities 4.7% 17.2%
Total Assets Less Current Liabilities 65.2% 62.7%
Turnover 6.9% 29.1%

Year-by-Year

Coverage by Financial Year
Unique organisations with accounts data per year
Financial Year Unique Orgs Total Active Orgs Coverage Rate
2010 15,106 296,033 5.1%
2011 27,288 302,320 9.0%
2012 41,357 291,847 14.2%
2013 52,918 298,929 17.7%
2014 62,515 304,716 20.5%
2015 73,582 312,785 23.5%
2016 81,184 320,250 25.4%
2017 85,361 323,495 26.4%
2018 91,585 324,720 28.2%
2019 98,407 330,993 29.7%
2020 106,128 343,987 30.9%
2021 114,511 350,916 32.6%
2022 120,788 357,835 33.8%
2023 127,391 365,147 34.9%
2024 132,931 372,905 35.6%
2025 100,081 381,101 26.3%

5. What Can You Learn?

The dataset supports questions about the size, financial health, and activity of the UK nonprofit company sector. A non-exhaustive list of uses:

Sector Growth Trends

CIC accounts and net assets by year
Figure: CICs with reported accounts and median net assets by financial year.

Financial Profile

Financial profile of nonprofit companies
Figure: Distribution of five high-coverage balance-sheet fields across the sector.

Employment Dynamics

CIC employee trends
Figure: Total employees and reporting organisations by financial year for CIC accounts.

CIC34 Narratives

The CIC34 narrative fields support qualitative analysis of community impact and stakeholder engagement. Coverage rates by field:

CIC34 Narrative Field Coverage
Coverage rates for CIC-specific narrative fields
Field Non-Missing Total CIC Records Coverage Rate
Activities & Impact 174,080 206,126 84.5%
Stakeholder Consultation 171,951 206,126 83.4%
Directors Remuneration 170,297 206,126 82.6%
Asset Transfer 166,942 206,126 81.0%

Director Remuneration

Director remuneration patterns can be inferred from the CIC34 directors’ remuneration narrative. Among CICs reporting remuneration: coverage rate 83%, median statement length 36 characters.


6. Limitations & Caveats

Accepted release limitations. The counts in this section come from the passing, hash-bound release manifest. They describe known omissions; they have not been converted into apparently observed values.

Source Archive Completeness

The dataset is built from the Companies House monthly accounts archives. 6 monthly archives that earlier releases had been missing were restored in v1.3 and are included here: May 2010, May 2011, January 2023, January 2024, January 2025, and March 2025. Records filed in those months were absent from releases before v1.3 and are present now, so year totals for the affected years are higher than in earlier versions. This is recovered data, not growth in the sector.

Reporting Lag

Accounts are typically filed 6–12 months after the financial year ends. The most recent financial years will therefore be incomplete.

Filing Thresholds

Many CICs file as micro-entities and are exempt from disclosing detailed P&L information. Coverage of fields such as turnover_gross_operating_revenue and staff_costs is materially lower than balance-sheet coverage as a result.

Variable Financial Year Periods

Financial years vary in length, particularly for newly incorporated or dormant companies. Compare values per-year cautiously when the FY length differs from twelve months.

Empty PDF Extractions

The conversion quarantined 812 source filings whose extraction contained no usable values: 318 were not classified as dormant and 494 were classified as dormant. 0 local source PDFs were unavailable for review. These cases remain in the release evidence rather than being filled with inferred values.

All-Zero PDF Rows

Among current PDF rows that are not explicitly dormant (including rows where dormancy is unknown), 6,584 of 125,464 have all twelve balance-sheet fields recorded as zero or blank (5.248%). This exceeds the unchanged 2.000% default diagnostic threshold. It is an accepted release exception and an extraction pattern, not evidence that every underlying company had a zero balance sheet.

PDF stratumAll-zero rowsRowsShare
Current, explicitly non-dormant3,93487,4544.498%
Current, dormancy unknown2,65038,0106.972%
Current, explicitly dormant9,69816,11460.184%
Prior comparative2,5656,41839.966%

Rows With No Monetary Values

24,602 rows (1.815% of the file) have every one of the 28 financial-amount columns blank and are flagged no_monetary_values = True (14,117 explicitly dormant, 8,200 explicitly non-dormant, 2,285 with dormancy unknown; 9,653 carry an employee count). A random sample of such rows was checked against the source filings on Companies House: they are accounts that genuinely contain no monetary figures — typically the nil balance sheet of a dormant or non-trading company limited by guarantee (flat-management, right-to-manage and residents’ association companies are common), in which every line is printed as a dash and not tagged, or in which only the average number of employees is tagged. On the PDF side (6,845 rows) they are extractions in which every current-year amount is zero or absent, or small receipts-and-payments statements whose headings (“Subscriptions”, “Donations”) have no counterpart among the published columns. They are retained because they record that the organisation filed accounts for that year, and because dropping them would leave false gaps in organisations’ histories. Rows in which every reported amount is 0 are different: there the filer tagged those lines as zero (an iXBRL “zero-dash”) and the values are as filed.

Conflicting PDF Candidates

The converter recorded 42,952 material conflict events, representing 32,004 distinct source-period-field cells omitted from the converted PDF data. It emitted 0 disputed values. When direct candidates disagree, no winner is selected: the published cell is blank unless a non-conflicting preferred source supplies it. In the assembled final population, 19,979 affected cells remain blank and 7,222 are safely populated by a separate, non-conflicting auditable source.

Disabled Arithmetic Derivations

The pipeline does not manufacture additional coverage using the following identities. Each was tested against PDF-sourced evidence and was not confirmed; the checks did not establish that the identity holds well enough on the rows it would fill, which is not the same as showing that it fails. They remain disabled until confirming evidence exists:

  • creditors_due_within_one_year = current_assets - net_current_assets_liabilities — PDF identity check failed.
  • current_assets = net_current_assets_liabilities + creditors_due_within_one_year — PDF identity check failed.

The release manifest does not record this list, so the two identities above are the guidance generator’s documented default. Unlike the counts elsewhere in this section, they have not been read from the hash-bound manifest.

How to Interpret Blanks

A blank can mean that the filing did not disclose the item, extraction produced no usable value, or conflicting candidates were deliberately suppressed. Treat blanks as missing information, not as zero; a 0 is a value the filer stated (including a printed dash the filer tagged as zero). A row on which every financial-amount column is blank is flagged no_monetary_values (see above). The source_dataset column can be used to distinguish XBRL and PDF provenance.

Spine Filtering

Only nonprofit companies present in the project Spine (the deduplicated register of UK third-sector and civil-society organisations) are included. Profit-making companies and other Companies House registrants are excluded.

What is NOT in the Data

The dataset does not include cash-flow statements, notes to the accounts in full, contingent liabilities, related-party transactions, or auditor qualifications. For these, consult the original PDFs at Companies House.


7. Citation & Licence

The dataset is released under the Creative Commons Attribution 4.0 International (CC BY 4.0) licence. You are free to share and adapt the data for any purpose, including commercially, provided you give appropriate credit.

Suggested Citation

McDonnell, D. et al. (2026). Nonprofit Financial Records,
version v1.3 (August 2026). UK Third and Civil Society Sector Database.
Available at: https://uk-third-sector-database.github.io/data/
Licensed under CC BY 4.0.
Cite the version, not just the filename. tcss-nonprofit-accounts-Feb2026.zip is a stable download name, not a version label. Every release replaces the previous file at the same address, and the name itself does not change to match the release — it still carries the month of the first publication. So neither the filename nor the download URL identifies which data you used. Record the version and release date shown above — and, for exact reproducibility, the release identifier and file hashes given in Annex A4 — alongside any published result. Two analyses citing only the filename may have used different data.

8. Changelog

v1.3 — August 2026

  • Value-changing release: every source archive was re-parsed, so figures may differ from v1.2.1 throughout.
  • Restored six monthly Companies House accounts archives that earlier releases were missing (May 2010, May 2011, January 2023, January 2024, January 2025, and March 2025), adding the filings made in those months.
  • Comparative-period rows kept because no filing of the organisation’s own exists for that year are now correctly flagged from_prior_year and name their donor filing in prior_source_fy and prior_source_file. On those rows employees, wages, dividends and grant income are the comparative period’s own values wherever the source reports them — read from the donor document’s comparative column on the PDF side, or refilled by the prior-period step (with prior_backfill_* provenance) on the XBRL side — and blank otherwise. company_dormant is always blank on a recovered row, because a dormancy declaration is made about the year the filing covers and not about the comparative period; principal_activities and the CIC34 narratives are blank for the same reason.
  • balance_sheet_date and fye_bs are now blank on those recovered rows. Both dates belong to the later donor filing rather than to the year the row reports on, and publishing them against the recovered year repeated the error corrected in v1.2.1. The period is still given by period_end, fys, and fye.
  • Creditors are now read by maturity from the FRC current/non-current financial-instrument dimension. Most small companies filing through Companies House WebFiling tag every creditors line as plain “Creditors” and mark the maturity only through that dimension, so creditors_due_within_one_year and creditors_due_after_one_year were blank on those filings in every earlier release (or filled only from a later filing’s comparative column). Both columns gain roughly 60,000 and 27,000 values respectively.
  • Re-extracted 28,643 PDF filings from FY2021 onward that were published in v1.2.1 without an employee count, using OpenAI gpt-5.4-mini with prompt v5 (which adds an explicit rule that a bracketed figure is negative). The new extraction replaces the earlier one for those filings: about 16,000 of them gain an employee count, monetary cells on them rise by about 8%, and 1.4% of the figures present in both versions change sign. A small number of note-derived cells (chiefly wages_salaries and depreciation) that the earlier extraction had read from the notes are no longer supplied. Twenty filings whose page images exceed OpenAI’s 50 MB per-request limit keep their earlier extraction.
  • Fixed a sign error in the comparative-figure harvest, so recovered expense and liability values now follow the same convention as the rest of the file.
  • Fixed the equity and called-up share capital fields, which had been conflated on some filings.
  • Added FRS-102 label aliases to the prior-period back-fill, recovering cells that previously stayed blank.
  • PDF extraction fixes: removed hallucinated documents (filings the extractor answered from a template rather than the document); corrected the sign taken from combined-column headers; added net-current-assets and tax-credit label aliases; and added a component-versus-total conflict rule so a component is never published as if it were the total.
  • Removed 5 columns that carried no usable information (66 columns down to 61): zip_url and error were empty on every row; date and companies_house_registered_number duplicated balance_sheet_date and coyno; and period_start equalled period_end on 99.9% of rows.
  • Added one column (61 up to 62): no_monetary_values, True on a row whose 28 financial-amount columns are all blank. Inspection of the source filings showed these are accounts that genuinely contain no monetary figures (nil balance sheets printed as untagged dashes, or a headcount as the only tagged number), so the rows are kept and flagged rather than dropped — see Section 6.
  • Fixed the reading of “zero-dash” figures: a printed dash whose iXBRL tag declares it to mean zero is now recorded as 0 rather than left blank, so many nil balance sheets that were blank in earlier releases now show explicit zeros read from the organisation’s own filing.
  • taxonomy is now populated for filings tagged under the FRC 2018–2025 taxonomies; earlier releases left it blank for almost every filing from FY2021 onward.
  • The label-mapping no-regression baseline (the pinned PDF-versus-XBRL overlap audit that guards the converter’s column routing) was rebuilt on the v1.3 inputs with the owner’s approval: the corrected panel makes many balance-sheet values match more than one column (net assets, total assets less current liabilities and shareholder funds are often equal), which the audit counts as ambiguous rather than confirmed. Column routing is unchanged on all but four low-frequency labels; the confirmed-correct count rose in 25 of 29 columns.
  • Documentation corrections: every published column is now defined in Section 3; the cross-source merge, the sign conventions, the derivation of fys and fye, and the meaning of csotype “Other” are described accurately.

v1.2.1 — August 2026

  • Point release: no financial value changed.
  • Removed the source_filing_date column. It had been populated on about 706,000 XBRL rows with the following filing’s accounting date even though nothing was recovered on those rows.
  • Removed 24 undocumented *__donor_end working columns (91 columns down to 66).
  • Coverage-rate denominators now use the release Spine v1.2 rather than the March 2026 spine.
  • Documented the provenance columns in Section 3.

v1.2 — July 2026

  • Rebuilt the XBRL and converted-PDF inputs and refreshed all figures and tables from the exact release candidate.
  • Applied source-backed scalar/date/employee corrections and a pinned mapping no-regression audit.
  • Removed arbitrary default coverage floors; coverage remains a diagnostic.
  • Documented accepted empty-extraction, all-zero-row, and suppressed conflict limitations from the passing release manifest.
  • Kept both unconfirmed arithmetic coverage derivations disabled.

v1.1 — May 2026

  • Incorporated ~42,000 newly extracted CIC PDF accounts.
  • Added csotype column (CIC / Charity / Co-operative / Mutual / Other).
  • Refreshed coverage statistics throughout the guidance.

v1.0 — February 2026

  • Initial public release of the merged XBRL + PDF combined dataset.

Part 2: Technical Annex

This annex provides the full technical detail on data sources, processing steps, coverage statistics, and known limitations for the combined accounts dataset.

A1. Pipeline Architecture

The dataset is produced by a multi-stage pipeline that combines two source streams into a single deduplicated CSV.

Stream 1: XBRL Accounts (monthly bulk extracts)

Companies House publishes monthly ZIP archives of XBRL-tagged accounts. Each archive is downloaded, parsed using stream-read-xbrl, and appended to a per-month CSV with a uniform schema. The combined extract represents the ‘XBRL’ stream.

Stream 2: PDF Accounts (LLM-extracted)

For each PDF account filing, the pipeline (a) renders pages to JPEG images, (b) submits the image set to OpenAI’s Batch API with a JSON-schema response format that prompts a structured extraction of line items, balance-sheet date, and CIC34 narratives, and (c) collects the JSON outputs to disk under data/output/accounts-extractions/json-schema/{coyno}/{txn}.json. The bulk of the corpus was extracted with gpt-4.1-mini (prompt v2, February 2026). Two later passes with gpt-5.4-mini replace the earlier extraction for the filings they cover: about 13,800 filings re-extracted after the hallucination review (May 2026, prompt v2), and 28,643 filings from FY2021 onward that were published without an employee count in v1.2.1 (August 2026, prompt v5). Every extraction JSON records its model and prompt version in extraction_metadata.

Stream Convergence: XBRL Format Conversion

The PDF JSON extractions are converted into the same column schema as the XBRL stream using an approved label mapping plus a Jaccard-similarity fallback for unmapped labels. Sign-aware columns (creditors, expenses) are normalised to positive magnitudes.

Merge and Provenance

The XBRL and PDF-converted CSVs are merged on (coyno, fy). Where both sources cover the same company-year they are combined into a single row, cell by cell, rather than one row being chosen and the other discarded. The XBRL value wins wherever it is present. Only where an XBRL financial cell is blank is the value taken from the PDF twin, and every column filled that way is named in financial_cell_sources on that row. In this release 49,007 cells across 29,631 rows were filled this way. Non-financial values and already-populated values from the PDF twin are not used. A PDF row with no XBRL counterpart is carried through whole and marked pdf_extraction. A source_dataset column records which stream each row was built from.


A2. Source Data

Companies House — XBRL Accounts

The official monthly bulk download of accounts in XBRL format is available at download.companieshouse.gov.uk/en_monthlyaccountsdata.html. Historic archives are at historicmonthlyaccountsdata.html.

Companies House — PDF Accounts

For filings outside the XBRL stream (older accounts, CIC abridged filings, etc.), original PDFs are obtained via the Companies House document API and stored locally for extraction.

Companies House — CIC34 Forms

Community Interest Reports are filed as part of the same PDF accounts package for CICs and are extracted alongside the financial line items.

Spine

The hash-bound release Spine (code/release-v1.2-Jul2026/TSCS_spine.spine.csv) is the deduplicated register of UK third-sector and civil-society organisations. It is named and hash-checked in the release manifest for this candidate, so the register behind these figures is the one this release was actually built from. It is the inclusion list for the dataset and supplies uid, normalised_name, is_cic, and the derived csotype.


A3. Deduplication & Validation

Within-Source Deduplication

For each (coyno, fy) pair, the pipeline prefers current-year filings over prior-year companion rows. Same-source duplicate rows (for example, balance-sheet and P&L sections of the same filing) may contribute non-conflicting values to one row. It does not simply take the first non-null value: distinct direct candidates for the same source-period-field cell are treated as a material conflict, logged, and omitted. This release logged 42,952 such events and omitted 32,004 disputed cells and emitted 0 unsafe disputed values. In the assembled final population, 19,979 of the affected cells remain blank and 7,222 are populated from a separate, non-conflicting auditable source.

Cross-Source Deduplication

Where both XBRL and PDF sources cover the same (coyno, fy), the two are coalesced into one row, cell by cell. The XBRL row is the base and its value wins on every column where it holds one. A blank XBRL financial cell — and only a financial cell — is filled from the matching PDF row, and each column filled that way is listed in financial_cell_sources on that row, so a reader can tell exactly which figures on an xbrl row came from the PDF extraction. In this release that filled 49,007 cells across 29,631 rows. Non-financial values from the PDF twin, and PDF values for cells the XBRL filing already populated, are not used. A PDF row with no XBRL counterpart is appended whole and carries financial_cell_sources = pdf_extraction. Earlier versions of this guidance said the PDF row was dropped; that was never what the pipeline did.

Prior-Period Recovery

Accounts print the previous year’s figures alongside the current year’s. Where an organisation has no filing of its own for a year, the pipeline can recover that year from the comparatives in the following year’s filing; the row is flagged from_prior_year and the donor filing is named in prior_source_fy and prior_source_file. On an ordinary row, individual blank cells may be filled the same way; when that happens the fields filled are listed in prior_backfilled_fields, with the donor filing named in prior_backfill_source_fy and prior_backfill_source_file. A value that is already present is never overwritten.

Every recovered row names its donor filing, and both source streams are normalised the same way. Where prior_source_file is blank it is set from source_file, and where prior_source_fy is blank it is derived from the donor’s own year. Provenance that is already recorded is never overwritten. For a recovered PDF row prior_source_file and source_file therefore name the same filing.

The same step then clears balance_sheet_date and fye_bs on a recovered row. Those dates describe the donor filing, not the year the row reports on, and leaving them in place would reproduce the error corrected in v1.2.1: a date from a later filing standing against an earlier year. The period the row does report on is in period_end, fys, and fye, and the donor is identified by prior_source_fy and prior_source_file.

The same step blanks company_dormant on a recovered row, for the same reason: a dormancy declaration is made about the year the filing covers, not about the comparative period, so the donor’s declaration would stand against a year nobody made it about. The PDF converter copies it onto its comparative rows, so this is where the two sources are brought into line. principal_activities and the CIC34 narratives are blanked on the same grounds.

Employee numbers, wages_salaries, dividends_paid and government_grant_income are not blanked. Where a source reports them on a recovered row they are that period’s own values: read from the comparative column of the donor document on the PDF side, or refilled by the prior-period step on the XBRL side, in which case the row also carries prior_backfilled_fields and its own prior_backfill_source_* columns in addition to the row-level prior_source_* ones. Where no source reports them for the comparative period they are blank. The organisation’s name and the columns recording where the row was read from are kept.

The recovery step also uses internal working columns to carry the donor period’s own end date while it matches filings. Those working columns are not part of the published dataset: they were removed in v1.2.1 (see Section 8), together with a filing-date column that had been stamped on every row with a later filing, whether or not anything was recovered from it. Nothing was published as a submission date, and no financial value depended on either column.

Date Validation and Derived Period Columns

fy is normalised to integer-string. Where fy is blank but balance_sheet_date is populated, fy is derived (with a one-year offset for prior-year companion rows), and fy is corrected to the year of fye where the two disagree.

Two period columns are derived rather than filed, and should be read as such:

  • fye is taken from the filing’s tagged period end, falling back to the balance-sheet date. Where neither is usable it is imputed by applying the company’s most common financial-year-end month and day to the row’s fy year; where the company has no other filing to learn a month-day from, the month and day of balance_sheet_date are used. 29 February in a non-leap year falls back to 28 February. Rows with neither source keep a blank fye. The number of imputed rows is not recorded in the release manifest, so no count is quoted here.
  • fys is always computed, never read from a filing. Within each organisation, ordered by fye, it is the previous financial year end plus one day, provided the resulting period runs to no more than 397 days (13 months) — enough headroom for an ordinary change of accounting reference date. Where a gap in the filing sequence would otherwise create a multi-year period, and for the first year of a company’s sequence, it falls back to fye minus one year plus one day. A blank fye gives a blank fys.

Sign Normalisation

All monetary columns are signed: a negative value means a liability, a deficit, or a loss. Ten expense and liability columns are treated differently on the PDF side, because XBRL stores them as positive magnitudes while PDF extractions often preserve the negative accounting sign printed in the accounts. The conversion applies abs() to these ten columns so both sources share one convention:

  • creditors_due_within_one_year
  • creditors_due_after_one_year
  • cost_sales
  • administrative_expenses
  • raw_materials_consumables
  • staff_costs
  • wages_salaries
  • other_operating_charges_format2
  • tax_on_profit_or_loss_on_ordinary_activities
  • depreciation_other_amounts_written_off_tangible_intangible_fixed_assets

One of the ten is an exception within the exception: tax_on_profit_or_loss_on_ordinary_activities keeps its direction, because a tax charge and a tax credit are different facts. A charge is positive; a credit is negative. Taking the absolute value of that column would report a refund as a liability.

This normalisation is applied to PDF-extracted values only. Values that came from an XBRL filing keep the sign the filer used. The practical consequence is that a negative value can still appear in these ten columns on an xbrl row, and means what the filer tagged; it is not an extraction error. Take the absolute value before comparing these columns across sources.

Arithmetic Derivations

Potential balance-sheet identities are disabled unless they pass source-stratified confirmation checks. The two candidate derivations in this release remain disabled because those checks did not confirm them — the available PDF-sourced evidence was too thin, and too dominated by commercial filers, to establish that the identity holds on the rows it would fill. That is a lack of confirmation, not a demonstration that the identities are wrong.

Employee-Count Guardrails

Legitimate non-negative fractional employee counts are preserved. Values tagged as currency or percentages, negative values, and counts above 100,000 are rejected. The same guard applies to direct, recovered, and prior-year candidates; if a current employee fact is rejected, the pipeline does not silently replace it with a prior-period value.

Spine Filtering

Only company numbers present in the Spine are retained. A left-join on uid populates is_cic and the derived csotype.


A4. Reproducibility

Code

The end-to-end pipeline lives in code/companies-house/:

  • pdf_accounts_extraction/openai-api/ — PDF-to-JSON extraction (rendering, batch submission, batch processing).
  • pdf_accounts_extraction/xbrl_format_conversion/ — JSON-to-XBRL column schema conversion, label mapping, dataset merge, and public-zip publishing.
  • reporting/guidance/ — this guidance document generator.

Dependencies

Managed via the uv package manager. Run python code/companies-house/pdf_accounts_extraction/xbrl_format_conversion/dependencies.py to install conversion-pipeline dependencies; analogous scripts cover the other modules.

Running the Pipeline

The root ordinary build now names its two formerly hidden final stages. convert-pdf creates one explicit, versioned PDF CSV and merge-final consumes that exact path to build the actual final financial dataset. combine-full remains the XBRL-only intermediate; release gating and publication remain separate.

cd code/companies-house
python run_pipeline.py convert-pdf --xbrl-csv XBRL.csv --output CONVERTED_PDF.csv --diagnostics-dir PDF_DIAGNOSTICS
python run_pipeline.py merge-final --xbrl-csv XBRL.csv --pdf-csv CONVERTED_PDF.csv --spine-csv SPINE.csv --matches-csv MATCHES.csv --output COMBINED.csv

cd pdf_accounts_extraction/xbrl_format_conversion
python run_pipeline.py release 2026-08-18-spine-v1.3 --converted-csv CONVERTED_PDF.csv --conversion-report CONVERSION_REPORT.csv --check-b-audit CHECK_B_AUDIT.json --xbrl-csv XBRL.csv --spine-csv SPINE.csv --matches-csv MATCHES.csv --output-dir OUTPUT_DIR --accepted-limitations-policy POLICY.json

python ../../reporting/guidance/generate_guidance.py --combined-csv cso-spine-nonprofit-company-accounts-combined-2026-08-18-spine-v1.3.csv --pdf-csv cic-accounts-extractions-xbrl-format-2026-08-18-spine-v1.3.csv --release-manifest manifest.json --release-date 2026-08-18 --release-version v1.3 --public-zip-name PUBLIC.zip

python run_pipeline.py finalize-release --manifest manifest.json --guidance-pdf GUIDANCE.pdf --guidance-html GUIDANCE.html --licence-template LICENCE.txt --release-repo RELEASE_REPO --website-guidance WEBSITE_GUIDANCE --public-zip-name PUBLIC.zip --archive-date 2026-08-18

Guidance generation verifies the SHA-256 of both CSV inputs against the passing candidate manifest. Finalization does not rebuild that immutable candidate: it verifies the candidate and retained limitation evidence, then binds the explicit guidance pair, licence, and destinations by SHA-256 in a separate publication manifest before promotion. A lexical “latest file” lookup is not part of the release procedure.

Source Code

The release repository is at github.com/uk-third-sector-database/tso-database-builder.