Batch processing
Use batch processing for large portfolios or high-volume workloads that should be processed asynchronously in the background. The batch workflow automatically chunks large submissions and tracks progress until completion.
The batch API accepts the same payload as POST /portfolios, returns a batch_id, and lets you track progress until processing completes.
Overview
The typical flow is:
- Submit a portfolio to
POST /portfolios/batch - Poll
GET /portfolios/batch/{batch_id}until the batch reaches a terminal state - Fetch the resulting account outputs from
GET /results/accounts - Filter those results to the account codes that belonged to the submitted batch
This workflow is useful when:
- the portfolio is large
- you want background processing instead of waiting on a synchronous response
- you want queueing and progress tracking
- you want the platform to split large submissions into chunks automatically
How It Works
Batch submission returns:
{
"batch_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}
You then poll the batch status endpoint until status becomes one of:
completedfailedcompleted_with_errors
Once the batch completes, the results are written into the user's live portfolio store. The linked public batch example then retrieves the latest per-account results using GET /results/accounts.
The linked public batch example uses GET /results/accounts after completion, then filters those account-level results down to the submitted account_code values.
Submit the batch
POST /portfolios/batch accepts the same JSON payload as POST /portfolios.
Example request:
{
"calculation_type": "margins",
"vendor_symbology": "clearing",
"portfolio": [
{
"account_code": "account_0000",
"exchange_code": "ICE.EU",
"contract_code": "B",
"contract_type": "FUT",
"contract_expiry": "203212",
"net_position": "100",
"account_type": "H"
}
]
}
The batch endpoint also supports the x-processing-mode header:
x-processing-mode only applies to POST /portfolios/batch. It controls queue handling, not margin calculation.
fifo: default. Batch jobs are processed in submission order.priority: can move the job ahead of the same user's other queued non-priority jobs.replace_all: removes the user's currently queued batch jobs before adding the new one. Jobs already processing are not cancelled.
Poll batch status
Use GET /portfolios/batch/{batch_id} to check progress.
Example response:
{
"batch_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"status": "processing",
"queue_position": 0,
"created_at": "2025-01-03T09:31:14Z",
"completed_at": null,
"runtime_ms": 4523,
"completed_pct": 67.5
}
Poll until status is:
completedfailedcompleted_with_errors
If the batch fails, stop there and do not attempt to fetch results.
Fetch results
The batch example shows the next step after completion:
results_response = requests.get(f"{C9_API_ENDPOINT}/results/accounts", headers=HEADERS)
results_response.raise_for_status()
accounts = results_response.json()
submitted = set(account_codes)
batch_accounts = [a for a in accounts if a["account_code"] in submitted]
This means:
GET /results/accountsreturns the latest calculation for every live account owned by the caller- your client should keep track of the submitted
account_codevalues - after the batch completes, filter the returned accounts down to the ones from that batch
The example then:
- prints how many account results were fetched
- calculates total initial margin across the batch
- lists the top 5 accounts by initial margin
Because GET /results/accounts returns the caller's live account results,
you should explicitly filter by the account codes that were submitted in the
batch.
Batch submissions are limited to 500 MB. Large submissions are chunked automatically during processing.