UK company accounts  ·  2008 to today

Every fact,
every filing

Eighteen years of Companies House accounts, parsed out of the bulk XBRL feed and served as JSON. Not a fixed set of 27 figures: everything a filing reports, kept with the dimensions that make it mean something.

5.7M
Live companies, from the monthly register snapshot
2008
Earliest accounts held, and unbroken since
985
Distinct XBRL concepts in a single day of filings
38%
Of facts carry dimensional context, and keep it

The data

Three sources, one registration number

The register says who exists. The accounts say what they are worth. Geocoding says where they are. Fortuna holds all three against one company number, so a question that spans them is a single request.

5,948,213

The register

Company status, registered office and SIC codes, taken from the monthly Free Company Data Product rather than the REST API, which would take about 64 days of continuous calling to cover once.

68 facts / filing

The accounts

Every XBRL fact in every filing, stored whole. The daily feed lands Tuesday to Saturday, matching the days Companies House actually publishes, and the archive reaches back to 2008.

PostGIS

The geography

Companies geocoded to postcode level and measured on the WGS84 spheroid, so radius, district and county searches are answered by a spatial index rather than filtered after the fact.

Why every fact

A figure without its dimensions is not a figure

A filing reports the same concept many times: once undimensioned as the balance sheet total, then again per share class, per asset class, per movement, and for the consolidated group. Across 4,212 real filings, 45% carried more than one distinct Equity value for a single period.

One period, one concept, five reported values
Dimensional contextEquity
None company, undimensioned 2,898,598
ShareCapital100
SharePremium412,000
RetainedEarnings2,486,498
ConsolidatedGroup3,140,220

Highlighted is the one Fortuna projects into share_holder_funds. Selection ranks by selector priority, then company before consolidated, then fewest dimensions, then latest period.

A breakdown is never a total

The class dimensions carry genuine components only, with no Total member to fall back on. A filing that reports a figure solely by class therefore yields null, not an arbitrary component picked out of the breakdown.

Concept identity ignores the prefix

A census of 400 documents from one daily file found core:, frs-core:, uk-core: and d: all bound to the same FRC namespace across four taxonomy years. Prefixes are document local, so identity is the namespace family and the local name.

FRS 102 renamed most of it

Checked against current filings, 16 of the legacy 27 concept mappings no longer match. Both the modern and the legacy spelling are held per field, so a 2009 filing and a 2026 filing answer in the same shape.

A specimen

What one answer looks like

Two of the shapes the API returns, with the request that produced each. Every field is snake_case, matching the column names the data has.

JKL Limited

03364921  ·  BH22 9NH  ·  Dorset
net_assets£2,898,598
employees43
distance_miles11.81
company_statusActive
sic_codes82990
domainnull

Net assets and employees come from the most recent filing. Status comes from the register. Distance is measured from the origin postcode on the spheroid.

GET /api/search/area?postcode=DT11+7ST&radiusMiles=20
    &minNetAssets=1000000&minEmployees=20&status=active

{
  "items": [
    {
      "company_registration": "03364921",
      "name":                 "JKL Limited",
      "post_code":            "BH22 9NH",
      "admin_district":       "Dorset",
      "admin_county":         null,
      "distance_miles":       11.81,
      "net_assets":           2898598,
      "employees":            43,
      "period_end":           "2025-03-31",
      "company_status":       "Active",
      "status_code":          1,
      "sic_codes":            ["82990"],
      "domain":               null
    }
  ],
  "total":    37,
  "limit":    50,
  "offset":   0,
  "has_more": false
}

API

Nine keyed routes, three open

Everything under /api takes an X-Api-Key header. /health, /progress and the OpenAPI document do not, so a probe never needs a credential. Everything below is also at /docs, where you can paste a key in and send the requests.

# A first request
curl -H "X-Api-Key: $FORTUNA_KEY" \
     "https://fortuna.mythic.software/api/companyfiling/00005775"

Endpoints

GET /api/companyfiling/{id}
Every filing held for a company, newest period end first
GET /api/companyfiling/{id}/from/{from}
Filings whose period ends on or after a date
GET /api/companyfiling/{id}/to/{to}
Filings whose period ends on or before a date
GET /api/companyfiling/{id}/between/{from}/{to}
Filings whose period ends within a range
GET /api/search/name/{name}
Companies whose registered name contains the text
GET /api/search/postcode/{postcode}
Companies whose postcode contains the text
GET /api/search/director/{director}
Companies with an officer whose name contains the text
GET /api/search/id/{id}
Companies whose registration number starts with the text
GET /api/search/area
A postcode with a radius, or a district or county, with financial filters
GET /health
LivenessNo key
GET /progress
Ingest progress, the same data the live page is pushedNo key
GET /openapi/v1.json
OpenAPI document, generated from the controller doc commentsNo key
GET /docs
The same document as a reference you can send requests fromNo key

Area search parameters

Either a postcode with a radius, or a district or county, is required. Supplying both narrows to the intersection. Query parameters are camelCase even though response fields are snake_case.

ParameterTypeMeaning
postcodestringOrigin postcode to measure a radius from, for example DT1 1XJ
radiusMilesnumberRadius around the origin, up to 500
districtstringAdministrative district. Prefer this to a radius for a named area: a 20 mile circle on Dorchester spills into Somerset and still misses east Dorset
countystringAdministrative county, where the area has one
statusstringCompanies House status, for example active. Needs enrichment to have run, since it comes from the register rather than the accounts
minNetAssetsnumberMinimum net assets on the most recent filing
minEmployeesnumberMinimum average employees on the most recent filing
sicCodestringSIC code the company is registered under
limitintPage size, 1 to 200. Defaults to 50
offsetintHow many results to skip. Defaults to 0

Paging

Filing and area responses wrap their results in an envelope. The four simple search routes return a bare array of companies and take the same limit and offset parameters.

{ "items": [ … ], "total": 37, "limit": 50,
  "offset": 0, "has_more": false }

Filing response fields

Identity 5 fields
company_registrationcompany_namelegal_form_of_company company_tradingcompany_dormant
Period 5 fields
start_date_for_period_covered_by_report end_date_for_period_covered_by_report made_up_tobalance_sheet_datecurrency_used_in_report
Balance sheet 9 fields
fixed_assetsfixed_assets_cost_or_value fixed_assets_cost_or_value_depreciationcurrent_assets debtorscash_at_bank net_current_assets_liabilities total_assets_less_current_liabilitiesnet_assets_liabilities
Creditors 3 fields
creditors_due_within_one_yearcreditors_due_after_one_year creditors_total
Equity and shares 7 fields
share_holder_fundscalled_up_share_capital profit_loss_account_reserveshare_type par_value_shareshares_allotted shares_allotted_called_up_paid
Address and people 5 fields
principal_place_of_businessaddress_line1 address_line2post_codeofficers
Other 1 field
average_employees

officers is a JSON array, and records who signed that particular filing, which is a different fact from who is a director today.

Status codes

200Results, possibly an empty page
400An invalid range, or an area search with neither a radius nor an area
401Missing or unrecognised X-Api-Key
404No filings for that company, or an origin postcode no company is registered at, which is a different answer from no companies nearby
429Daily or monthly quota spent, or the burst limit hit. Carries Retry-After

Access

A key, a quota, and headers that tell you where you are

Keys carry a daily and a monthly allowance, set per key when it is issued. Every response reports what you have spent, so you never have to guess, and a 429 tells you how long until the window rolls over.

Quota
Durable daily and monthly counts. A new window is a new row, so there is no reset job to miss.
Burst
A token bucket per key, defaulting to 20 a second with a burst of 40, so a generous monthly allowance cannot be spent in an afternoon.
Unlimited
A null limit means no cap on that window.
Request a key
X-RateLimit-Limit-Day:     10000
X-RateLimit-Used-Day:      1284
X-RateLimit-Limit-Month:   200000
X-RateLimit-Used-Month:    38117
X-RateLimit-Remaining:     8716