Formats — CSV & NDJSON
Formats — CSV & NDJSON
Every dataset streams as either CSV or NDJSON. The choice is a query/config value; the same typed schema drives both.
CSV (text/csv)
RFC-4180: one header row of column names, then one row per record. Quoting is done by PHP's
native fputcsv (a comma delimiter, " enclosure, \r\n line ending), so a value
containing a comma, quote or newline is escaped correctly.
Rendering per column type:
| Type | CSV cell |
|---|---|
string |
the value, quoted if needed |
integer |
the number, unquoted |
boolean |
true / false |
timestamp |
ISO-8601 UTC string (e.g. 2026-06-15T10:00:00Z) |
json |
the compact JSON string |
| null | empty field |
CSV is the interchange for spreadsheets and finance/RevOps tooling.
NDJSON (application/x-ndjson)
Newline-delimited JSON — one JSON object per line, no header. This is the format Snowflake, BigQuery and Redshift load natively, and the only meaningful encoding for the raw usage-event stream (which carries typed and nullable fields).
Types are preserved:
| Type | NDJSON value |
|---|---|
string |
JSON string |
integer |
JSON number |
boolean |
JSON true / false |
timestamp |
ISO-8601 UTC string |
json |
a nested JSON object/array (decoded from its stored string, not double-encoded) |
| null | JSON null |
Example line from usage_events:
{"event_id":"evt-1","org":"acme","meter":"api.requests","service":"edge","value":4200,"unique_key":null,"weight":1,"occurred_at":"2023-11-14T22:13:20.000Z","occurred_at_ms":1700000000000}
Note the raw millisecond epoch (occurred_at_ms) is exported alongside the ISO-8601
occurred_at, so a warehouse can re-partition on the exact instant without reparsing.
Money & currency
All money is an integer count of minor units in a *_minor column, paired with the
row's ISO-4217 currency. A consumer scales deterministically (amount_minor / 100 for a
2-decimal currency). No amount is ever exported as a float.