If you run agents in production and you use Langfuse for observability, your traces have always been exportable to a bucket on a schedule. The friction was the format. CSV and JSON land as untyped text, so every downstream query started with a load-and-cast step: parse the CSV, CAST the numeric columns, pull fields out of JSON blobs, then analyze.
As of July 8, 2026, that step is optional. Langfuse's scheduled blob-storage exports can now write Apache Parquet — a columnar binary format with typed columns — alongside the existing CSV, JSON, and JSONL. The payoff is direct: point a query engine at the file and go.
The one-line version#
Here's the end state, so you know what you're setting up. Once exports write Parquet to your bucket, querying last night's agent runs in DuckDB is a single statement — no schema, no load script:
-- How many observations per model, and total spend, from last night's export
SELECT model, count(*) AS calls, sum(total_cost) AS usd
FROM read_parquet('s3://my-bucket/langfuse/observations/2026-07-16/*.parquet')
GROUP BY model
ORDER BY usd DESC;
Because Parquet carries typed columns, total_cost is already a number and model is already a string. No CAST, no CSV dialect flags, no JSON extraction. That's the whole pitch.
Turning it on#
The switch is one field — fileType — on the blob-storage integration.
In the UI: Project Settings → Integrations → Blob Storage → set the file type to Parquet.
Programmatically, read and write the integration config over the public API:
# Inspect the current blob-storage config
curl -s https://cloud.langfuse.com/api/public/integrations/blob-storage \
-u "$LANGFUSE_PUBLIC_KEY:$LANGFUSE_SECRET_KEY"
# Switch it to Parquet (send back the full config with fileType: PARQUET)
curl -s -X PUT https://cloud.langfuse.com/api/public/integrations/blob-storage \
-u "$LANGFUSE_PUBLIC_KEY:$LANGFUSE_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{ "fileType": "PARQUET", "enabled": true }'
Two things worth knowing:
- New integrations already default to Parquet. If you set up blob export today, you get Parquet without touching anything.
- Existing integrations keep their current format. A pipeline that's been writing CSV keeps writing CSV until you flip
fileType. Nothing changes under you — which is the right default, but it means you have to opt in for existing exports.
The schedule (hourly / daily / weekly), the destination (S3, GCS, or Azure Blob Storage), and the datasets (traces, observations, scores) are unchanged. You're only changing what the files are encoded as.
The gotcha that will bite a cost dashboard#
One behavior differs, and it's the kind that fails silently. Parquet observation exports omit the per-unit price columns — input_price, output_price, and total_price are not in the Parquet files.
If you have a dashboard that multiplies tokens by a per-unit price, switching to Parquet will quietly drop those columns and your spend math breaks. The fix is already in the export:
- Use
total_costfor the computed cost of an observation. - Use
cost_detailsfor the breakdown.
Both are present in every file type, Parquet included. So the correct query never touched the per-unit columns in the first place — the example above uses total_cost for exactly this reason. If your current pipeline reads input_price/output_price, rewrite it against cost_details/total_cost before you flip the format, and the switch is invisible.
Why this is worth ten minutes#
The value isn't "Parquet is a nicer format." It's that Parquet collapses the distance between your trace store and the place you actually analyze. A daily Parquet export plus DuckDB gives a solo builder a real analytics stack over their agent behavior — cost per model, latency tails, error clustering, eval scores over time — with zero ETL code. Load the file, write SQL.
And it composes upward without change: the same files that read_parquet() opens in DuckDB load natively into BigQuery, Snowflake, or ClickHouse when you outgrow a laptop. You set the export format once; the warehouse you pick later reads it as-is.
If you're already on Langfuse, this is a ten-minute change — flip fileType, confirm your cost query uses total_cost, point a query engine at the bucket — for a permanent reduction in the glue code between "the agent ran" and "here's what it did and what it cost."



