How to Use Bulk Operations with Streaming in the Ledger API

Last updated: January 16, 2026

This article explains how to use the new streaming feature (starting with ledger 2.2) for bulk operations in the Ledger API, which allows for more efficient processing of large datasets.

Using the Streaming Protocol

To use the streaming feature for bulk operations, follow these steps:

  1. Set the Content-Type header to:
    - application/vnd.formance.ledger.api.v2.bulk+script-stream
    For large Numscript file streaming. Given the size of files, this removes some significant network delay before the ledger starts working.
    - application/vnd.formance.ledger.api.v2.bulk+json-stream
    For JSON content

  2. Use the ?parallel=true query parameter to enable parallel processing (optional)

  3. Structure your request body as a stream of NumScript commands

  • The streaming feature is currently not available in the SDK.

  • When using parallel=true, the API processes scripts concurrently (up to 10 workers by default).

  • The API currently keeps all results in memory until the full stream is complete, which may result in large responses for big datasets.

  •  parallel + atomic is not compatible: atomic imply a single sql transaction, and obviously, things need to be sequential in this case

bulk_json

Request format

JSON elements sent one by one in the stream, separated by a new line :

{"action": "CREATE_TRANSACTION", "data": {"postings": [{"source": "world", "amount": 100, "asset": "USD", "destination": "bank"}]}}
{"action": "CREATE_TRANSACTION", "data": {"postings": [{"source": "world", "amount": 200, "asset": "USD", "destination": "bank"}]}}

Available actions

* CREATE_TRANSACTION

* ADD_METADATA

* REVERT_TRANSACTION

* DELETE_METADATA

bulk_script

Request format

When using the streaming protocol for script, the API reads the connection line by line. Each script should be separated by //script at the beginning and //end at the end.

If using the parameter parallel=true, each time the api will receive a complete script, it will spawn a task to process the script.

Here's an example:

//script
send [USD 100] (
  source = @world
  destination = @accounts:1
)
//end
//script
send [USD 100] (
  source = @world
  destination = @accounts:2
)
//end

Using Idempotency Keys

To prevent duplicate transactions, you can specify an idempotency key for each script. You can use an idempotency key k for each bulk element, in addition to the parallel parameter. In case of failure, you can safely replay the same batch without having duplicates.

Here is an example in Numscript:

//script ik=<your idempotency key>

Example cURL Request

Here's an example of how to make a request using cURL:

curl -X POST \
--header "Authorization: Bearer $(fctl cloud generate-personal-token --organization <ID> --stack <ID>)" \
--header 'Content-Type: application/vnd.formance.ledger.api.v2.bulk+script-stream' \
'https://org-stack.eu.sandbox.formance.cloud/api/ledger/v2/test-001/_bulk?parallel=true' \
--data-binary @/Users/johnDoe/formance/streaming/bulk_stream_script.num
curl -X POST \
--header "Authorization: Bearer $(fctl cloud generate-personal-token --organization <ID> --stack <ID>)" \
--header 'Content-Type: application/vnd.formance.ledger.api.v2.bulk+json-stream' \
'https://org-stack.eu.sandbox.formance.cloud/api/ledger/v2/test-001/_bulk?parallel=true' \
--data-binary @/Users/johnDoe/formance/streaming/bulk_json.json