Ledgersys API
Reference
Everything you need to integrate Bitcoin financial intelligence into your application or AI agent. Versioned, stable, agent-friendly.
# 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.
https://api.ledgersys.live/v1/
v1 (stable)
JSON (application/json)
X-API-Key header
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
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
https://api.ledgersys.live/v1/
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
/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"
}
/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
/v1/transactions
List all transactions
Query Parameters
pageintegerPage number (default: 1)limitintegerResults per page, max 100 (default: 25)typestringFilter: buy, sell, transferfromdateStart date (YYYY-MM-DD)todateEnd date (YYYY-MM-DD)/v1/transactions
Add a single transaction
Include Idempotency-Key header to prevent duplicate transactions if your request is retried.
Request Body
daterequiredTransaction date (YYYY-MM-DD)typerequiredbuy, sell, or transferamount_btcrequiredAmount in BTCvalue_usdrequiredValue in USD at time of transactionexchangeoptionalExchange nameExample 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}'
/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"
}
/v1/transactions/{id}
Get a single transaction
Tax Calculations
/v1/tax
Get tax calculation
Query Parameters
methodoptionalFIFO (default), LIFO, HIFOjurisdictionoptionalZA, US, UK, AUtax_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"
}
/v1/tax/bulk
Bulk tax calculation (async)
/v1/tax/jobs/{job_id}
Get bulk job status
Reports
/v1/reports/tax
Download tax report (PDF)
tax_yearoptionalTax year (default: current)jurisdictionoptionalZA, US, UK, AUformatoptionalpdf (default), json/v1/reports/bulk
Generate reports for multiple entities
Webhooks
/v1/webhooks
List registered webhooks
/v1/webhooks
Register a new webhook
{
"url": "https://your-agent.com/webhook",
"events": [
"transaction.imported",
"tax.calculated",
"report.generated"
]
}
/v1/webhooks/{id}
Delete a webhook
Sandbox Endpoints
/sandbox/generate_sample_transactions
Generate realistic test data
/sandbox/generate_sample_user
Generate a test user profile
/sandbox/reset
Reset sandbox account to clean state
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.
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}'
Keys expire after 30 days
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 |
|---|---|---|---|
| Free | 1,000 | 5 | 10,000 |
| Pro ($19/month) | 10,000 | 10 | 10,000 |
| Agent Micro ($10/month) | 5,000 | 10 | 10,000 |
| Agent Starter ($50/month) | 20,000 | 20 | 10,000 | Agent Business ($200/month) | 100,000 | 50 | 10,000 |
| Agent Enterprise | Unlimited | Unlimited | 10,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 importedtax.calculatedeventFired when a tax calculation completesreport.generatedeventFired when a report is ready for downloadbulk.completeeventFired when a bulk job finishes processingPayload 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 minuteAttempt 25 minSecond retry after 5 minutesAttempt 330 minFinal retry after 30 minutesSandbox Guide
The sandbox is completely free. No API costs, no rate limit charges, no expiry. Stay as long as you need.
https://sandbox.ledgersys.live/v1/10,000 calls/hourDaily at 00:00 UTC1,000 per accountSandbox Quick Start
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
| Status | Code | Description |
|---|---|---|
| 200 | — | Success |
| 400 | invalid_request | Missing or invalid parameters |
| 401 | unauthorized | Invalid or missing API key |
| 403 | forbidden | Action not permitted on your plan |
| 404 | not_found | Resource not found |
| 429 | rate_limit_exceeded | Too many requests — see rate limit headers |
| 500 | server_error | Internal 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.
- 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(); } }