Ledgerize API Documentation

Integrate Ledgerize into your application to convert bank statement PDFs into structured data (JSON or CSV). The Ledgerize REST API lets you upload bank statement files and retrieve transaction data programmatically. This guide explains each endpoint, provides example requests/responses, and offers tips for seamless integration.

Authentication

All requests require your API key for authorization. Sign in to your Ledgerize account to obtain your API key (it begins with api-). Include it in every request's headers:

Authorization: api-YOUR_API_KEY
  • Base URL: https://ledgerize.ai/api/v1 (use this prefix for all endpoint paths)
  • Authentication: Use the Authorization header with your API key on every request.

Endpoints

Upload a Statement (PDF to Ledgerize)

Upload a bank statement PDF file to Ledgerize for processing. The API will respond with a unique UUID for the statement, which you will use to check its processing status and later retrieve the data.

Request

  • Method: POST
  • URL: https://ledgerize.ai/api/v1/BankStatement
  • Headers:
    • Authorization: api-YOUR_API_KEY
    • (No Content-Type header needed for file uploads; the request will be multipart/form-data.)
  • Body: Multipart form data with a file field:
    • file: The PDF file of the bank statement to upload.
Example Request (cURL)
curl -X POST "https://ledgerize.ai/api/v1/BankStatement" \
  -H "Authorization: api-YOUR_API_KEY" \
  -F "file=@/path/to/statement.pdf"

Response

On success, returns a JSON array (one object per file uploaded) with details for each statement:

[
  {
    "uuid": "bb2f3c62-331e-42ee-a931-d25a5ee0946f",
    "filename": "bankstatement.pdf",
    "pdfType": "TEXT_BASED",
    "state": "READY"
  }
]
  • uuid: The unique identifier for this uploaded statement.
  • pdfType: TEXT_BASED for digital PDFs, IMAGE_BASED for scanned/image PDFs.
  • state: Processing status. Usually READY for text-based PDFs or PROCESSING for scans that require OCR.

Note: If state is PROCESSING, use the Check Status endpoint to poll for completion.

Check Statement Status (Polling for Completion)

Check the processing status of a previously uploaded statement, especially when the initial state was PROCESSING.

Request

  • Method: POST
  • URL: https://ledgerize.ai/api/v1/BankStatement/status
  • Headers:
    • Authorization: api-YOUR_API_KEY
    • Content-Type: application/json
  • Body: JSON array of one or more statement UUID strings.
    Example: ["b0df4b60-1ab7-4edf-bf87-4664f91a67b7"]
Example Request (cURL)
curl -X POST "https://ledgerize.ai/api/v1/BankStatement/status" \
  -H "Authorization: api-YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '["b0df4b60-1ab7-4edf-bf87-4664f91a67b7"]'

Response

Returns a JSON array of status objects corresponding to each requested UUID:

[
  {
    "uuid": "b0df4b60-1ab7-4edf-bf87-4664f91a67b7",
    "filename": "bankstatement.pdf",
    "pdfType": "IMAGE_BASED",
    "state": "READY"
  }
]
  • When state becomes READY, the statement is ready for conversion.
  • If state is still PROCESSING, continue polling.

Polling Tip: Poll every few seconds (e.g., 5s). Avoid polling too frequently to prevent rate limiting (HTTP 429).

Convert a Statement to JSON/CSV (Retrieve Transactions)

Convert a fully processed statement (state: READY) into structured data (JSON or CSV).

Request

  • Method: POST
  • URL: https://ledgerize.ai/api/v1/BankStatement/convert (append ?format=CSV for CSV)
  • Headers:
    • Authorization: api-YOUR_API_KEY
    • Content-Type: application/json
  • Body: JSON array of one or more statement UUIDs to convert.
    Example: ["b0df4b60-1ab7-4edf-bf87-4664f91a67b7"]
Example Request (cURL) - JSON
curl -X POST "https://ledgerize.ai/api/v1/BankStatement/convert" \
  -H "Authorization: api-YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '["b0df4b60-1ab7-4edf-bf87-4664f91a67b7"]'
Example Request (cURL) - CSV
curl -X POST "https://ledgerize.ai/api/v1/BankStatement/convert?format=CSV" \
  -H "Authorization: api-YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '["b0df4b60-1ab7-4edf-bf87-4664f91a67b7"]'

Response

If format=JSON (default): Returns a JSON array of conversion results.

[
  {
    "normalised": [
      {
        "date": "03/08/20",
        "description": "Monthly Service Fee",
        "amount": "-5.00"
      },
      {
        "date": "03/10/20",
        "description": "ATM Withdrawal",
        "amount": "-200.00"
      }
      // ... more transactions ...
    ]
  }
]
  • date: Transaction date.
  • description: Transaction description.
  • amount: Transaction amount (negative for debits).

If format=CSV: Returns CSV text string in the response body (Content-Type: text/csv).

Unlock a Password-Protected PDF (Provide Password)

Provide a password for an uploaded PDF that is password-protected.

Request

  • Method: POST
  • URL: https://ledgerize.ai/api/v1/BankStatement/setPassword
  • Headers:
    • Authorization: api-YOUR_API_KEY
    • Content-Type: application/json
  • Body: JSON object with a passwords field containing an array of { uuid, password } objects.
Example Body
{
  "passwords": [
    {
      "uuid": "b0df4b60-1ab7-4edf-bf87-4664f91a67b7",
      "password": "elephant"
    }
    // ... more {uuid, password} pairs if needed ...
  ]
}
Example Request (cURL)
curl -X POST "https://ledgerize.ai/api/v1/BankStatement/setPassword" \
  -H "Authorization: api-YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"passwords":[{"uuid":"b0df4b60-1ab7-4edf-bf87-4664f91a67b7","password":"elephant"}]}}'

Response

Returns a JSON array of statement objects for which the password was provided. If successful, the state should become READY.

[
  {
    "uuid": "b0df4b60-1ab7-4edf-bf87-4664f91a67b7",
    "filename": "locked.pdf",
    "pdfType": "TEXT_BASED",
    "state": "READY",
    "numberOfPages": 3
  }
]

Note: Handle potential errors if the password is incorrect.

Get Remaining Credits (Account Info)

Retrieve your account information and remaining credit balance.

Request

  • Method: GET
  • URL: https://ledgerize.ai/api/v1/user
  • Headers:
    • Authorization: api-YOUR_API_KEY
  • Body: None required.
Example Request (cURL)
curl -X GET "https://ledgerize.ai/api/v1/user" \
  -H "Authorization: api-YOUR_API_KEY"

Response

Returns a JSON object with user details and credit balances:

{
  "user": {
    "userId": 1,
    "firstName": "John",
    "lastName": "Doe",
    "email": "john@example.com",
    "emailVerified": true,
    "referralCode": "LED0001",
    "apiKey": "api-XXXX********XXXX"
  },
  "credits": {
    "paidCredits": 100,
    "freeCredits": 0
  },
  "unlimitedCredits": false,
  "subscriptionCount": 1
}

Use this data to manage usage and check if the user has credits available.

Code Examples

Below are quick examples in Python and JavaScript demonstrating how to use the Ledgerize API end-to-end.

Python Example

import requests
import time

API_KEY = 'api-YOUR_API_KEY'
BASE_URL = 'https://ledgerize.ai/api/v1'

# 1. Upload a statement PDF
files = {'file': open('bank_statement.pdf', 'rb')}
headers = {'Authorization': API_KEY}
response = requests.post(f'{BASE_URL}/BankStatement', headers=headers, files=files)
statements = response.json()

# Extract the UUID of the uploaded statement
statement_uuid = statements[0]['uuid']
state = statements[0]['state']

# 2. If the statement is processing, poll the status until it's ready
if state == 'PROCESSING':
    status_url = f'{BASE_URL}/BankStatement/status'
    print(f"Statement {statement_uuid} is processing. Polling status...")
    # Keep checking until the state is READY
    while True:
        time.sleep(5)  # wait 5 seconds before the next poll
        status_resp = requests.post(status_url, headers={'Authorization': API_KEY, 'Content-Type': 'application/json'}, json=[statement_uuid])
        if status_resp.status_code == 200:
            status_info = status_resp.json()[0]
            print(f"Current status: {status_info['state']}")
            if status_info['state'] == 'READY':
                print("Statement is READY.")
                break
            elif status_info['state'] == 'ERROR':
                print("Error processing statement.")
                # Handle error appropriately
                exit()
        else:
            print(f"Error checking status: {status_resp.status_code} - {status_resp.text}")
            # Handle error appropriately
            exit()

# (Optional: If the PDF was password-protected, call setPassword here)
# password_payload = {"passwords":[{"uuid": statement_uuid, "password": "YOUR_PASSWORD"}]}
# password_resp = requests.post(f'{BASE_URL}/BankStatement/setPassword', headers={'Authorization': API_KEY, 'Content-Type': 'application/json'}, json=password_payload)
# if password_resp.status_code == 200:
#     print("Password provided successfully.")
# else:
#     print(f"Error setting password: {password_resp.status_code}")
#     # Handle error
#     exit()

# 3. Convert the statement to JSON
print("Converting statement to JSON...")
convert_url = f'{BASE_URL}/BankStatement/convert'
convert_headers = {
    'Authorization': API_KEY,
    'Content-Type': 'application/json'
}
convert_resp = requests.post(convert_url, headers=convert_headers, json=[statement_uuid])

if convert_resp.status_code == 200:
    converted_data = convert_resp.json()
    print("Converted Data:")
    import json
    print(json.dumps(converted_data, indent=2))
else:
    print(f"Error converting statement: {convert_resp.status_code} - {convert_resp.text}")

JavaScript Example (Browser/Node)

const API_KEY = 'api-YOUR_API_KEY';
const BASE_URL = 'https://ledgerize.ai/api/v1';

// Helper function to delay execution
const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));

// 1. Upload a statement
async function uploadStatement(file) {
  const formData = new FormData();
  formData.append('file', file);
  
  try {
    const response = await fetch(`${BASE_URL}/BankStatement`, {
      method: 'POST',
      headers: {
        'Authorization': API_KEY
        // Content-Type is set automatically by browser for FormData
      },
      body: formData
    });
    if (!response.ok) {
        throw new Error(`HTTP error! status: ${response.status} - ${await response.text()}`);
    }
    return await response.json();
  } catch (error) {
    console.error("Error uploading statement:", error);
    throw error;
  }
}

// 2. Check statement status
async function checkStatus(uuid) {
  try {
    const response = await fetch(`${BASE_URL}/BankStatement/status`, {
      method: 'POST',
      headers: {
        'Authorization': API_KEY,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify([uuid])
    });
    if (!response.ok) {
        throw new Error(`HTTP error! status: ${response.status} - ${await response.text()}`);
    }
    return await response.json();
  } catch (error) {
    console.error("Error checking status:", error);
    throw error;
  }
}

// 3. Convert statement to JSON
async function convertStatement(uuid) {
  try {
    const response = await fetch(`${BASE_URL}/BankStatement/convert`, {
      method: 'POST',
      headers: {
        'Authorization': API_KEY,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify([uuid])
    });
     if (!response.ok) {
        throw new Error(`HTTP error! status: ${response.status} - ${await response.text()}`);
    }
    return await response.json();
  } catch (error) {
     console.error("Error converting statement:", error);
     throw error;
  }
}

// --- Example usage for a browser environment ---
document.addEventListener('DOMContentLoaded', () => {
    const uploadForm = document.getElementById('uploadForm'); // Assuming form with id="uploadForm"
    const fileInput = document.getElementById('statementFile'); // Assuming input with id="statementFile"
    const statusDiv = document.getElementById('statusArea'); // Assuming div with id="statusArea"

    if (uploadForm) {
        uploadForm.addEventListener('submit', async (e) => {
            e.preventDefault();
            if (!fileInput.files || fileInput.files.length === 0) {
                statusDiv.textContent = 'Please select a file.';
                return;
            }
            
            statusDiv.textContent = 'Uploading...';
            try {
                // Upload the file and get the UUID
                const statements = await uploadStatement(fileInput.files[0]);
                const uuid = statements[0].uuid;
                let state = statements[0].state;
                statusDiv.textContent = `Upload successful. UUID: ${uuid}, State: ${state}`;

                // If processing is needed, poll until ready
                if (state === 'PROCESSING') {
                    statusDiv.textContent += '. Polling status...';
                    let statusInfo;
                    do {
                        await sleep(5000); // wait 5 seconds
                        const statusResult = await checkStatus(uuid);
                        statusInfo = statusResult[0];
                        state = statusInfo.state;
                        statusDiv.textContent = `Polling... Current state: ${state}`;
                        if (state === 'ERROR') {
                            statusDiv.textContent = 'Error during processing.';
                            return; // Stop processing
                        }
                    } while (state === 'PROCESSING');
                    statusDiv.textContent = `Processing complete. State: ${state}`;
                }
                
                // Convert the statement to JSON data
                if (state === 'READY') {
                    statusDiv.textContent += '. Converting...';
                    const data = await convertStatement(uuid);
                    statusDiv.textContent = 'Conversion Complete!';
                    console.log('Converted Data:', data);
                    // Display data or handle it as needed
                    const outputArea = document.getElementById('outputData'); // Assuming pre/div with id="outputData"
                    if(outputArea) outputArea.textContent = JSON.stringify(data, null, 2);
                } else {
                    statusDiv.textContent = `Cannot convert. Final state: ${state}`;
                }

            } catch (error) {
                statusDiv.textContent = `An error occurred: ${error.message}`;
                console.error("API Interaction Error:", error);
            }
        });
    }
});

Note: In a Node.js environment, use a library like node-fetch or axios and handle file uploads using appropriate modules like form-data.

Best Practices & Tips

  • Use the correct data formats: Always include Content-Type: application/json when sending JSON. Send UUIDs in a JSON array [...] as required.
  • Handling errors: Check HTTP status codes (400, 401, 403, 429, 500). Handle errors gracefully (e.g., back off on 429, check API key on 401/403, ensure sufficient credits).
  • Optimizing polling: Use a reasonable interval (e.g., 5 seconds). Poll multiple UUIDs in a single /status request if needed.
  • Securing API keys: Never expose API keys in client-side code. Route requests through your server or use secure storage methods.
  • Testing: Use cURL or Postman during development to verify requests and responses.

By following this documentation, first-time developers can quickly get up to speed with Ledgerize's API. Happy coding!