---
title: 'Upload CSV Files'
api: 'POST /api/upload-csv'
description: 'Upload and process transaction history and realized gains CSV files from brokerage'
---
## Endpoint
```
POST /api/upload-csv
```
## Authentication
Requires OAuth 2.0 authentication and user must have brokerage account number set in profile.
## Content Type
`multipart/form-data`
## Parameters
Transaction history CSV file from your brokerage
Realized gains/losses CSV file from your brokerage
## Response Format
The endpoint returns streaming JSON responses to provide real-time progress updates:
```json
{"type": "progress", "percentage": 25, "message": "Starting data processing..."}
{"type": "progress", "percentage": 40, "message": "Processing transaction history and realized gains..."}
{"type": "progress", "percentage": 60, "message": "Synchronizing with database..."}
{"type": "progress", "percentage": 80, "message": "Finalizing data import..."}
{"type": "success", "message": "Successfully processed files", "stats": {"transactions_inserted": 279, "realized_gains_lots": 713, "broker_verified_trades": 156}}
```
## Success Response Fields
Response type: "progress", "success", or "error"
Progress percentage (0-100) for progress type responses
Human-readable status message
Import statistics (only in success response)
Number of transactions inserted
Number of realized gain/loss lots processed
Number of broker-verified completed trades
## Example
```javascript JavaScript with Progress
const formData = new FormData();
formData.append('transaction_history_file', transactionFile);
formData.append('realized_gains_file', gainsFile);
const response = await fetch('/api/upload-csv', {
method: 'POST',
body: formData
});
// Handle streaming response
const reader = response.body.getReader();
const decoder = new TextDecoder();
while (true) {
const { done, value } = await reader.read();
if (done) break;
const text = decoder.decode(value);
const lines = text.split('\n').filter(l => l.trim());
for (const line of lines) {
const data = JSON.parse(line);
if (data.type === 'progress') {
console.log(`Progress: ${data.percentage}% - ${data.message}`);
} else if (data.type === 'success') {
console.log('Upload complete!', data.stats);
} else if (data.type === 'error') {
console.error('Upload failed:', data.message);
}
}
}
```
```python Python
import requests
files = {
'transaction_history_file': open('transactions.csv', 'rb'),
'realized_gains_file': open('gains.csv', 'rb')
}
response = requests.post(
'https://performance.miningwood.com/api/upload-csv',
files=files,
stream=True
)
for line in response.iter_lines():
if line:
data = json.loads(line)
if data['type'] == 'progress':
print(f"Progress: {data['percentage']}%")
elif data['type'] == 'success':
print("Upload successful!", data['stats'])
```
```bash cURL
curl -X POST https://performance.miningwood.com/api/upload-csv \
-H "Cookie: session=your_session_cookie" \
-F "transaction_history_file=@transactions.csv" \
-F "realized_gains_file=@realized_gains.csv"
```
## Error Responses
```json
{"type": "error", "message": "Both files must be CSV files"}
```
HTTP Status: `400 Bad Request`
```json
{"type": "error", "message": "Brokerage account number not set. Please update your profile."}
```
HTTP Status: `400 Bad Request`
```json
{"type": "error", "message": "Both transaction history and realized gains files are required"}
```
HTTP Status: `400 Bad Request`
## CSV File Requirements
### Transaction History File
Should include columns such as:
- Transaction date
- Symbol
- Action (Buy, Sell, etc.)
- Quantity/Shares
- Price
- Amount
- Account number
### Realized Gains File
Should include columns such as:
- Symbol
- Date acquired
- Date sold
- Proceeds
- Cost basis
- Gain/Loss
File format requirements may vary by brokerage. The system attempts to automatically detect and parse common formats.
## Related Endpoints
View history of previous uploads
Learn more about CSV upload functionality