How to convert a stripe json export to csv for accounting
- Step 1Save the Stripe API response — Pull the data you need — e.g.
GET /v1/charges,/v1/invoices, or/v1/payouts— and save the JSON. Stripe returns a list object{ "object": "list", "data": [...] }. Drop the saved file onto the converter (free files cap at 2 MB). - Step 2Extract the `data` array if the envelope has scalars — Stripe's list response wraps
dataalongside scalar fields (object,has_more,url). Because not every top-level value is an array, extract$.datafirst with json-path-extractor so you get one row per item rather than a single wide row. - Step 3Keep flattening on — Flatten nested objects (default) turns
billing_details.address.countryandmetadata.order_idinto individual columns — exactly what reconciliation needs. - Step 4Choose a delimiter for your books — Comma for standard import; Semicolon for European Excel where commas are decimal separators; Tab to paste into Sheets.
- Step 5Convert and verify amounts — Click Convert to CSV. Remember amounts are in the smallest unit (cents/pence) and timestamps are Unix epochs — both are intact but need conversion in your spreadsheet. Check the row count matches your expected transaction count.
- Step 6Download for reconciliation — Use Excel-Ready CSV (BOM + CRLF) so amounts and currency codes open cleanly. For QuickBooks/Xero column mapping, see the accounting guide cross-linked below.
Common Stripe fields after conversion
How nested Stripe charge fields map to flat CSV columns. The converter never alters values.
| Stripe field | CSV column | Note |
|---|---|---|
amount | amount | Smallest unit — 4900 = 49.00; no conversion applied |
currency | currency | ISO code, e.g. usd — pair with amount to interpret |
created | created | Unix epoch integer — convert in spreadsheet |
billing_details.address.country | billing_details.address.country | Dot-notation from the nested address object |
metadata.order_id | metadata.order_id | Your custom metadata, flattened |
refunds (array) | refunds | One cell — list of refund objects, not expanded |
Stripe object types and recommended prep
All Stripe list responses share the same envelope shape — extract data first.
| Object | Endpoint | Prep before convert |
|---|---|---|
| Charges | /v1/charges | Extract $.data, then flatten |
| Invoices | /v1/invoices | Extract $.data; lines.data arrays stay as cells |
| Payouts | /v1/payouts | Extract $.data, flatten |
| Balance transactions | /v1/balance_transactions | Extract $.data; ideal for fee reconciliation |
Cookbook
Real Stripe response shapes and the reconciliation CSV the converter produces. Card and customer data anonymised.
Charges list, extracted then flattened
ExampleStrip the list envelope first so each charge becomes a row; nested fields flatten to columns.
Input (after extracting $.data):
[
{ "id":"ch_1", "amount":4900, "currency":"usd", "billing_details":{"address":{"country":"US"}} }
]
Output CSV:
id,amount,currency,billing_details.address.country
ch_1,4900,usd,USAmounts stay in the smallest unit
ExampleThe converter applies no currency math. A 4900-cent charge stays 4900; divide by 100 in your spreadsheet to show 49.00.
Input:
[ { "id":"ch_2", "amount":1250, "currency":"gbp" } ]
Output CSV:
id,amount,currency
ch_2,1250,gbp
In Excel: =amount/100 → 12.50Custom metadata flattened to columns
ExampleYour defined metadata object becomes one dot-notation column per key, ideal for matching to your own order system.
Input:
[ { "id":"ch_3", "metadata":{"order_id":"A100","channel":"web"} } ]
Output CSV:
id,metadata.order_id,metadata.channel
ch_3,A100,webOptional fields kept aligned
ExampleOnly the disputed charge has a dispute field; the union header keeps the column and leaves the other row empty.
Input:
[
{ "id":"ch_4", "amount":4900 },
{ "id":"ch_5", "amount":2000, "dispute":"dp_1" }
]
Output CSV:
id,amount,dispute
ch_4,4900,
ch_5,2000,dp_1Timestamps stay as Unix epochs
ExampleThe converter does not interpret created. Convert the epoch in your spreadsheet for a readable date.
Input:
[ { "id":"ch_6", "created":1717200000 } ]
Output CSV:
id,created
ch_6,1717200000
In Excel: =(created/86400)+DATE(1970,1,1) → a dateErrors and edge cases
Real errors and silent failures sourced from each platform's own documentation. Match the wording to the row, fix what the row says to fix.
Whole list collapses into one row
By designStripe's { "object":"list", "data":[...], "has_more":true } mixes the data array with scalar fields, so the converter treats the top level as a single record. Extract $.data first with json-path-extractor to get one row per charge.
Amounts look 100x too large
By designStripe amounts are in the smallest currency unit; 4900 is $49.00. The converter does no division so the books stay exact — divide by 100 (or the unit for zero-decimal currencies like JPY) in your spreadsheet.
`created` is a big integer, not a date
By designStripe timestamps are Unix epochs. The converter keeps the integer as-is; format it as a date in your spreadsheet rather than expecting a parsed value.
`refunds.data` or `lines.data` arrays in one cell
Not expandedNested arrays of objects (refunds, invoice lines) stay as a JSON literal in one cell. For a row per line item, extract that array path with json-path-extractor and convert it separately.
Paginated export missing transactions
ExpectedStripe lists page with has_more / starting_after. A single response only holds one page. Concatenate pages before converting, or you will reconcile against an incomplete set.
Export over the free 2 MB limit
BlockedFree conversions cap at 2 MB / 500 rows. A full month of charges usually needs Pro (100 MB / 100,000 rows) or splitting by date range.
Currency mix in one file
SupportedMulti-currency charges convert fine — each row carries its own currency column. Just remember the amount interpretation depends on that per-row currency (zero-decimal currencies have no /100 division).
Invalid or truncated JSON
Invalid JSONA truncated API copy errors with Invalid JSON. Re-fetch the full response; validate with json-validator if unsure.
Frequently asked questions
Why does my whole Stripe list become one row?
Stripe wraps data alongside scalar fields like has_more and url, so the top level is not a pure array. Extract $.data first with json-path-extractor, then convert that array for one row per transaction.
Why are the amounts 100x too big?
Stripe stores amounts in the smallest currency unit — cents, pence — so 4900 means 49.00. The converter does no currency math to keep the books exact. Divide by 100 in your spreadsheet (and note zero-decimal currencies like JPY have no division).
How do I turn `created` into a real date?
It is a Unix epoch integer, preserved as-is. Convert it in your spreadsheet — for Excel: =(created/86400)+DATE(1970,1,1) then format as a date.
Does it flatten my custom metadata?
Yes. Each key under metadata becomes a metadata.<key> column, which is ideal for matching Stripe charges to your own order IDs.
What about refunds and invoice line items?
Those nested arrays stay in a single cell as JSON. For one row per refund or line, extract that array path with json-path-extractor and convert it on its own.
Will optional fields like disputes break the columns?
No. The union-of-keys header includes every field seen across all records, leaving empty cells where a charge lacks it. Columns stay aligned.
Is my payment data uploaded?
No. All parsing and conversion happen in your browser. Card and customer data in the export never reaches a server.
How do I handle a full month of charges?
Stripe paginates, so concatenate the pages first. Then convert — likely on Pro (100 MB / 100,000 rows), since a month often exceeds the free 2 MB / 500-row cap.
Can I use this output for QuickBooks or Xero?
Yes — after converting, map the columns for your ledger. The accounting import guide covers QuickBooks/Xero delimiter and column tips, including the semicolon option for European books.
Does it support a semicolon delimiter for EU Excel?
Yes. Switch the delimiter to Semicolon so amounts and currency codes land in separate columns in European-locale Excel.
My export has multiple currencies — is that a problem?
No. Each row carries its own currency column. Just interpret each amount against its row's currency, since the smallest-unit scaling differs by currency.
How do I get this CSV back into JSON if needed?
Use csv-to-json to reverse the conversion, for example to re-feed reconciled data into a downstream system.
Privacy first
Conversion runs locally in your browser. No file is uploaded — only metadata counters are saved for signed-in dashboard stats.