Home Pricing How It Works About Blog API Docs Login Get Started Free

Ledgersys API
Reference

Everything you need to integrate Bitcoin financial intelligence into your application or AI agent. Versioned, stable, agent-friendly.

Version: v1 (stable)
API Operational
Sandbox: Free
curl
# Get your tax position
curl -X GET \
  https://api.ledgersys.live/v1/tax \
  -H "X-API-Key: your_api_key"

# Response
{
  "status": "ok",
  "tax_obligation": 6820.00,
  "realized_gains": 24381.00,
  "currency": "USD",
  "jurisdiction": "ZA"
}

Overview

The Ledgersys API provides programmatic access to Bitcoin financial intelligence. It is designed for developers, AI agents, and automated systems that need to track Bitcoin transactions, calculate tax obligations, and generate financial reports.

Base URL https://api.ledgersys.live/v1/
API Version v1 (stable)
Format JSON (application/json)
Auth X-API-Key header
Sandbox Available

Test your integration for free using our sandbox environment. Same endpoints, fake data, no API costs. Use sandbox_ prefixed keys or set X-Environment: sandbox header.

Authentication

All API requests require authentication using an API key passed in the X-API-Key request header.

Generating an API Key

1 Log into your Ledgersys account at ledgersys.live/login
2 Navigate to your Agent Dashboard → API Keys
3 Click Generate New API Key and name your key
4 Copy and store the key securely — it will only be shown once

Using Your API Key

curl -X GET \
  https://api.ledgersys.live/v1/health \
  -H "X-API-Key: lgs_live_your_api_key_here"
import requests

headers = {
    "X-API-Key": "lgs_live_your_api_key_here",
    "Content-Type": "application/json"
}

response = requests.get(
    "https://api.ledgersys.live/v1/health",
    headers=headers
)
print(response.json())
const response = await fetch(
    "https://api.ledgersys.live/v1/health",
    {
        headers: {
            "X-API-Key": "lgs_live_your_api_key_here",
            "Content-Type": "application/json"
        }
    }
);
const data = await response.json();
console.log(data);

Sandbox Keys

# Production key
X-API-Key: lgs_live_sk_xxxxxxxxxxxxxxxxxxxxxxxx

# Sandbox key (free, no charges)
X-API-Key: sandbox_sk_xxxxxxxxxxxxxxxxxxxxxxxx

# Alternatively, force sandbox via header
X-Environment: sandbox

Base URL

Production https://api.ledgersys.live/v1/
Sandbox https://sandbox.ledgersys.live/v1/

Quick Start

Get from zero to a tax calculation in under 5 minutes.

import requests

API_KEY = "sandbox_sk_your_key_here"
BASE_URL = "https://api.ledgersys.live/v1"
headers = {"X-API-Key": API_KEY, "Content-Type": "application/json"}

# Step 1: Check API health
health = requests.get(f"{BASE_URL}/health", headers=headers).json()
print(f"API Status: {health['status']}")

# Step 2: Add a transaction
tx = requests.post(
    f"{BASE_URL}/transactions",
    json={
        "date": "2026-04-25",
        "type": "buy",
        "amount_btc": 0.5,
        "value_usd": 42500.00,
        "exchange": "coinbase"
    },
    headers=headers
).json()

# Step 3: Get tax calculation
tax = requests.get(f"{BASE_URL}/tax", headers=headers).json()
print(f"Tax Obligation: ${tax['tax_obligation']}")
# Step 1: Health check
curl https://api.ledgersys.live/v1/health \
  -H "X-API-Key: sandbox_sk_your_key"

# Step 2: Add a transaction
curl -X POST https://api.ledgersys.live/v1/transactions \
  -H "X-API-Key: sandbox_sk_your_key" \
  -H "Content-Type: application/json" \
  -d '{"date":"2026-04-25","type":"buy","amount_btc":0.5,"value_usd":42500}'

# Step 3: Get tax calculation
curl https://api.ledgersys.live/v1/tax \
  -H "X-API-Key: sandbox_sk_your_key"
const API_KEY = "sandbox_sk_your_key_here";
const BASE = "https://api.ledgersys.live/v1";
const h = {
    "X-API-Key": API_KEY,
    "Content-Type": "application/json"
};

const tx = await fetch(`${BASE}/transactions`, {
    method: "POST", headers: h,
    body: JSON.stringify({ date: "2026-04-25", type: "buy", amount_btc: 0.5, value_usd: 42500 })
}).then(r => r.json());

const tax = await fetch(`${BASE}/tax`, { headers: h }).then(r => r.json());
console.log(`Tax owed: $${tax.tax_obligation}`);
interface TaxResult {
    tax_obligation: number;
    realized_gains: number;
    currency: string;
    jurisdiction: string;
}

const API_KEY = "sandbox_sk_your_key";
const BASE = "https://api.ledgersys.live/v1";
const headers = { "X-API-Key": API_KEY, "Content-Type": "application/json" };

const tax: TaxResult = await fetch(`${BASE}/tax`, { headers }).then(r => r.json());
console.log(`Tax owed: $${tax.tax_obligation}`);

Health & Status

GET /v1/health Check API health

Returns the current health status of the Ledgersys API. Use this endpoint to verify connectivity before sending requests.

{
  "status": "ok",
  "version": "v1",
  "database": "connected",
  "timestamp": "2026-04-25T10:00:00Z"
}
GET /api/status Detailed API status with rate limits

Returns detailed status including rate limit information and any active incidents. Ideal for agent health checks before high-volume operations.

{
  "status": "operational",
  "version": "v1",
  "sandbox": "operational",
  "latency_ms": 45,
  "rate_limit_remaining": 9876,
  "rate_limit_reset": "2026-04-25T11:00:00Z",
  "incidents": []
}

Transactions

GET /v1/transactions List all transactions

Query Parameters

pageintegerPage number (default: 1)
limitintegerResults per page, max 100 (default: 25)
typestringFilter: buy, sell, transfer
fromdateStart date (YYYY-MM-DD)
todateEnd date (YYYY-MM-DD)
POST /v1/transactions Add a single transaction
Idempotency Recommended

Include Idempotency-Key header to prevent duplicate transactions if your request is retried.

Request Body

daterequiredTransaction date (YYYY-MM-DD)
typerequiredbuy, sell, or transfer
amount_btcrequiredAmount in BTC
value_usdrequiredValue in USD at time of transaction
exchangeoptionalExchange name

Example with Idempotency Key

curl -X POST https://api.ledgersys.live/v1/transactions \
  -H "X-API-Key: your_key" \
  -H "Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000" \
  -H "Content-Type: application/json" \
  -d '{"date":"2026-04-25","type":"buy","amount_btc":0.1,"value_usd":8500}'
POST /v1/transactions/bulk Bulk upload up to 10,000 transactions

Upload thousands of transactions in a single request. Processing is asynchronous — you receive a job ID immediately and a webhook when complete.

{
  "job_id": "job_xyz789",
  "status": "pending",
  "transaction_count": 2,
  "estimated_completion": "2026-04-25T10:05:00Z"
}
GET /v1/transactions/{id} Get a single transaction

Tax Calculations

GET /v1/tax Get tax calculation

Query Parameters

methodoptionalFIFO (default), LIFO, HIFO
jurisdictionoptionalZA, US, UK, AU
tax_yearoptionalTax year (e.g. 2026)
currencyoptionalUSD, ZAR, GBP, EUR
{
  "tax_obligation": 6820.00,
  "realized_gains": 24381.00,
  "cost_basis_method": "FIFO",
  "jurisdiction": "ZA",
  "currency": "USD",
  "calculated_at": "2026-04-25T10:00:00Z"
}
POST /v1/tax/bulk Bulk tax calculation (async)
GET /v1/tax/jobs/{job_id} Get bulk job status

Reports

GET /v1/reports/tax Download tax report (PDF)
tax_yearoptionalTax year (default: current)
jurisdictionoptionalZA, US, UK, AU
formatoptionalpdf (default), json
POST /v1/reports/bulk Generate reports for multiple entities

Webhooks

GET /v1/webhooks List registered webhooks
POST /v1/webhooks Register a new webhook
{
  "url": "https://your-agent.com/webhook",
  "events": [
    "transaction.imported",
    "tax.calculated",
    "report.generated"
  ]
}
DELETE /v1/webhooks/{id} Delete a webhook

Sandbox Endpoints

GET /sandbox/generate_sample_transactions Generate realistic test data
GET /sandbox/generate_sample_user Generate a test user profile
POST /sandbox/reset Reset sandbox account to clean state
Destructive Action

Resets all sandbox data. Production data is never affected.

Idempotency Keys

Idempotency keys prevent duplicate processing when requests are retried — critical for AI agents that may retry failed requests automatically.

Why Agents Need This

If your agent sends a transaction and the network drops before receiving a response, retrying without an idempotency key creates a duplicate transaction. With an idempotency key, the second request returns the original response — no duplicate.

# Generate a unique key per request (UUID recommended)
curl -X POST https://api.ledgersys.live/v1/transactions \
  -H "X-API-Key: your_key" \
  -H "Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000" \
  -H "Content-Type: application/json" \
  -d '{"date":"2026-04-25","type":"buy","amount_btc":0.1,"value_usd":8500}'
Key Expiry

Keys expire after 30 days

Uniqueness

Use UUID v4 or similar per-request unique string

Rate Limits

Rate limits protect the API from abuse and ensure fair access. Limits are applied per API key.

Plan Calls / Hour Bulk Requests / Hour Sandbox Calls / Hour
Free1,000510,000
Pro ($19/month)10,0001010,000
Agent Micro ($10/month)5,0001010,000
Agent Starter ($50/month)20,0002010,000
Agent Business ($200/month)100,0005010,000
Agent EnterpriseUnlimitedUnlimited10,000

Rate Limit Headers

X-RateLimit-Limit: 10000
X-RateLimit-Remaining: 9876
X-RateLimit-Reset: 1745578800

When you exceed the rate limit, you receive a 429 Too Many Requests response. Wait until X-RateLimit-Reset before retrying.

Webhook Guide

Event Types

transaction.importedeventFired when a transaction is successfully imported
tax.calculatedeventFired when a tax calculation completes
report.generatedeventFired when a report is ready for download
bulk.completeeventFired when a bulk job finishes processing

Payload Format

{
  "event": "tax.calculated",
  "timestamp": "2026-04-25T10:00:00Z",
  "data": {
    "tax_obligation": 6820.00,
    "realized_gains": 24381.00,
    "jurisdiction": "ZA"
  }
}

Verifying Signatures

import hmac, hashlib

def verify_webhook(payload: bytes, signature: str, secret: str) -> bool:
    expected = hmac.new(
        secret.encode(), payload, hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(expected, signature)

# In your webhook handler:
if not verify_webhook(request.body, request.headers["X-Webhook-Signature"], YOUR_SECRET):
    return 401

Retry Policy

Attempt 11 minFirst retry after 1 minute
Attempt 25 minSecond retry after 5 minutes
Attempt 330 minFinal retry after 30 minutes

Sandbox Guide

Free. No time limit.

The sandbox is completely free. No API costs, no rate limit charges, no expiry. Stay as long as you need.

Sandbox URLhttps://sandbox.ledgersys.live/v1/
Rate Limit10,000 calls/hour
Data ResetDaily at 00:00 UTC
Max Transactions1,000 per account

Sandbox Quick Start

Sandbox Quick Start Python
import requests

# Use your sandbox key — free, no charges
API_KEY = "sandbox_sk_your_key_here"
BASE = "https://api.ledgersys.live/v1"
headers = {"X-API-Key": API_KEY}

# Generate sample transaction data
requests.get(
    "https://api.ledgersys.live/sandbox/generate_sample_transactions",
    headers=headers
)

# Get tax calculation on fake data
tax = requests.get(f"{BASE}/tax", headers=headers).json()
print(f"Test tax obligation: ${tax['tax_obligation']}")

# Reset when done testing
requests.post(
    "https://api.ledgersys.live/sandbox/reset",
    headers=headers
)

Error Handling

StatusCodeDescription
200Success
400invalid_requestMissing or invalid parameters
401unauthorizedInvalid or missing API key
403forbiddenAction not permitted on your plan
404not_foundResource not found
429rate_limit_exceededToo many requests — see rate limit headers
500server_errorInternal server error — contact support

Error Response Format

{
  "error": {
    "code": "rate_limit_exceeded",
    "message": "You have exceeded your rate limit. Try again after 2026-04-25T11:00:00Z.",
    "docs": "https://ledgersys.live/docs#rate-limits"
  }
}

API Changelog

Use GET /api/changelog for the machine-readable version. Human-readable history below.

v1.0.0 April 2026 Stable
  • Initial stable release of Ledgersys API
  • Transaction management endpoints (GET, POST, bulk)
  • Tax calculation endpoints (single and bulk)
  • Report generation (PDF, JSON)
  • Webhook registration and delivery
  • Idempotency key support
  • Sandbox environment

Code Examples

import requests
import uuid

API_KEY = "sandbox_sk_your_key_here"
BASE_URL = "https://api.ledgersys.live/v1"
headers = {"X-API-Key": API_KEY, "Content-Type": "application/json"}

# Record a trade with idempotency
response = requests.post(
    f"{BASE_URL}/transactions",
    json={
        "date": "2026-04-25",
        "type": "buy",
        "amount_btc": 0.5,
        "value_usd": 42500
    },
    headers={**headers, "Idempotency-Key": str(uuid.uuid4())}
)
print(response.json())

# Get tax calculation
tax = requests.get(f"{BASE_URL}/tax", headers=headers).json()
print(f"Tax owed: ${tax['tax_obligation']}")
# Generate a unique idempotency key (UUID)
IDEM_KEY=$(uuidgen)

curl -X POST https://api.ledgersys.live/v1/transactions \
  -H "X-API-Key: your_key" \
  -H "Idempotency-Key: $IDEM_KEY" \
  -H "Content-Type: application/json" \
  -d '{"date":"2026-04-25","type":"buy","amount_btc":0.1,"value_usd":8500}'

curl https://api.ledgersys.live/v1/tax \
  -H "X-API-Key: your_key"
const { v4: uuidv4 } = require('uuid');
const API_KEY = "sandbox_sk_your_key";
const BASE = "https://api.ledgersys.live/v1";
const headers = { "X-API-Key": API_KEY, "Content-Type": "application/json" };

const tx = await fetch(`${BASE}/transactions`, {
    method: "POST",
    headers: { ...headers, "Idempotency-Key": uuidv4() },
    body: JSON.stringify({ date: "2026-04-25", type: "buy", amount_btc: 0.5, value_usd: 42500 })
}).then(r => r.json());
console.log(tx);

const tax = await fetch(`${BASE}/tax`, { headers }).then(r => r.json());
console.log(`Tax owed: $${tax.tax_obligation}`);
interface Transaction {
    date: string;
    type: 'buy' | 'sell' | 'transfer';
    amount_btc: number;
    value_usd: number;
    exchange?: string;
}

interface TaxResult {
    tax_obligation: number;
    realized_gains: number;
    cost_basis_method: string;
}

class LedgersysClient {
    private apiKey: string;
    private baseUrl: string;

    constructor(apiKey: string) {
        this.apiKey = apiKey;
        this.baseUrl = "https://api.ledgersys.live/v1";
    }

    async recordTransaction(tx: Transaction, idempotencyKey: string) {
        const response = await fetch(`${this.baseUrl}/transactions`, {
            method: "POST",
            headers: {
                "X-API-Key": this.apiKey,
                "Idempotency-Key": idempotencyKey,
                "Content-Type": "application/json"
            },
            body: JSON.stringify(tx)
        });
        return response.json();
    }

    async getTax(): Promise {
        const response = await fetch(`${this.baseUrl}/tax`, {
            headers: { "X-API-Key": this.apiKey }
        });
        return response.json();
    }
}