> AI agents: this is one page from PostHog's docs. Full index of Markdown docs for LLMs: https://posthog.com/llms.txt # File download exports Canonical URL: https://posthog.com/docs/data-pipelines/file-download-exports > **Cloud region:** This page uses US Cloud hosts. For an EU Cloud project, use `https://eu.posthog.com` instead of `https://us.posthog.com` (app host for links). ## On this page - [When to use file download exports](#when-to-use-file-download-exports) - [What you can export](#what-you-can-export) - [Creating an export](#creating-an-export) - [SQL queries](#sql-queries) - [Polling an export](#polling-an-export) - [Downloading the files](#downloading-the-files) - [Canceling an export](#canceling-an-export) - [Billing](#billing) 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. **API-only (for now)** 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](https://posthog.com/docs/data-pipelines/batch-exports.md)** 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](https://posthog.com/docs/api/queries.md)** 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](https://posthog.com/docs/data-pipelines/batch-exports.md#models): - `events`: all events received within a time interval - `persons`: all persons that were updated within a time interval - `sessions`: 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](https://posthog.com/docs/data-pipelines/batch-exports/s3.md#models) 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](https://posthog.com/docs/sql.md) query using the `hogql` model. This is in [closed beta](#sql-queries) (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](https://posthog.com/docs/api/personal-api-keys.md) 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: ```bash curl -X POST \ -H 'Content-Type: application/json' \ -H "Authorization: Bearer $POSTHOG_PERSONAL_API_KEY" \ /api/projects/:project_id/file_download_batch_exports/ \ -d '{ "model": "events", "include": ["$pageview"], "data_interval_start": "2026-08-18T00:00:00Z", "data_interval_end": "2026-08-19T00:00:00Z", "file": { "format": "Parquet", "compression": "zstd" } }' ``` A successful request returns `202 Accepted` with the ID of the export run: ```json { "id": "01991f2e-5f8e-7c1a-b3d4-8a2f9c0e1d2b" } ``` 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`. For `hogql`, required only for the [placeholders](#sql-queries) that the query uses. The range must be at most one week, and neither bound can be in the future. | | `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. | | `hogql_modifiers` | Optional. Settings for how the `hogql` query runs. Set `{"convertToProjectTimezone": true}` to export timestamps in the project timezone, or `{"convertToProjectTimezone": false}` for UTC. Only supported for 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](#canceling-an-export) one you no longer need. ## SQL queries **This model is in closed beta** SQL query exports are enabled per organization. To get access, message us via the [in-app support form](https://us.posthog.com/#panel=support%3Asupport%3Abatch_exports%3Alow%3Atrue). 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](https://posthog.com/docs/sql.md) query, so you aren't limited to the standard models – filter, join, and aggregate whatever you need. To export a query on a schedule instead, use the [SQL query model](https://posthog.com/docs/data-pipelines/batch-exports.md#sql-query-model) of batch exports. For example, to export the URL of every event from the last hour: ```bash curl -X POST \ -H 'Content-Type: application/json' \ -H "Authorization: Bearer $POSTHOG_PERSONAL_API_KEY" \ /api/projects/:project_id/file_download_batch_exports/ \ -d '{ "model": "hogql", "hogql_query": "SELECT event, timestamp, properties.$current_url AS url FROM events WHERE timestamp > now() - INTERVAL 1 HOUR", "file": { "format": "Parquet" } }' ``` A few things to know about SQL query exports: - **Every column needs a name.** Each column in the `SELECT` clause must be a plain field or have an alias, like `count() AS event_count`. - **Only interval placeholders.** The query can use the `{data_interval_start}` and `{data_interval_end}` placeholders. For each placeholder that the query uses, pass a value in the field with the same name. Other placeholders, like `{filters}`, are not supported. - **The time interval only fills placeholders.** `data_interval_start` and `data_interval_end` do not add filters to the query. A query without placeholders runs unchanged, as of the time the export starts. - **Timestamps are in UTC by default.** This also applies to date functions in the query. If your project sets the `convertToProjectTimezone` modifier, exports use that setting instead. To choose the timezone for one export, set `hogql_modifiers` to `{"convertToProjectTimezone": true}` for the project timezone, or `{"convertToProjectTimezone": false}` for UTC. - **Late-arriving events can be missed.** Events can reach PostHog well after their `timestamp`, so a query bounded by `timestamp` may miss events that had not arrived when the export ran. We're adding columns with the time of ingestion to the `events` table and other tables soon, so you'll be able to filter on that instead. Until then, 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 the export to the rows you expect. For example, when you query the `events` table, bound the `timestamp` column: ```sql SELECT event, timestamp, properties.$current_url AS url FROM events WHERE timestamp >= '2026-08-18' AND timestamp < '2026-08-19' ``` If you keep hitting resource limits even with a bounded query, [get in touch](https://us.posthog.com/#panel=support%3Asupport%3Abatch_exports%3Alow%3Atrue) – we want to hear about it. ## Polling an export Export runs are asynchronous. Poll the run with a `GET` request: ```bash curl \ -H "Authorization: Bearer $POSTHOG_PERSONAL_API_KEY" \ /api/projects/:project_id/file_download_batch_exports/:run_id/ ``` 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: ```json { "status": "Completed", "files": ["01991f30-a2c4-7d5e-9f01-3b4c5d6e7f80"] } ``` 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: ```bash curl -L -o export.parquet \ -H "Authorization: Bearer $POSTHOG_PERSONAL_API_KEY" \ /api/projects/:project_id/file_download_batch_exports/:run_id/download/:file_id/ ``` 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: ```bash curl -X POST \ -H "Authorization: Bearer $POSTHOG_PERSONAL_API_KEY" \ /api/projects/:project_id/file_download_batch_exports/:run_id/cancel/ ``` 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](https://posthog.com/pricing.md) for details. Pricing for SQL query exports hasn't been decided yet, but they're free while the `hogql` model is in closed beta.