Convert bank statement PDFs to structured CSV and JSON data programmatically. Built for accounting platforms, fintech apps, and data pipelines.
Create an account and generate an API key from the dashboard.
Send your bank statement PDF via the upload endpoint.
Start conversion — AI extracts every transaction with high accuracy.
Retrieve structured data as CSV or JSON.
All API requests require an API key sent in the X-API-Key header.
https://api.bank-statements.co/api/v1
curl -H "X-API-Key: bsco_YOUR_API_KEY" \
https://api.bank-statements.co/api/v1/balance
Key security: API keys are hashed with SHA-256 before storage. The full key is only shown once at creation. Keep it secret — anyone with your key can access your account.
/balance
Returns your current credit balance with a per-purchase breakdown including expiry dates.
{
"status": "success",
"data": {
"total_credits": 288,
"records": [
{
"balance": 149,
"purchase_date": "2024-06-14 14:17:06",
"expiry_date": "2028-10-27 23:07:52"
}
]
}
}
/files
List all your uploaded files with their conversion status, page count, and output locations.
{
"status": "success",
"data": [
{
"filename": "user_abc123.pdf",
"original_filename": "jan-2024-statement.pdf",
"pages": 5,
"uploaded_at": "2024-06-14 14:17:06",
"is_converted": true,
"csv_location": "csv/user_abc123.csv",
"json_location": "json/user_abc123.json"
}
]
}
/upload
Upload a PDF bank statement. Send as multipart/form-data with the file in the file field. Max 50MB.
# Request
curl -X POST -H "X-API-Key: bsco_YOUR_KEY" \
-F "file=@statement.pdf" \
https://api.bank-statements.co/api/v1/upload
# Response
{
"status": "success",
"data": {
"filename": "user_a1b2c3d4.pdf",
"original_filename": "statement.pdf",
"pages": 12
}
}
/convert/{filename}
Start converting a PDF. Costs 1 credit per page. The conversion runs in the background — poll the status endpoint to check progress.
{
"status": "success",
"data": {
"filename": "user_a1b2c3d4.pdf",
"total_pages": 12,
"credits_used": 12,
"conversion_status": "processing"
}
}
/convert/{filename}/status
Check conversion progress. Poll this endpoint every 3-5 seconds until conversion_status is completed or failed.
{
"status": "success",
"data": {
"conversion_status": "processing",
"current_page": 7,
"total_pages": 12
}
}
/download/{filename}?format=csv|json
Download a converted file. Returns a pre-signed download URL valid for 1 hour. Default format is csv.
{
"status": "success",
"data": {
"download_url": "https://s3.amazonaws.com/...",
"format": "csv",
"expires_in": 3600
}
}
/files/{filename}
Delete an uploaded file and all its conversions from S3 and the database. This action is irreversible.
{
"status": "success",
"data": {
"filename": "user_a1b2c3d4.pdf",
"deleted": true
}
}
The API uses standard HTTP status codes. Errors return a JSON body with a detail field.
| Status | Meaning |
|---|---|
| 200 | Success |
| 400 | Bad request (invalid PDF, missing params) |
| 401 | Invalid or missing API key |
| 402 | Insufficient credits |
| 404 | File not found |
| 429 | Rate limit exceeded (60 req/min per key) |
| 500 | Server error |
| Limit | Value |
|---|---|
| API requests | 60 per minute per API key |
| Concurrent conversions | 3 per user |
| Max file size | 50 MB |
| Conversion timeout | 30 minutes |
| API keys per account | 5 |
| Download URL expiry | 1 hour |
import requests
import time
API_KEY = "bsco_YOUR_API_KEY"
BASE_URL = "https://api.bank-statements.co/api/v1"
HEADERS = {"X-API-Key": API_KEY}
# 1. Check balance
balance = requests.get(f"{BASE_URL}/balance", headers=HEADERS).json()
print(f"Credits available: {balance['data']['total_credits']}")
# 2. Upload a PDF
with open("bank_statement.pdf", "rb") as f:
upload = requests.post(
f"{BASE_URL}/upload",
headers=HEADERS,
files={"file": ("statement.pdf", f, "application/pdf")}
).json()
filename = upload["data"]["filename"]
pages = upload["data"]["pages"]
print(f"Uploaded: {filename} ({pages} pages)")
# 3. Start conversion
convert = requests.post(
f"{BASE_URL}/convert/{filename}",
headers=HEADERS
).json()
print(f"Conversion started, using {convert['data']['credits_used']} credits")
# 4. Poll until complete
while True:
status = requests.get(
f"{BASE_URL}/convert/{filename}/status",
headers=HEADERS
).json()
progress = status["data"]
print(f" Progress: {progress['current_page']}/{progress['total_pages']}")
if progress["conversion_status"] == "completed":
print("Conversion complete!")
break
elif progress["conversion_status"] == "failed":
print(f"Conversion failed: {progress.get('error', 'Unknown error')}")
break
time.sleep(3)
# 5. Download CSV
download = requests.get(
f"{BASE_URL}/download/{filename}?format=csv",
headers=HEADERS
).json()
csv_url = download["data"]["download_url"]
csv_data = requests.get(csv_url)
with open("output.csv", "wb") as f:
f.write(csv_data.content)
print("Saved output.csv")
# 6. Download JSON
download_json = requests.get(
f"{BASE_URL}/download/{filename}?format=json",
headers=HEADERS
).json()
json_url = download_json["data"]["download_url"]
json_data = requests.get(json_url)
with open("output.json", "wb") as f:
f.write(json_data.content)
print("Saved output.json")