How I scraped the CQC Care Register without hitting the API auth wall
The Care Quality Commission regulates 56,000+ healthcare and social care locations in England — care homes, GP surgeries, hospitals, dental practices, home care agencies. If you work in care sector tech, you've probably needed this data at some point. There's a CQC REST API, and I was planning to wrap it. Then I hit the auth wall. The API is now authenticated CQC migrated their API to api.service.cqc.org.uk and added bearer token authentication. You need to register at their developer portal, create an application, and include an Authorization: Bearer <token> header on every request. That's not a dealbreaker for enterprise use cases, but it creates friction for a data product — it means requiring users to register with CQC before they can run your actor. I checked the old API base ( api.cqc.org.uk/public/v1 ) as a fallback. HTTP 403. Fully blocked. The open-data file rescue CQC publishes a monthly open-data file called HSCA_Active_Locations.ods . It's a 23 MB OpenDocument Spreadsheet with every active regulated location in England — all 56,000 of them. Free, no auth, Open Government Licence. The URL is date-stamped and changes each month, but the transparency page always links to the current version. The approach: scrape the transparency page to find the current ODS URL, download the file, parse it, filter rows, push results. No API. No auth wall. The ODS parsing challenge ODS files ( .ods ) are ZIP archives containing XML. The standard tool for parsing them in Node.js is SheetJS ( xlsx package, v0.18.5 — the last Apache 2.0 release). The first surprise: the workbook has three sheets — README , HSCA_Active_Locations , and Dual_Registration_Locations . SheetJS defaults to the first sheet, which is the README with 34 rows. I added logic to find the sheet with the most rows. for ( const name of workbook . SheetNames ) { const probe = XLSX . utils . sheet_to_json ( sheet , { header : 1 }); if ( probe . length > bestCount ) { bestCount = probe . length ; bestSheet = shee