Companies House API 403 Forbidden and 401 Unauthorised: why it happens and how to fix it
401, 403 or 429: what each one means
A 401 means Companies House did not accept your credentials, a 403 means it recognised the request but refused it, and a 429 means you are over the rate limit. We reproduced each response against the live services on 21 October 2026, and the 403 causes come from Companies House staff replies on the developer forum.
On the main API the 401 body is short JSON, and a Ch-Authentication-Error header repeats the reason:
HTTP/1.1 401 Unauthorized
Ch-Authentication-Error: Invalid Authorization
Www-Authenticate: Invalid or no Authorisation header has been provided
{"error":"Invalid Authorization","type":"ch:service"}
With no header at all, the error reads Empty Authorization header instead. The Document API returns a 401 with an empty body.
Troubleshooting table
Most failures come from one of nine causes, and the status code narrows it down quickly.
| Status | Where | Cause | Fix |
|---|---|---|---|
| 401 | Any API | No Authorization header | Send Authorization: Basic base64(key:) on every request |
| 401 | Any API | Key sent as Bearer, or wrong base64 | Use Basic auth: key as username, empty password, colon before encoding |
| 401 | Streaming API | REST API key used on stream.companieshouse.gov.uk | Create a separate stream key; the two are not interchangeable |
| 401 | Sandbox | Live key used on api-sandbox.company-information.service.gov.uk | Create a test application and test keys for the sandbox |
| 403 | Any API | Your server's IP is not in the key's Restricted IPs list | Add the egress IP or clear the list, then wait about 10 minutes |
| 403 | Any API | Traffic far above the rate limit, or a manual ban | Slow down; if it persists, contact Companies House with your IP |
| 403 | Document API | Pre-signed S3 link used after it expired (60 seconds) | Request /content again and download straight away |
| 400 | Document API | Your Companies House Authorization header forwarded to S3 | Follow the redirect without the header |
| 429 | Any API | Over 600 requests in five minutes | Throttle and back off; see rate limits |
A browser CORS error is often a 403 or 401 in disguise, so check the network tab for the real status before changing your CORS settings.
Fix 401: the Basic auth format
Companies House uses HTTP Basic authentication with the API key as the username and a blank password. That is how the authorisation guide describes it, and its example header is Authorization: Basic bXlfYXBpX2tleTo=, which is my_api_key: in base64.
The colon matters. A forum moderator put it plainly in 2016: "take your api key, append a colon and then base64 encode. NOT base64 encode the api key and then add a colon." The streaming API guide warns that leaving the colon out gives "a different and invalid base64 encoded value". In our own tests the REST API tolerated a couple of non-standard variants, but a Bearer token was always rejected, so stick to the documented format.
curl
export CH_API_KEY="your-key-here"
curl -u "$CH_API_KEY:" https://api.company-information.service.gov.uk/company/00445790
The trailing colon in -u "$CH_API_KEY:" tells curl the password is empty, so it does not prompt for one.
Python (requests)
import os
import requests
s = requests.Session()
s.auth = (os.environ["CH_API_KEY"], "") # key as username, empty password
r = s.get("https://api.company-information.service.gov.uk/company/00445790", timeout=30)
print(r.status_code, r.headers.get("Ch-Authentication-Error"))
r.raise_for_status()
print(r.json()["company_name"])
JavaScript (fetch)
const key = process.env.CH_API_KEY;
const auth = "Basic " + Buffer.from(`${key}:`).toString("base64");
const res = await fetch("https://api.company-information.service.gov.uk/company/00445790", {
headers: { Authorization: auth },
});
if (!res.ok) throw new Error(`${res.status} ${res.headers.get("Ch-Authentication-Error") ?? ""}`);
console.log((await res.json()).company_name);
All three print TESCO PLC; we ran them on 21 October 2026, the JavaScript under Bun. Buffer is a server-side API: in a browser you would use btoa, but read the CORS section first.
Fix 401: wrong key type or wrong environment
Companies House keys are tied to one API and one environment, so a valid key can still get a 401. The Developer Hub application guide lists three client types: API key, stream key and OAuth web client.
- REST key on the Streaming API. The streaming guide says "REST API and streaming API keys are not interchangable". Our REST key returned
Invalid Authorizationfromstream.companieshouse.gov.uk. - Live key on the sandbox. Applications are created as either test (sandbox) or live. The sandbox, at
api-sandbox.company-information.service.gov.uk, does not accept live API clients, according to the Companies House API testing page. Our live key got a 401 there. - Keep test and live apart. Store the base URL and the key in the same config entry so they cannot drift apart.
Fix 403: Restricted IPs and blocked addresses
The most common confirmed cause of a 403 is the Restricted IPs setting on the key. When you create a REST key you can fill in optional Restricted IPs and JavaScript domains. Companies House recommends restricting keys by IP in its developer guidelines, so a compromised key is less useful, but the list has to match your real outgoing address.
In February 2019, after Companies House "tightened up security around restricted IPs", its staff told users with 403s to "update your API account Restricted IPs to ensure you have the correct IP addresses or remove them altogether", adding "there is a caching period of 10 minutes". A 2022 forum user fixed a 403 on the Streaming API the same way, by removing the IPs from the key.
This catches cloud deployments in particular. Containers, serverless functions and autoscaling groups often leave through a NAT gateway or a changing address, not the IP you tested from. Use a fixed egress IP, or leave the list empty.
Is Companies House blocking AWS or other cloud IPs?
There is no official statement that cloud IP ranges are blocked. What staff did say, in a March 2019 forum reply: "If you exceed the 600 requests in a 5 minute window you will get 429. If you go higher and exceed the AWS limit, or we ban you manually or your IP address does not match the IPs in your API restricted_ips list you will get 403." The API runs behind AWS, so "the AWS limit" refers to Companies House's own infrastructure, not to your hosting.
What users report is less clear. There are forum threads about one IP getting 403s while the same key works elsewhere, including a July 2026 report from a production environment with no reply visible. In 2024, users on Azure Virtual Desktop reported 403s on the Companies House website, which is a different service from the API. If you get a 403 from one address only, check Restricted IPs first, then your request volume, then raise it with Companies House and give your IP.
Fix 403 and 400 on the Document API
Document downloads fail in a different way, because the Document API does not serve the file itself. A request to document-api.company-information.service.gov.uk/document/{id}/content with Accept: application/pdf returns 302 Found, and the Location header points to a pre-signed Amazon S3 URL that already contains its own credentials.
Two things go wrong at that point, and we reproduced both on 21 October 2026:
- Your Authorization header goes to S3. S3 accepts only one auth mechanism and returns
400 InvalidArgument: "Only one auth mechanism allowed". Forum users reported this as early as 2018. Pythonrequestsand Bun'sfetchdrop the header on a redirect to another host, but hand-written redirect code often copies it. - The S3 link expires. The signed URL carried
X-Amz-Expires=60. Used 70 seconds later, S3 returned403 AccessDenied: Request has expired. Do not store theLocationURL; request/contentagain when you need the file.
A 403 straight from S3 on a fresh link is rarer and is usually on the Companies House side: in a September 2026 thread, six filings returned AccessDenied until Companies House staff fixed them on 19 October.
r = s.get(f"https://document-api.company-information.service.gov.uk/document/{doc_id}/content",
headers={"Accept": "application/pdf"}, timeout=60) # requests strips auth on the S3 hop
r.raise_for_status()
open(f"{doc_id}.pdf", "wb").write(r.content)
The Document API has its own rate limit headers, covered on the rate limits page.
Browser requests and CORS
The Companies House API does send CORS headers: a preflight from an arbitrary origin returned Access-Control-Allow-Origin: * in our test. In 2024 staff told forum users with CORS errors that "you will need to ensure the api key that you are using has your site in the js_domains array", which is the JavaScript domains field on the key.
That field is not a security control for server traffic: a January 2024 forum post showed a key restricted to certain domains still worked from curl. Anyone who opens your page can read a key shipped in front-end code, so call the API from your own server and keep the key in an environment variable.
How our API authenticates
Our API uses a Bearer token, not Basic auth, so do not mix the two formats. Send Authorization: Bearer ukcd_.... A missing or unknown key returns 401 with a JSON error message, and a used-up monthly quota returns 429. We have no IP allow-list on keys and the API allows cross-origin requests, but the same advice applies: keep the key on your server. The quickstart has working examples.
Getting financial figures without hitting these limits
If you are calling the Document API to get balance sheet numbers, most of these errors disappear when you stop downloading documents. Getting net assets from Companies House takes filing history, document metadata, a redirect, an S3 download and your own iXBRL parser, and about 30% of active companies have no machine-readable accounts at all.
We parse the accounts once and serve the figures as JSON, alongside the register record for every company: 5,704,712 on the 1 October 2026 file (how many companies are in the UK). Field coverage is in Companies House financial data as JSON. A free key with 50 requests a month is one POST away on the developer docs page.
Sources
- Companies House API: authorisation guide
- Companies House API: rate limiting
- Streaming API: authentication
- Developer Hub: how to create an application
- Developer guidelines
- Forum: 403 Forbidden from API on one IP address
- Forum: 403 Forbidden when accessing the API
- Forum: Requests to Officers API returning CORS error and 403
FAQ
Why do I get 401 Unauthorised with a valid Companies House API key?
Usually the header format, the key type or the host is wrong. Send the key as HTTP Basic auth with an empty password, so the header is Basic plus base64 of the key followed by a colon. A REST key will not work on the Streaming API, and a live key will not work on the sandbox host. The Ch-Authentication-Error response header tells you which check failed.
What does 403 Forbidden mean on the Companies House API?
According to Companies House staff on the developer forum, a 403 means your IP does not match the Restricted IPs set on the key, you have gone far above the rate limit, or you were banned manually. Plain rate limiting returns 429. Check the key settings first; changes can take about 10 minutes to apply.
Does Companies House block AWS, Azure or other cloud IP addresses?
No official policy says so. Staff have confirmed 403s for mismatched Restricted IPs, very heavy traffic and manual bans, and some users report individual addresses being blocked. Cloud hosting makes the first cause likely, because your outgoing IP can change. Use a fixed egress IP or clear the list, and contact Companies House if one address stays blocked.
Why does the Document API return 403 or 400 from Amazon S3?
The content endpoint redirects to a pre-signed S3 URL. If your client forwards the Companies House Authorization header, S3 rejects it with 400 because only one auth mechanism is allowed. If you reuse the URL after it expires, about 60 seconds in our tests, S3 returns 403 Request has expired. Follow the redirect without the header and download immediately.
Try it with a free key. 50 requests a month, REST and MCP, no card. Company register, iXBRL financial history since 2008 and watchlist alerts.
Get a free API key