SStratum APIs
← Blog

How to verify FCA authorisation programmatically

25 April 2026

If your product touches UK financial services, at some point you will need to confirm that a counterparty is actually authorised by the Financial Conduct Authority. Lenders verify introducers, fintech onboarding flows verify partner firms, marketplaces verify advisers, accountants verify clients before MLR-regulated work. The good news: the FCA publishes the entire register through a public API. The bad news: the API has rough edges that take a few days of trial and error to learn.

This is a working guide to the parts most people get wrong on the first attempt: registering, calling the right endpoint, interpreting the status field, handling appointed representatives, and caching responses without breaking compliance assumptions.

The register, in one paragraph

The Financial Services Register is the FCA's public list of every firm and individual it regulates. Every regulated entity has a Firm Reference Number (FRN); every regulated individual has an Individual Reference Number (IRN). Firms hold permissions (the regulated activities they may carry on); individuals hold approved roles (Senior Managers, Certified Persons, Appointed Representatives). The register is updated daily from the FCA's internal systems, with most changes visible the next working day.

Getting access

The API is free but requires a registered key. Apply at register.fca.org.uk/Developers. You will receive an email address (yours, used as the auth identifier) and a developer key, both of which go in HTTP request headers as X-Auth-Email and X-Auth-Key. Approval typically takes one working day.

The basic firm lookup

Once you have an FRN, fetching the full firm record is a single GET request:

const FCA_BASE = 'https://register.fca.org.uk/services/V0.1';

async function getFirm(frn: string) {
  const r = await fetch(`${FCA_BASE}/Firm/${frn}`, {
    headers: {
      'X-Auth-Email': process.env.FCA_EMAIL!,
      'X-Auth-Key': process.env.FCA_KEY!,
      'Accept': 'application/json',
    },
  });
  if (!r.ok) throw new Error(`FCA upstream returned ${r.status}`);
  return r.json();
}

const firm = await getFirm('122702'); // Barclays Bank Plc

The response is a JSON envelope with a Status string at the top level (this is the HTTP-style status of the API call, not the firm) and a Data array containing one record per firm. For a single-firm lookup the array always has exactly one element; for the search endpoint it can have many.

The status field, and why "Authorised" is not always Authorised

The FCA models firm regulatory status as a free-text string. In practice it takes one of about a dozen values, the most common being Authorised, EEA Authorised, No longer authorised, Cancelled, Applied to cancel, and Authorisation declined. There is no enum and no official documentation of the full list; you discover values by encountering them.

The first gotcha: a firm with the status EEA Authorised is authorised under the temporary permissions regime that succeeded passporting. Whether you treat that as "authorised" depends on your use case. Many compliance teams reject it; others accept it with a note. Either way, do not silently bucket it into the same group as Authorised.

The second gotcha: a firm can be Authorised but have all its permissions removed or restricted. To know what a firm is actually permitted to do, you need a separate call:

const perms = await fetch(
  `${FCA_BASE}/Firm/${frn}/Permissions`,
  { headers: { /* same as before */ } },
).then(r => r.json());

The permissions response lists each regulated activity the firm currently holds, with restrictions and conditions where applicable. For mortgage brokers, payment institutions, EMIs, and credit firms this is where the substantive answer lives.

Individuals, and why approved-person history matters

Individuals get their own endpoint, keyed by IRN:

const ind = await fetch(
  `${FCA_BASE}/Individuals/${irn}`,
  { headers: { /* same */ } },
).then(r => r.json());

The response includes current and historic regulated roles, the firms they were held at, and the dates. For Senior Managers and Certified Persons this is the only authoritative source: an SM&CR firm may not employ an unauthorised individual in a controlled function, so verifying current status before placement is non-optional. The Certification Regime makes this an annual recheck, not a one-off check at hire.

Appointed representatives

An appointed representative is a firm that operates under the regulatory umbrella of an authorised principal firm. The register exposes this as a relationship: the AR firm has its own FRN, but its "authorisation" is borrowed from the principal. The AR endpoint lets you walk that relationship:

const ar = await fetch(
  `${FCA_BASE}/Firm/${frn}/AR`,
  { headers: { /* same */ } },
).then(r => r.json());

If you are verifying a regulated relationship, do not stop at the AR firm's status. Always fetch the principal too and check that the AR is still listed as one of the principal's representatives. ARs can be terminated by the principal at short notice, and lag in the register can be days.

Caching, without breaking the audit story

The register is updated daily. A 5-minute response cache is generally safe and cuts upstream load by a large factor. Anything longer starts to risk stale-data complaints from compliance buyers. A pragmatic pattern is to cache by FRN with a 5-minute TTL, but always re-fetch when generating an audit certificate or evidence record so the timestamp on the record reflects a live check.

const cache = new Map<string, { data: unknown; ts: number }>();
const TTL_MS = 5 * 60 * 1000;

async function getFirmCached(frn: string) {
  const hit = cache.get(frn);
  if (hit && Date.now() - hit.ts < TTL_MS) return hit.data;
  const fresh = await getFirm(frn);
  cache.set(frn, { data: fresh, ts: Date.now() });
  return fresh;
}

What the API does not give you

Three things sit outside the FCA register API and surprise teams the first time:

  • Webhook notifications on status change. There is no "tell me when this firm's status changes" mechanism. You have to poll. Daily polling per monitored firm is the usual answer.
  • Cross-reference to Companies House. The register has the firm name and address; matching it back to a CH company number is your problem. Most matches are easy by exact name plus postcode; a few percent need fuzzy logic.
  • Cross-reference to other regulators. A solicitor practising as a financial adviser is regulated by both the SRA and the FCA. Neither register tells you about the other; you cross-reference.

When to stop building this yourself

The first version of an FCA verification feature is satisfying to ship: one fetch, one status check, one cache. The second and third versions absorb time, because each new edge case (EEA, AR principals, permission restrictions, individual recerts, multi-regulator firms) needs handling and tests. If you find yourself maintaining a separate normalisation library, an audit-log table, and a daily change-detection cron just to support a single product feature, it is worth pricing the alternative.

Our FCA Verification API wraps the above, plus SRA cross-reference, daily change feed, and a 365-day audit log, behind one HTTP call and one stable JSON schema. It is built on the same FCA endpoints described here, so you can switch back at any time without lock-in.

How to verify FCA authorisation programmatically | Stratum APIs