REST API

Solid Accounting can run a Local REST API Server on your computer. A script or tool on the same computer can use it to read your books and to create contacts, invoices and journal entries. Every write goes through the same checks as the app: entries must balance, closed periods stay closed, and every change is recorded in the Audit Trail.

The API is included with Professional and Accountant. It is not available on Standard.

Turn it on and get a key

  1. Open Settings → AI & Integrations. Under Apps & API, click Create a key….
  2. Type a name for the key (for example "Excel add-in"), choose what it can do, and click Create key. If the API server isn't running, creating a key starts it.
  3. Copy the key when it appears. It is shown once. Keys start with tlk_, and only a hash of the key is stored, so a lost key can't be shown again; revoke it and create a new one.

You can also create a key with permissions and a rate limit of your choosing from Apps → Add app → Inbound API Key:

  • Read — query accounts, contacts, balances, journal entries, items
  • Write — create and update contacts, journal entries, invoices
  • Import — batch import transactions through /import
  • Rate limit — 1 to 600 requests per minute (default 60)

To start, stop or change the port, open Advanced under Apps & API in Settings → AI & Integrations (shown in Expert Mode). Revoke a key from the API keys list on the same card at any time. API docs there opens the explorer described below.

The server runs only while Solid Accounting is open with a company file. It listens on 127.0.0.1 (this computer only) on port 21384 unless you change it. In this release it can't be exposed to your network.

Make a request

All endpoints are under:

http://127.0.0.1:21384/api/v1

Send your key in the X-API-Key header. Authorization: Bearer tlk_… also works.

curl -H "X-API-Key: $SA_API_KEY" \
  http://127.0.0.1:21384/api/v1/accounts

A successful response wraps the result in data. List endpoints add meta with total, limit and offset:

{
  "data": [ ... ],
  "meta": { "total": 212, "limit": 50, "offset": 0 }
}

An error returns an error object with a stable code you can switch on, a human-readable message, and sometimes details:

{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Entry does not balance: debits=10000 credits=9900",
    "details": { "reason": "unbalanced", "debit_cents": 10000, "credit_cents": 9900, "difference_cents": 100 }
  }
}

Codes include UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_ERROR, RATE_LIMITED, NO_FILE_OPEN and INTERNAL_ERROR. A write into a closed period returns VALIDATION_ERROR with details.reason = "closed_period". When you hit the rate limit you get HTTP 429 with a Retry-After header.

Conventions:

  • IDs are UUID strings. Invoices refer to customers by customer_id, not by name.
  • Money is whole cents as an integer: 12345 means $123.45.
  • Dates are YYYY-MM-DD.
  • Paging uses ?limit= and ?offset=.
  • No duplicates on re-runs. Send external_id and source_id on an import or journal entry and a repeat of the same pair is skipped.

Endpoints

The full, current list with request and response shapes is served by your own copy of the app:

  • GET /api/v1/openapi.json — the OpenAPI spec
  • GET /api/v1/docs — an interactive explorer
  • GET /api/v1/health — a liveness check

These three need no key. The main groups:

AreaEndpoints
Company & accountsGET /company, GET/POST /accounts, GET/PUT /accounts/{id}, GET /accounts/by-number/{num}, POST /opening-balances
Contacts & itemsGET/POST /contacts, GET/PUT /contacts/{id}, GET /items, GET/POST /tax-codes, GET/POST /payment-terms
Journal entriesGET/POST /journal-entries, GET /journal-entries/{id}, POST /journal-entries/{id}/void, POST /import
SalesGET/POST /invoices, GET /invoices/open, GET/POST /estimates, POST /estimates/{id}/convert, GET /credit-memos, POST /payments/apply
PurchasesGET /bills, GET /bills/open, GET/POST /purchase-orders, POST /purchase-orders/{id}/receive
Balances & reportsGET /balances, GET /trial-balance, GET /reports/income-statement, /reports/balance-sheet, /reports/cash-flow, /reports/ar-aging, /reports/ap-aging, /reports/1099
BankingGET/POST /reconciliations, GET /reconciliations/history, GET /bank-feeds/transactions, POST /import/csv-upload, POST /import/ofx-upload
Other listsclasses, locations, projects, budgets, fixed assets (/assets), loans, recurring transactions, drafts
Fiscal yearsGET/POST /fiscal-years, GET /fiscal-years/{id}/periods, POST /fiscal-years/{id}/close
Review & auditGET /review-queue, POST /review-queue/{id}/approve, GET /audit-log

Bank feed endpoints also need active Updates & Cloud Services.

What the API checks for you

  • Balance. An entry whose debits and credits differ is refused.
  • Closed periods. Nothing posts into a closed period.
  • Permissions. A key can only do what its Read / Write / Import permissions allow.
  • Review first. A key set to Review first parks its writes in the Review Queue until someone approves them.
  • Audit. Every write is recorded in the Audit Trail with the key's name.

A script can fail, but it can't unbalance your books.

Limits in this release

  • This computer only. The API listens on 127.0.0.1. A cloud service such as Zapier or a webhook from Stripe can't reach it. Run your script on the computer where Solid Accounting is open.
  • No outbound webhooks. The app doesn't call your URL when something changes. A script on the same computer can poll, or listen to GET /events (server-sent events).
  • Browser pages are blocked. The API sends no CORS headers, so a web page in your browser can't call it. Use a script or a native program.
Updated October 31, 2026
Report a problem with this page
Was this helpful?

We use this to prioritize which docs to improve. No tracking, no email follow-up.