Skip to main content

API Reference

The Statement Pro API lets you convert PDF bank statements to structured data programmatically. Upload a PDF, poll for results, and download transactions in your preferred format.

Base URL

https://www.statementpro.app/api/v1/

All responses are JSON. For interactive testing, try the Swagger UI.

Authentication

Authenticate every request with a Bearer token in the Authorization header.

Header
Authorization: Bearer sp_live_your_key_here

Managing API Keys

  1. Go to Account → API Keys
  2. Click Generate New Key
  3. Copy the key immediately — it won’t be shown again

Keep your key secret. Don’t commit it to version control or expose it in client-side code. Use environment variables instead.

Quick Start

Three steps: upload a PDF, poll for completion, download the results.

bash
# 1. Upload a PDF
curl -X POST https://www.statementpro.app/api/v1/convert/ \
  -H "Authorization: Bearer sp_live_your_key_here" \
  -F "[email protected]"

# 2. Check status (replace {job_id} with the id from step 1)
curl https://www.statementpro.app/api/v1/jobs/{job_id}/ \
  -H "Authorization: Bearer sp_live_your_key_here"

# 3. Download CSV
curl -O https://www.statementpro.app/api/v1/jobs/{job_id}/download/csv/ \
  -H "Authorization: Bearer sp_live_your_key_here"
python
import time
import requests

API_KEY = "sp_live_your_key_here"
headers = {"Authorization": f"Bearer {API_KEY}"}

# 1. Upload a PDF
with open("statement.pdf", "rb") as f:
    resp = requests.post(
        "https://www.statementpro.app/api/v1/convert/",
        headers=headers,
        files={"file": f},
    )
job = resp.json()
print(f"Job created: {job['id']}")

# 2. Poll for completion
while job["status"] in ("pending", "processing"):
    time.sleep(2)
    job = requests.get(
        f"https://www.statementpro.app/api/v1/jobs/{job['id']}/",
        headers=headers,
    ).json()

# 3. Get transactions
transactions = requests.get(
    f"https://www.statementpro.app/api/v1/jobs/{job['id']}/transactions/",
    headers=headers,
).json()
print(f"Found {len(transactions)} transactions")
javascript
const API_KEY = "sp_live_your_key_here";
const headers = { Authorization: `Bearer ${API_KEY}` };

// 1. Upload a PDF
const form = new FormData();
form.append("file", fileInput.files[0]);

let job = await fetch("https://www.statementpro.app/api/v1/convert/", {
  method: "POST",
  headers,
  body: form,
}).then((r) => r.json());

// 2. Poll for completion
while (["pending", "processing"].includes(job.status)) {
  await new Promise((r) => setTimeout(r, 2000));
  job = await fetch(
    `https://www.statementpro.app/api/v1/jobs/${job.id}/`,
    { headers }
  ).then((r) => r.json());
}

// 3. Get transactions
const transactions = await fetch(
  `https://www.statementpro.app/api/v1/jobs/${job.id}/transactions/`,
  { headers }
).then((r) => r.json());

Endpoints

POST /api/v1/convert/

Upload a PDF bank statement for conversion. Returns a job object with status pending.

Request Body (multipart/form-data)

Field Type Description
file file required The PDF bank statement to convert
webhook_url string URL to receive a POST when the job completes or fails

Response 202 Accepted

json
{
  "id": "abc123",
  "status": "pending",
  "created_at": "2026-02-19T10:30:00Z",
  "file_name": "statement.pdf",
  "pages": 3
}
GET /api/v1/jobs/{id}/

Retrieve the current status of a conversion job.

Response Fields

Field Type Description
id string Unique job identifier
status string pending | processing | completed | failed
file_name string Original uploaded file name
pages integer Number of pages in the PDF
bank_name string | null Detected bank name, or null if AI-parsed
transaction_count integer Number of extracted transactions (0 while processing)
created_at datetime ISO 8601 timestamp
error_message string | null Error details if status is failed

Response 200 OK

json
{
  "id": "abc123",
  "status": "completed",
  "file_name": "statement.pdf",
  "pages": 3,
  "bank_name": "Chase",
  "transaction_count": 47,
  "created_at": "2026-02-19T10:30:00Z",
  "error_message": null
}
GET /api/v1/jobs/{id}/transactions/

Retrieve extracted transactions for a completed job.

Response 200 OK

json
[
  {
    "date": "2026-01-15",
    "description": "DIRECT DEPOSIT - ACME CORP",
    "amount": 3250.00,
    "type": "credit",
    "balance": 5432.10
  },
  {
    "date": "2026-01-16",
    "description": "WHOLEFDS MKT #10245",
    "amount": -87.32,
    "type": "debit",
    "balance": 5344.78
  }
]
GET /api/v1/jobs/{id}/download/{format}/

Download the converted statement in the specified format. Returns the file as a binary download.

Path Parameters

Parameter Options Description
format csv xlsx json ofx qif Export format for the download
GET /api/v1/usage/

Check your current billing period usage and plan limits.

Response 200 OK

json
{
  "tier": "professional",
  "pages_used": 42,
  "pages_limit": 200,
  "pages_remaining": 158,
  "credit_pages": 10,
  "billing_period_end": "2026-03-01T00:00:00Z"
}

Webhooks

Instead of polling, pass a webhook_url when creating a conversion. We’ll send a POST request to that URL when the job completes or fails.

Setup

bash
curl -X POST https://www.statementpro.app/api/v1/convert/ \
  -H "Authorization: Bearer sp_live_your_key_here" \
  -F "[email protected]" \
  -F "webhook_url=https://your-app.com/webhooks/statement-pro"

Payload

The webhook POST body contains the event type and the full job object.

json
{
  "event": "job.completed",
  "job": {
    "id": "abc123",
    "status": "completed",
    "file_name": "statement.pdf",
    "pages": 3,
    "bank_name": "Chase",
    "transaction_count": 47,
    "created_at": "2026-02-19T10:30:00Z"
  }
}
json
{
  "event": "job.failed",
  "job": {
    "id": "abc123",
    "status": "failed",
    "file_name": "statement.pdf",
    "pages": 3,
    "error_message": "No transactions found in the document",
    "created_at": "2026-02-19T10:30:00Z"
  }
}

Retry Policy

If your endpoint returns a non-2xx status, we retry up to 5 times with exponential backoff (1 min, 5 min, 30 min, 2 hr, 12 hr).

Security tip: Verify webhook authenticity by checking that the job ID matches one you created. You can also restrict incoming IPs or use a secret path segment in your webhook URL.

Rate Limits

API requests are rate-limited per hour based on your subscription tier.

Tier Requests / Hour
Free 10
Starter 30
Professional 60
Business 200

When you exceed the limit, the API returns 429 Too Many Requests with a Retry-After header (seconds until the window resets).

json
{
  "error": {
    "code": "rate_limit_exceeded",
    "message": "Rate limit exceeded. Retry after 42 seconds."
  }
}

Error Handling

Errors return a consistent JSON envelope with an error object containing a machine-readable code and a human-readable message.

json
{
  "error": {
    "code": "invalid_file",
    "message": "The uploaded file is not a valid PDF."
  }
}

Error Codes

HTTP Status Code Description
400 invalid_file File is not a valid PDF or is corrupted
400 missing_file No file was included in the request
401 unauthorized Missing or invalid API key
403 usage_exceeded Monthly page limit reached — upgrade or buy credits
404 not_found Job ID does not exist or does not belong to you
409 job_not_ready Tried to download results for a job still processing
429 rate_limit_exceeded Too many requests — check the Retry-After header

Export Formats

Pass the format slug in the download endpoint path. All formats include the same transaction data.

Format Slug Description
CSV csv Comma-separated values — works with Excel, Google Sheets, and accounting apps
Excel xlsx Native Excel workbook with formatted columns
JSON json Structured JSON array of transaction objects
OFX ofx Open Financial Exchange — import into Quicken, GnuCash, and other financial software
QIF qif Quicken Interchange Format — legacy financial software support