Ingest · data
Flat Files
The corpus as parquet, one file per source per day, each with its own sha256 in a manifest. Download it once and work offline; the manifest states exactly which corpus version you are holding.
- LIVE
- REST
- parquet
- signed GCS
Power and up.
The artifact
One partition, and a query over it
/v1/files lists every partition with its hash, its
byte count and a time-limited signed GCS URL.
$ curl -s -H "Authorization: Bearer $PIT_API_KEY" \
'https://api.pit.aqx.llc/v1/files?source=sec.edgar'
{
"id": "sec.edgar/2023/03/10.parquet",
"source": "sec.edgar",
"partition": "2023-03-10",
"bytes": 998952,
"sha256": "95db284f431d7dccf15a9cf0539eae379d22aff5097b539a3911ce7f7d4267bd",
"storage": "gcs",
"gcs_uri": "gs://pit-aqx-corpus-prod/sec.edgar/2023/03/10.parquet",
"url": "https://storage.googleapis.com/… (signed, time-limited)",
"expires_at": "…",
"corpus_version": "sha256:16cb590e…"
}
One entry from the listing. bytes and
sha256 are this build's real values for that partition;
url and expires_at are minted per request and
are shown here as their shape.
Once it is on disk it is an ordinary parquet file. This is DuckDB reading the partition above with no PIT code in the process at all.
$ duckdb -c "select form, count(*) as rows from 'sec.edgar/2023/03/10.parquet' group by 1 order by 2 desc limit 5" ┌──────────┬───────┐ │ form │ rows │ │ varchar │ int64 │ ├──────────┼───────┤ │ 4 │ 2071 │ │ 8-K │ 383 │ │ SC 13G/A │ 215 │ │ 10-K │ 151 │ │ 6-K │ 111 │ └──────────┴───────┘ $ shasum -a 256 sec.edgar/2023/03/10.parquet 95db284f431d7dccf15a9cf0539eae379d22aff5097b539a3911ce7f7d4267bd
Run on 2026-08-27 against the corpus in this build. The hash is the one
the manifest and the FileObject above both carry, so the
three agree or the download is wrong.
The failure it prevents
Re-running a result six months later
A result computed against a live endpoint depends on what that endpoint held the day you ran it. The corpus moves under you: the tape adds a day every day, and SEC rewrites its own index files on Saturday mornings, folding in corrections taken after acceptance. Six months later the same query can return a different answer, and nothing in the old result says which corpus produced it.
Every file here carries a sha256, and every listing carries the
corpus_version the files were cut from. Pin that string in
your run and the comparison holds: two runs over the same
corpus_version read the same bytes, and a run over a
different one is a different experiment that says so up front.
The API path has the same property in a weaker form. Reading a whole
window through /v1/news means a few thousand paginated
requests against a corpus that can publish underneath you, which is why
a cursor is bound to corpus_version and expires when the
corpus does. For a full-window read the files are the cheaper and more
stable path.
How it works
Partitions, hashes, and a manifest
Layout
One parquet file per source per day, keyed
<source>/<yyyy>/<mm>/<dd>.parquet.
The corpus in this build is 415 such files across seven sources. The
columns are the served row, field for field, so a query written
against the API's JSON reads the parquet without a translation
layer.
The manifest
MANIFEST.json lists every file with its
sha256, bytes and row_count.
Its own hash is the corpus_version that
/v1/meta reports and that every API response repeats, so
the same string identifies the corpus whether you queried it or
downloaded it.
Delivery
url is a signed HTTPS GET against Google Cloud Storage
and gcs_uri is the same object for a client that speaks
gs://. There is no S3 mirror and no packed archive
format. On a local backend there is no bucket, so
url, gcs_uri and expires_at
are JSON null and id is the locator; the server's own
filesystem path is never published.
Timestamps
The four optional clocks — event_at,
available_at, acceptance_at and
supersedes — are the only nullable columns.
Every string column is required and carries '' when it
holds nothing, so where cik is null matches no rows and
where cik = '' matches the ones you meant.
Access
Power and Desk plans read /v1/files. A Researcher key
gets 403 with plan_required rather than a partial
listing.
Integration
pandas, DuckDB, or the harness
import duckdb
con = duckdb.connect()
con.sql("set timezone='UTC'")
con.sql("""
select form, count(*) as rows
from 'sec.edgar/2023/03/*.parquet'
where published_at <= timestamp '2023-03-10 23:59:59+00'
group by 1 order by 2 desc
""").show()
The as-of rule is the same one the API applies: a row is in the cut
when the clock you chose is at or before your instant. What the files
do not carry is the coverage certificate, so a query over a day the
corpus never read returns zero rows instead of refusing. Read
/v1/coverage alongside the files, or take the certificates
that ship inside a benchmark
release, where they are a line per calendar day.
pandas.read_parquet and
datasets.load_dataset("parquet", …) both read these files
directly. Nothing in the path needs a PIT SDK.
Related
Upstream and downstream
- Point-in-Time API
- The same rows through a query, with a coverage certificate and a refusal where the corpus has a hole.
- Benchmark Datasets
- A frozen window of these partitions with control arms, day-by-day receipts, and a content digest to pin.
- Live Feed
- The job that writes today's partition, every fifteen minutes.
- Coverage reference
- What a certificate asserts, and how a partial day differs from a missing one.