---
title: "Export the whole filtered event set as CSV or JSON"
method: POST
path: "/api/events_export"
tags: ["Events"]
---

# Export the whole filtered event set as CSV or JSON

`POST /api/events_export`

## Request

- Base URL: `https://eyalsec.com`
- URL: `https://eyalsec.com/api/events_export`
- Auth: one of:
  - API key in header `X-API-Key`
  - API key in cookie `__Host-es2session`

Takes the SAME body as /api/get_all_events_with_count/ and returns every matching event as a downloadable file rather than one page as JSON. It shares that endpoint's request parsing, so the filters, the monthly per-view quota, the suppression bucket and the impact-tag predicate all mean exactly what they mean on the events page: the file covers the rows the screen was showing, never a different set. Any cursor in the body is ignored, because an export is the whole set rather than the page you had scrolled to. Unlike the list, every row carries the heavy fields (page URL and call site, origin, repr_created and trace), since a finding you cannot reproduce is not worth exporting. taint_chain is the forensic chain and is blank for non-admin accounts, matching GET /api/event/{id}; the column is present either way so a non-admin file and an admin file stay diffable. The export is capped at 50000 rows: CSV announces the cap as a final row beginning TRUNCATED, and JSON sets truncated to true, because a silent stop at the cap is indistinguishable from having found exactly that many events.

This is a **read**: a read-only API key may call it.

## Query parameters

- `format` string

## Request body

- EventQuery: The events filter. Every field is optional, and each matches a control on the events page.
  - `cursor` string: Opaque pagination cursor from a prior next_cursor.
  - `sort` string: List order: newest (the default) or severity (critical first, then high, medium, low and info, newest first within each; events with no recognised severity last). Both orders list the same events. A cursor only continues the order that produced it: a cursor from the other order is a 400, so start again without one when changing order. Any other value is a 400.
  - `whitelist_repr` string, regex: Show only events whose sink label (the where field) matches this regex, case-insensitively. Despite the name, it is not matched against the repr. An invalid regex is a 400.
  - `blacklist_repr` string, regex: Hide events whose sink label (the where field) matches this regex, case-insensitively. Despite the name, it is not matched against the repr. An invalid regex is a 400.
  - `from` string: Lower time bound (RFC3339 or YYYY-MM-DD).
  - `to` string: Upper time bound (RFC3339 or YYYY-MM-DD, inclusive of the whole day).
  - `machine` string: Restrict to one machine by display name (exact match).
  - `severity` string[]: Severity levels to include: critical, high, medium, low, info. Unknown values are ignored.
  - `sources` string[]: Taint-source categories to include, matched against the event's origin: socket, file, stdin, env, argv, manual, fuzzer, foreign. Unknown values are ignored.
  - `tags` string[]: Restrict to events carrying any of these impact class labels (e.g. sqli, xss). An unknown label is a 400.
  - `labels` integer[]: Restrict to events carrying any of these triage label ids (your own, from GET /api/event_labels/).
  - `xss_only` boolean: es-chromium only: keep just the events whose sink can end in script execution. Must be sent with view=browser; with any other view it is a 400.
  - `scope` object: The events page's scope bar, as {mode, value, include_empty}: mode is is, ends (suffix) or regex, matched against the event's scope key (see scope on GET /api/event_view_counts/). A scope with no mode or no value is ignored; an unknown mode or an invalid regex is a 400.
  - `advanced` object: The condition builder, as {match, conditions}: match is all (default) or any, and each condition is {field, op, value, negate}. The fields available depend on the view; an unknown field or an invalid value is a 400.
  - `view` string: Agent kind to include: python, browser, go, c, cpp, rust, node, solidity, java, bash, ruby or php. Omitted or empty = every view your account holds, which is what the pre-views clients this field exists for still get. Naming a view your account does not hold is a 403; if no products are enabled at all, even the omitted form is a 403. Any unrecognised value is a 400, never a silent empty page.
  - `bucket` string: Sanitizer visibility bucket: live (the default, unsuppressed rows only), suppressed, or all. This was briefly named view during development; view now always means the agent kind above.

## Response `200`

OK.

- object

## Other responses

- `401`: The API key is missing, wrong, revoked or expired.
- `429`: A rate limit was hit, or the account is locked after wrong passwords. Retry-After gives the seconds to wait.
- `default`: An error. Check the status first; the body carries a short message, as JSON ({"error": "..."}) on most endpoints and as plain text on some. 400: malformed JSON, an invalid regex, an unreadable cursor or date, or an unknown value. 401: the API key is missing, wrong, revoked or expired. 403: a failed CSRF check, a read-only key trying to write, a product or feature not enabled on your account, a wrong password, or a write on an account limited to reading. 404: not found, including things that belong to another account. 409: conflict with the current state. 422: a field is missing or invalid. 429: a rate limit was hit, or the account is locked after wrong passwords. 500: something failed on our side.

---

[API](https://skmtc.dev/eyalsec/apis/eyalsec-api.md) · [All operations](https://skmtc.dev/eyalsec/apis/eyalsec-api/llms.txt) · [OpenAPI document](https://skmtc.dev/eyalsec/apis/eyalsec-api/revisions/dc7520689cf1?raw)
