// Node.js 20+. No packages required. Optional key: DATASCOOP_API_KEY. const endpoint = "https://datascoop.io/v1/datasets/cqc/query"; const dataset = "cqc"; const filters = { "careHome": "true", "localAuthorityExact": "Devon", "serviceType": "Nursing homes" }; const PAGE_SIZE = 25, MAX_ROWS = 2500, MAX_PAGES = 100; const headers = { Accept: "application/json" }; const key = (process.env.DATASCOOP_API_KEY || "").trim(); if (key) headers.Authorization = "Bearer " + key; async function retrieve() { const rows = []; let total = null, importedAt = null, first = null; for (let page = 0; page < MAX_PAGES; page++) { const url = new URL(endpoint); url.search = new URLSearchParams({ ...filters, strict_filters: "1", limit: String(PAGE_SIZE), offset: String(rows.length) }); const response = await fetch(url, { headers, signal: AbortSignal.timeout(30000) }); if (!response.ok) throw new Error("Query failed: HTTP " + response.status + ". Check filters and access; retry later for rate limits or server errors."); const result = await response.json(); if (result.dataset !== dataset || !Array.isArray(result.rows) || result.rows.some(row => !row || typeof row !== "object" || Array.isArray(row)) || !Array.isArray(result.ignored) || result.ignored.length || !Number.isSafeInteger(result.total) || result.total < 0 || result.offset !== rows.length || result.returned !== result.rows.length || result.returned > PAGE_SIZE || typeof result.truncated !== "boolean") { throw new Error("Unexpected query response; no partial result was printed."); } if (result.total > MAX_ROWS) throw new Error("More than " + MAX_ROWS + " rows match. Narrow the filters or review the bound before increasing it."); for (const [name, value] of Object.entries(filters)) { if (String(result.applied_filters?.[name]) !== String(value)) throw new Error("A requested filter was not applied: " + name); } const imported = result.provenance?.imported_at; if (typeof imported !== "string" || !Number.isFinite(Date.parse(imported))) throw new Error("Import date is missing; no partial result was printed."); if (first === null) { first = result; total = result.total; importedAt = imported; } if (result.total !== total || imported !== importedAt) throw new Error("The import or match count changed. Run the request again."); const next = rows.length + result.returned; if (next > total || result.truncated !== (next < total) || (result.returned === 0 && next < total)) throw new Error("Pagination stopped making sense; no partial result was printed."); rows.push(...result.rows); if (rows.length === total) return { dataset, filters, total, rows, provenance: first.provenance, licence: first.licence, attribution: first.attribution }; } throw new Error("Page limit reached. Narrow the filters; no partial result was printed."); } try { const result = await retrieve(); if (result.total === 0) console.error("No matching records. Check the selected area and filters."); // Keep provenance and attribution with cached or republished data. console.log(JSON.stringify(result, null, 2)); } catch (error) { console.error(error instanceof Error ? error.message : "Request failed."); process.exitCode = 1; }