File download exports
Contents
File download exports let you export PostHog data on demand and download the results as Parquet or JSON Lines files. You start an export with an API call, poll until it completes, and download the files – no destination setup, no schedule.
File download exports are currently only available through the API. A UI is coming soon.
When to use file download exports
Use file download exports when you want a one-off copy of your data as files: running some ad-hoc analysis, sharing a dataset with a colleague, or doing a one-time load into another tool.
Use batch exports when you want recurring, scheduled delivery of data to a destination you control, like S3, BigQuery, or Snowflake. This is the right choice for ETL and warehouse syncs.
Use the /query API when you want small, interactive result sets in an API response, like powering embedded analytics. It is not designed to be an export tool.
What you can export
A file download export can export any of the standard batch export models:
events: all events received within a time intervalpersons: all persons that were updated within a time intervalsessions: all sessions that occurred within a time interval
Exports of these models require a time interval (data_interval_start and data_interval_end) of at most one week. For longer ranges, run multiple exports.
File download exports write the same files as an S3 batch export, so the schemas are identical. See the S3 model schemas for the fields in each model, and for how the types differ between Parquet and JSON Lines.
You can also export the results of an arbitrary SQL query using the hogql model. This is in closed beta (see below).
Creating an export
Start an export with a POST request to the file_download_batch_exports endpoint. You need a personal API key with the batch_export:write scope (and batch_export:read to poll and download).
For example, to export all $pageview events from a single day as zstd-compressed Parquet:
A successful request returns 202 Accepted with the ID of the export run:
The request body accepts:
| Field | Description |
|---|---|
model | One of events, persons, sessions, or hogql (currently in closed beta). |
data_interval_start, data_interval_end | ISO 8601 datetimes bounding the export. Required for events, persons, and sessions. The range must be at most one week, and the end cannot be in the future. Not supported for hogql. |
include, exclude | Optional lists of event names to include or exclude. Only supported for the events model. |
hogql_query | The SQL query to export. Required for (and only supported by) the hogql model. |
file.format | Parquet (default) or JSONLines. |
file.compression | Optional. zstd, lz4, snappy, gzip, or brotli for Parquet. Only gzip and brotli for JSONLines. |
file.max_size_mb | Optional. Splits the output into multiple files of at most this size, instead of one potentially large file. |
Each project can have up to 20 file download exports running at the same time. Requests beyond that return 429 Too Many Requests. If you hit this limit, wait for a running export to finish, or cancel one you no longer need.
SQL queries
SQL query exports are enabled per team. To get access, message us via the in-app support form. We're actively looking for feedback on this feature, and we'd love to hear about the use cases and queries you want to run.
The hogql model exports the results of any SQL query, so you aren't limited to the standard models – filter, join, and aggregate whatever you need:
A few things to know about SQL query exports:
- Every column needs a name. Each column in the
SELECTclause must be a plain field or have an alias, likecount() AS event_count. - No placeholders. Placeholder syntax like
{filters}is not supported yet, so write the query out in full. - No time interval. The query runs as of the time the export starts, so
data_interval_startanddata_interval_endare not accepted. Bound the data in the query itself instead. - Late-arriving events can be missed. Events can reach PostHog well after their
timestamp, so a query bounded bytimestampmay miss events that had not arrived when the export ran. (The standardeventsmodel reads from an internal table built to avoid this, which SQL queries do not have access to currently.) If completeness matters, leave a margin behind the present or re-run the export later.
Bound your queries
Since user-supplied queries are more unpredictable in nature, they run under stricter resource limits than the standard models. They are subject to limits on execution time, memory usage, and the volume of data read. A query that exceeds any of them fails with an error.
Always narrow your query with a WHERE clause. This keeps you inside the resource limits, and it keeps you from being billed for more rows than you expected. For example, when you query the events table, bound the timestamp column:
If you keep hitting resource limits even with a bounded query, get in touch – we want to hear about it.
Polling an export
Export runs are asynchronous. Poll the run with a GET request:
The response contains the run's status:
| Status | Meaning |
|---|---|
Starting, Running | The export is in progress. Wait and poll again. |
Completed | The export finished. The response includes a files array with the ID of each exported file. |
Cancelled | The export was canceled. |
Failed, FailedBilling | The export did not finish. The response includes an error field with details. |
A completed run looks like this:
Exports usually complete within a few minutes, but large exports can take longer – narrowing the date range or filtering events will help speed things up. To list all of a project's export runs, send a GET request to the collection endpoint without a run ID.
Downloading the files
Once the run is Completed, download each file in the files array:
The endpoint responds with a 302 redirect to a temporary signed URL, so make sure your HTTP client follows redirects (-L in curl). The signed URL carries its own authentication, so do not send your API key with it. The URL expires after an hour. If it expires, call the download endpoint again for a fresh one. Treat the signed URL as a secret: anyone who has it can download the file while it is valid.
Exported files are kept for one week. After that they are deleted, the download endpoint returns an error, and you need to run the export again.
In place of the file ID, you can use a zero-based index (.../download/0/). If the export produced a single file, you can omit the file identifier entirely (.../download/). If you set file.max_size_mb, remember to download every file in the files array, not just the first.
Canceling an export
Cancel a run that is still Starting or Running with a POST request:
A canceled run cannot be resumed. Start a new export instead.
Billing
File download exports are billed the same way as batch exports: based on the number of rows exported. See our pricing page for details.