POST /query returns application/vnd.apache.arrow.stream. Any Apache Arrow library can read
it. There is one thing to get right: the body is several Arrow IPC streams back to back, one
per row type, not one stream.
The response layout
- One complete Arrow IPC stream per row type you asked for, always in the order transactions, then logs, then blocks (whatever order you listed them in).
- Each stream has its own schema, its record batches, and its own end-of-stream marker.
- Every stream’s schema carries the same
bl.*metadata.bl.streamslists the streams in order ("transactions,logs") andbl.row_typenames the current one. - The first schema arrives as soon as the query is planned, before any rows.
- Opens a stream reader on the body and reads it to its end-of-stream marker.
- Reads
bl.streamsfrom its schema, then opens the next stream on the same body, until it has read that many. - Checks that the total rows received equal
bl.objects.
Complete readers
Each of these is a small, complete client that returns Arrow tables: it sends the query, decodes every stream, checks for truncation and reports API errors with their code. Copy one into your project.- Python
- TypeScript
- Rust
Needs
pyarrow and requests (pip install pyarrow requests). query() also retries
rate limits, busy servers and broken connections; see
Errors and retries.bloklake_arrow.py
In any other language, the recipe is the same: open an Arrow IPC stream reader on the response
body, read it to the end, read the next one from the same position, repeat
bl.streams times,
then compare the row count with bl.objects. Go’s ipc.NewReader, Java’s
ArrowStreamReader and C++‘s RecordBatchStreamReader all work this way.Into a dataframe
Arrow tables convert to dataframes without copying the data.Reading incrementally
The readers above hold one response in memory at a time. A response carries about 8 MB of row data at most, so that’s fine for almost everyone, and the pagination loop keeps memory flat across a whole backfill. If you want to process rows while a response is still arriving (lower latency to the first row), iterate the record batches instead of collecting them (for batch in reader in pyarrow,
for await (const batch of reader) in arrow-js). Keep reading promptly: a response that isn’t
read for 60 seconds, or is read slower than 50 KB/s, is
cut off.