Bulk exports
Download the complete bills mirror as a stream designed for offline copies: gzip on the wire, one newline-delimited record at a time, and no client-side cursor loop.
Authentication
This is a programmatic API endpoint. Send an API key in the Authorization header on every request:
curl --compressed https://api.archivist.dev/v1/exports/bills \
-H "Authorization: Bearer $ARCHIVIST_KEY"The export uses bearer-key authentication rather than browser sessions or cookies. Missing or invalid keys return 401; revoked or suspended access returns 403. See Errors & limits for the shared status reference.
Stream behavior
The endpoint has no query parameters and no cursor to follow. The server walks the full mirror internally in 500-row pages, then yields records through a pull-driven gzip pipeline so consumers can pipe the response to disk without buffering the export in memory.
- 200 OK. The success headers include
Content-Type: application/x-ndjsonandContent-Encoding: gzip. - The decoded body is newline-delimited JSON: every line is one
BillItemobject followed by\n. - Records are ordered by
congress DESC, number DESC, id DESC. Content-Dispositionisattachment; filename="bills.ndjson.gz".
Download with curl
Use --compressed so curl advertises and transparently decodes the gzip response before writing plain NDJSON to disk. The same decoded stream can be piped directly to jq.
# Stream the full bills mirror; curl decodes Content-Encoding: gzip
curl --compressed --fail-with-body \
-H "Authorization: Bearer $ARCHIVIST_KEY" \
-o bills.ndjson \
https://api.archivist.dev/v1/exports/bills
# Inspect the decoded NDJSON while piping
curl --compressed --fail-with-body \
-H "Authorization: Bearer $ARCHIVIST_KEY" \
https://api.archivist.dev/v1/exports/bills \
| jq -c .Consume with JavaScript
Standard Node and browser fetch implementations honor Content-Encoding: gzip before exposing response.body. Read chunks incrementally and split on newline boundaries; do not run the normally decoded body through a second gunzip.
const res = await fetch('https://api.archivist.dev/v1/exports/bills', {
headers: { Authorization: `Bearer ${process.env.ARCHIVIST_KEY}` },
});
if (!res.ok) {
const errorBody = await res.text();
throw new Error(`Archivist ${res.status}: ${errorBody}`);
}
if (!res.body) throw new Error('Archivist returned no response body');
// Standard Node/browser fetch honors Content-Encoding: gzip before exposing response.body.
// Do not add a second gunzip. Use DecompressionStream('gzip') only when a lower-level
// client explicitly exposes raw compressed bytes.
const reader = res.body.getReader();
const decoder = new TextDecoder();
let buffer = '';
for (;;) {
const { value, done } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
const lines = buffer.split('\n');
buffer = lines.pop() ?? '';
for (const line of lines) {
if (!line.trim()) continue;
const bill = JSON.parse(line);
console.log(`${bill.id} — ${bill.officialTitle}`);
}
}
buffer += decoder.decode();
if (buffer.trim()) {
const bill = JSON.parse(buffer);
console.log(`${bill.id} — ${bill.officialTitle}`);
}Response schema
The response is a stream, not a JSON envelope. The table describes the success headers and the fields on each decoded BillItem line.
| Field | Type | Required | Description |
|---|---|---|---|
| status | 200 OK | required | The export stream opened successfully. |
| Content-Type | application/x-ndjson | required | The decoded response body is newline-delimited JSON. |
| Content-Encoding | gzip | required | The body is gzip encoded on the wire. curl --compressed and standard fetch clients decode it before exposing the body. |
| Content-Disposition | attachment | required | Provides the download filename bills.ndjson.gz. |
| body | NDJSON stream | required | One BillItem object per line, with each line terminated by "\n". |
| body[].id | string | required | Stable bill id in the form {congress}-{type}-{number}. |
| body[].congress | integer | required | Congress number, for example 118 or 119. |
| body[].type | string | required | Bill type, for example hr, s, or sjres. |
| body[].number | integer | required | The bill number within its type and Congress. |
| body[].officialTitle | string | required | Official title from the bill mirror. |
| body[].introducedDate | string | null | required | ISO 8601 calendar date, or null when unavailable. |
| body[].memberName | string | null | required | Sponsor name, or null when no member is linked. |
| body[].memberBioguideId | string | null | required | Sponsor Bioguide id, or null when unavailable. |
| body[].memberByCongressKey | string | null | required | Congress-scoped sponsor key, or null when unavailable. |
| body[].committeeNames | string | required | Comma-separated committee names from the mirror. |
| body[].sourceUrl | string | null | required | Canonical source URL, or null when unavailable. |
| body[].lastSyncedAt | string | required | ISO 8601 timestamp for the last mirror sync. |
{"id":"118-hr-3076","congress":118,"type":"hr","number":3076,"officialTitle":"Civic Data Continuity Act","introducedDate":"2023-09-12","memberName":"Pelosi, Nancy","memberBioguideId":"P000197","memberByCongressKey":"118-h-P000197","committeeNames":"House Oversight,House Administration","sourceUrl":"https://www.congress.gov/bill/118th-congress/house-bill/3076","lastSyncedAt":"2026-04-22T03:11:08.412Z"}
{"id":"118-hr-3077","congress":118,"type":"hr","number":3077,"officialTitle":"…","introducedDate":null,"memberName":null,"memberBioguideId":null,"memberByCongressKey":null,"committeeNames":"","sourceUrl":null,"lastSyncedAt":"2026-04-22T03:11:08.418Z"}Notes
- The endpoint accepts no query parameters and exposes no cursor; one authenticated call covers the full mirror.
- The export uses one rate-limit tick per call regardless of dataset size. A 429 response includes Retry-After with the next retry window.
- Authentication and setup failures return JSON errors: 401 for missing or invalid bearer tokens, 403 for revoked or suspended access, and 500 for an unexpected setup failure.
- Content-Disposition is attachment; filename="bills.ndjson.gz". The filename describes the wire encoding; curl --compressed and native fetch expose decoded NDJSON.
- Internally the handler reads 500-row cursor pages and yields each line on demand, ordered by congress DESC, number DESC, id DESC.
- If a database or gzip failure occurs after the 200 response starts, the connection terminates; treat an incomplete NDJSON line or file as a failed export.