Skip to content

Journals and the general ledger

Can you get the actual general ledger out? Xero yes, through a read-only append-only feed that is the best endpoint in any of these APIs. NetSuite yes, through SQL. QuickBooks no — there is no GL endpoint, and that absence shapes every statement you build on it.

Concepts
9
Divergences
4
Structural
3

Endpoints

Xero

GET /Journals?offset={JournalNumber}

Read-only, gapless, every posting. Advanced partner tier only since March 2026

QBO

None

Reports API, or reconstruct from entities

NetSuite

SuiteQL over transactionaccountingline

The real GL, joinable

The same concept in three vocabularies

Read across a row. Where a cell is absent, the concept has nowhere to live in that system — which is a design constraint, not a gap to paper over.

NativeNative
— A field or object exists for it
PartialPartial
— Exists with a caveat that changes what you can build
DerivedDerived
— Not stored — you compute it
AbsentAbsent
— Nothing in the API
Journals and the general ledger fields compared across Xero, QuickBooks Online and NetSuite
ConceptXeroQBONetSuite
User-entered journal entry
Native

ManualJournals

Native

JournalEntry

Native

journalentry

Full GL feedEvery posting, including those generated by invoices and payments.
Native

Journals

Immutable, sequentially numbered, replayable

Absent

None

The largest structural gap in these three APIs

Native

transaction + transactionaccountingline

Filter posting = 'T'

Debit / credit shape
Partial

Signed NetAmount

Positive is a debit. ManualJournals use signed LineAmount

Native

PostingType + positive Amount

"Debit" | "Credit"

Native

debit and credit columns

Pagination
Native

offset by JournalNumber

100 per call, strictly sequential

Absent

—

Native

limit / offset on SuiteQL

Party on a GL line
Absent

—

Journals lines carry no contact

Native

Entity per line

Customer, vendor or employee

Native

entity per line

Dimensions on a GL line
Partial

TrackingCategories[]

Partial

ClassRef, DepartmentRef

Native

Full segment set

Multi-book
Absent

—

Absent

—

Native

accountingbook

The same transaction posts differently per book

Auto-reversal
Absent

Post the opposite journal yourself

The API has no reversal field

Absent

Post the reversal yourself

The UI's Reverse creates a second, dated entry; the API has no reversal field (Adjustment only marks an adjusting entry)

Native

reversaldate

Native, scheduled

Source of a postingWhether you can tell an invoice posting from a manual one.
Native

SourceType + SourceID

ACCREC, ACCPAY, CASHREC, MANJOURNAL…

Derived

—

Only via the Reports API's transaction type column

Native

transaction.type

What actually differs, and what you do about it

A difference stated without a response is trivia. Each of these carries the handling.

Structural

Xero's Journals endpoint is the crown jewel and is easy to miss

Xero

It emits the actual general ledger — every posting from invoices, payments, bills, bank transactions and manual journals — as immutable, sequentially numbered records. Paginate by offset on JournalNumber and you have an append-only, gapless, perfectly replayable ledger feed.

Do — Anything that reconciles or re-derives statements builds on /Journals, not on /Invoices. Store the highest JournalNumber seen as the sync cursor; it never goes backwards. Since 2 March 2026 Xero reserves the endpoint for apps on its Advanced partner tier that have passed a security assessment and had the use case approved — so make hasGlFeed a capability you discover, and keep a reconstruct-from-documents fallback for apps without it.

Structural

The Xero GL feed now sits behind a partner tier

Xero

From 2 March 2026 the Journals endpoint is available only from the Advanced tier of Xero's developer pricing, and only after an initial and annual security assessment and use-case approval. ManualJournals stays open on every tier, but it holds only the journals people typed — not the postings that invoices, payments and bank transactions generate.

Do — Treat Journals access as a per-app capability, not a property of Xero. Without it, statements are reconstructed from documents and tied to the Reports endpoints, exactly as on QBO, and the provenance label says so.

Structural

QuickBooks has no general ledger endpoint

QBO

There is no way to ask QBO for postings. Your three options are the Reports API (correct totals, presentation-shaped rows, no stable ids), reconstructing postings from every transaction entity (you then own the correctness of Intuit's tax and FX posting rules, forever), or mirroring entities via change data capture and deriving statements from the mirror.

Do — Reports API for tie-out, an entity mirror for detail. Display which one produced the number; the two will occasionally disagree.

Sharp edge

NetSuite requires you to pick the right line table and book

NetSuite

transactionline is the transaction's lines; transactionaccountingline carries the debit and credit amounts, per accounting book. Query the wrong one and you get quantities without postings, or every book summed together.

Do — Always filter posting = 'T' and accountingbook explicitly. Never rely on the default book being 1 without checking the account's configuration.

The same fact, different payloads

Stacked rather than tabbed — a reader comparing two shapes should not have to hold one of them in memory.

Xero — a journal from the read-only GL feed

Xero
{
  "JournalID": "6a1b…",
  "JournalNumber": 4821,
  "JournalDate": "2026-09-07",
  "SourceType": "ACCREC",
  "SourceID": "3f2a…",
  "JournalLines": [
    { "AccountCode": "610", "AccountType": "CURRENT",
      "NetAmount": 1200.00, "Description": "Accounts receivable" },
    { "AccountCode": "200", "AccountType": "REVENUE",
      "NetAmount": -1000.00, "TrackingCategories": [
        { "Name": "Department", "Option": "R&D" }] },
    { "AccountCode": "820", "AccountType": "CURRLIAB",
      "NetAmount": -200.00, "Description": "VAT" }
  ]
}

NetSuite — the GL for a period, primary book

NetSuite
SELECT t.trandate, t.tranid, t.type,
       a.acctnumber, a.acctname,
       tal.debit, tal.credit,
       tl.department, tl.class, tl.location, tl.memo
FROM   transaction t
JOIN   transactionline tl            ON tl.transaction = t.id
JOIN   transactionaccountingline tal ON tal.transaction = t.id
                                    AND tal.transactionline = tl.id
JOIN   account a                     ON a.id = tal.account
WHERE  tal.posting = 'T'
  AND  tal.accountingbook = 1
  AND  t.trandate BETWEEN TO_DATE('2026-08-01','YYYY-MM-DD')
                      AND TO_DATE('2026-08-31','YYYY-MM-DD')

QBO — the closest thing, and it is a report

QBO
GET /v3/company/{realmId}/reports/GeneralLedger
      ?start_date=2026-08-01&end_date=2026-08-31
      &columns=tx_date,txn_type,doc_num,name,memo,account_name,debt_amt,credit_amt

// Returns Rows/Columns with Header, Section and Summary row types.
// Correct totals. No stable row identity. Nothing to drill from.

The type the components consume

Xero hands you one signed NetAmount, QBO a PostingType enum, NetSuite two columns. Store two columns — the conversion only runs one way without loss.

journals.ts
type JournalLine = {
  accountId: string;
  /** Two columns. Exactly one is non-zero. Never a single signed amount. */
  debit: Money;
  credit: Money;
  dimensions: LineDimension[];
  partyId?: string;      // absent on Xero's GL feed
  memo?: string;
};

type Journal = {
  id: string;
  /** Xero's JournalNumber, NetSuite's internal id. Gapless where the source is. */
  sequence?: number;
  date: string;
  periodId?: string;
  /** What created this posting: an invoice, a payment, a manual entry. */
  origin: { type: string; documentId?: string };
  lines: JournalLine[];        // >= 2, and they balance
  book?: string;               // NetSuite accounting book
  source: SourceRef;
};

What this changes in the interface

The payoff. Every difference above that reaches the surface, and what the component does about it.

QBO statements have no posting-level backing
Every statement component declares its provenance — reconstructed or reported — and shows the tie-out variance against the ledger's own report. A controller trusts the variance line more than the chart.
Xero GL lines carry no party
Drill-down from a GL line goes to the source document for the party, rather than showing a blank counterparty column.
NetSuite postings are per book
The book is part of the statement header, alongside entity and period. Two books are two statements, never one merged number.

Where the accounting is explained

What consumes this payload

The normalised object above is what the components read. These are the surfaces it feeds, the words for the concepts in it, and the document that explains the accounting.

Compared acrossXero docsQuickBooks Online docsNetSuite docs

Works in your QuickBooks Online or Xero file

Get your books closed every month

Lopifi is building AI bookkeeping that keeps your books closed on US GAAP as transactions happen, with a CPA review every quarter, for $200 a month. We’re not open yet.

$200/month when we open. No card now.