API Reference

Reports API

Query revenue, outstanding, profit and loss, VAT, tax set-aside, and team analytics from the Rebill API.

Last updated

The reports endpoints give you aggregated analytics about your revenue, expenses, outstanding invoices, and team performance. All amounts are integers in the smallest currency unit (cents). Every report that totals money returns per-currency breakdowns (an object keyed by ISO 4217 code) rather than a single blind cross-currency number, since an account can bill in more than one currency.

Revenue report

GET /report/revenue

Returns revenue from issued invoices (sent, paid, or paid_partial - not just paid) within a date range, broken down by period.

Query parameterRequiredDescription
fromYesStart date (YYYY-MM-DD or full ISO 8601 timestamp)
toYesEnd date; the whole day is included
periodNoGrouping: monthly (default), quarterly, or yearly
include_invoicesNoSet to true to include the underlying invoice list, sorted by date descending
labelNoKeep only invoices (or payments on a cash basis) of clients carrying at least one of these client labels; repeat or comma-separate. Rows then carry client_labels.
curl "https://api.rebill.co.za/report/revenue?from=2026-01-01&to=2026-03-31&period=monthly" \
  -H "Authorization: Bearer sk_your_secret_key"

Response:

{
  "invoice_count": 9,
  "revenue_by_currency": { "ZAR": 450000 },
  "period_breakdown": [
    {
      "period": "2026-01",
      "label": "Jan 2026",
      "count": 3,
      "amounts": { "ZAR": 150000 }
    }
  ]
}
Response fieldTypeDescription
invoice_countintegerNumber of issued invoices in range
revenue_by_currencyobjectTotal revenue per currency code
period_breakdownarrayPer-period rows: period, label, count, amounts (per currency)
invoicesarrayIndividual invoices, only when include_invoices=true

Outstanding (aging) report

GET /report/outstanding

Returns outstanding invoice balances grouped into aging buckets, measured from each invoice's due date: current (0-30 days), 31-60 days, 61-90 days, and 90+ days overdue. No date range - covers every outstanding invoice regardless of issue date.

Query parameterRequiredDescription
include_invoicesNoSet to true to include the underlying invoice list, sorted by days overdue descending
labelNoKeep only invoices of clients carrying at least one of these client labels; repeat or comma-separate. Rows then carry client_labels.
curl https://api.rebill.co.za/report/outstanding \
  -H "Authorization: Bearer sk_your_secret_key"

Response:

{
  "invoice_count": 6,
  "outstanding_by_currency": { "ZAR": 320000 },
  "aging_breakdown": [
    { "bucket": "current", "label": "Current (0-30 days)", "count": 3, "amounts": { "ZAR": 150000 } },
    { "bucket": "30_days", "label": "31-60 days",          "count": 2, "amounts": { "ZAR": 80000 } },
    { "bucket": "60_days", "label": "61-90 days",          "count": 1, "amounts": { "ZAR": 60000 } },
    { "bucket": "90_plus", "label": "90+ days overdue",    "count": 0, "amounts": {} }
  ]
}

Payments report

GET /report/payments

Returns the payments received in a date range, one row per invoice the payment settled, for reconciling a bank statement. A payment that settled two invoices returns two rows; amount is the part applied to that invoice and payment_amount the full deposit. Any unallocated remainder is its own row with no invoice_id. Rows are sorted by received ascending. Credit adjustments are excluded.

Query parameterRequiredDescription
fromYesStart date (YYYY-MM-DD or full ISO 8601 timestamp), matched on the payment received date
toYesEnd date; the whole day is included
client_idNoOnly payments from this client
methodNoOnly payments with this reconciliation method: eft, cash, card, other, or a gateway such as yoco or paystack
curl "https://api.rebill.co.za/report/payments?from=2026-09-01&to=2026-09-30" \
  -H "Authorization: Bearer sk_your_secret_key"

Response:

{
  "payment_count": 2,
  "total_by_currency": { "ZAR": 283215 },
  "method_breakdown": [
    { "method": "eft", "count": 1, "amounts": { "ZAR": 188715 } },
    { "method": "yoco", "count": 1, "amounts": { "ZAR": 94500 } }
  ],
  "rows": [
    {
      "payment_id": "pay_abc123",
      "received": "2026-09-01T00:00:00Z",
      "invoice_id": "inv_xyz789",
      "invoice_number": "INV-26860",
      "client_id": "cli_def456",
      "client_name": "Hayli Soal",
      "amount": 94500,
      "payment_amount": 94500,
      "currency": "ZAR",
      "gateway": "yoco",
      "method": "yoco",
      "channel": "card",
      "reference": "EB02.09"
    },
    {
      "payment_id": "pay_ghi789",
      "received": "2026-09-01T00:00:00Z",
      "invoice_id": "inv_jkl012",
      "invoice_number": "INV-26866",
      "client_id": "cli_mno345",
      "client_name": "Rissa Parker",
      "amount": 188715,
      "payment_amount": 188715,
      "currency": "ZAR",
      "gateway": "manual",
      "method": "eft",
      "reference": "AM01.09.1",
      "recorded_by": "[email protected]"
    }
  ]
}
Response fieldTypeDescription
payment_countintegerNumber of payments in range (not rows)
total_by_currencyobjectTotal received per currency code, in minor units
method_breakdownarrayPer-method rows: method, count (payments), amounts (per currency)
rowsarrayOne row per settled invoice or unallocated remainder, oldest first
rows[].gatewaystringRaw source: manual, bank_import, or the gateway name
rows[].methodstringReconciliation bucket: the capture method for manual payments, eft for bank-import matches, the gateway name otherwise
rows[].channelstringThe gateway's own channel (card, eft) when known; absent for manual payments
rows[].recorded_bystringEmail of the team member who captured a manual payment; absent otherwise

Client revenue report

GET /report/clients

Returns revenue from issued invoices aggregated per client, sorted by total revenue descending.

Query parameterRequiredDescription
fromYesStart date (YYYY-MM-DD or full ISO 8601 timestamp)
toYesEnd date; the whole day is included
include_invoicesNoSet to true to include the underlying invoice list
labelNoKeep only clients carrying at least one of these client labels; repeat or comma-separate. Each client row carries client_labels.
curl "https://api.rebill.co.za/report/clients?from=2026-01-01&to=2026-03-31" \
  -H "Authorization: Bearer sk_your_secret_key"

Response:

{
  "client_count": 4,
  "revenue_by_currency": { "ZAR": 450000 },
  "clients": [
    {
      "client_id": "xyz789",
      "client_name": "Acme Corp",
      "invoice_count": 5,
      "revenue_by_currency": { "ZAR": 250000 },
      "invoice_count_by_currency": { "ZAR": 5 },
      "average_invoice_by_currency": { "ZAR": 50000 }
    }
  ]
}

Expense report

GET /report/expenses

Returns expense totals broken down by category and by period, with an optional profit summary comparing expenses against invoiced revenue.

Query parameterRequiredTypeDescription
fromYesdateStart of range (YYYY-MM-DD)
toYesdateEnd of range; the whole day is included
periodNostringmonthly (default), quarterly, or yearly
include_profitNobooleanInclude a profit summary comparing revenue against expenses
curl "https://api.rebill.co.za/report/expenses?from=2026-01-01&to=2026-03-31&period=monthly&include_profit=true" \
  -H "Authorization: Bearer sk_your_secret_key"

Response:

{
  "total_expenses_by_currency": { "ZAR": 185000 },
  "expense_count": 12,
  "by_category": [
    { "category_id": "cat-001", "category_name": "Travel & Transport", "count": 5, "totals": { "ZAR": 75000 } }
  ],
  "period_breakdown": [
    { "period": "2026-01", "label": "Jan 2026", "count": 4, "totals": { "ZAR": 62000 } }
  ],
  "profit_summary": {
    "revenue_by_currency": { "ZAR": 450000 },
    "expenses_by_currency": { "ZAR": 185000 },
    "net_profit_by_currency": { "ZAR": 265000 }
  }
}

The profit_summary field is only present when include_profit=true. Its revenue figure is calculated from invoices issued within the same date range.

Profit and loss report

GET /report/profit-loss

Returns income, expenses, and net profit for a date range, with a per-period breakdown.

Query parameterRequiredTypeDescription
fromYesdateStart of range (YYYY-MM-DD)
toYesdateEnd of range; the whole day is included
periodNostringmonthly (default), quarterly, or yearly
basisNostringcash (default) recognises income on the date payment was received; accrual recognises it on invoice_date regardless of payment status
curl "https://api.rebill.co.za/report/profit-loss?from=2026-01-01&to=2026-03-31&basis=accrual" \
  -H "Authorization: Bearer sk_your_secret_key"

Response:

{
  "basis": "accrual",
  "income_by_currency": { "ZAR": 450000 },
  "expenses_by_currency": { "ZAR": 185000 },
  "net_by_currency": { "ZAR": 265000 },
  "period_breakdown": [
    {
      "period": "2026-01",
      "label": "Jan 2026",
      "income_by_currency": { "ZAR": 150000 },
      "expenses_by_currency": { "ZAR": 62000 },
      "net_by_currency": { "ZAR": 88000 }
    }
  ]
}

Adjustments (credit notes, manual corrections) are excluded from cash basis income - only actual payment receipts count.

Summary report

GET /report/summary

Returns combined revenue and expense detail for a date range: every issued invoice and every expense in the window, with totals and net profit. Useful for a single-request dashboard view.

Query parameterRequiredDescription
fromYesStart date (YYYY-MM-DD)
toYesEnd date; the whole day is included
curl "https://api.rebill.co.za/report/summary?from=2026-01-01&to=2026-03-31" \
  -H "Authorization: Bearer sk_your_secret_key"

Response:

{
  "invoice_count": 9,
  "expense_count": 12,
  "revenue_by_currency": { "ZAR": 450000 },
  "total_expenses_by_currency": { "ZAR": 185000 },
  "net_profit_by_currency": { "ZAR": 265000 },
  "invoices": [
    {
      "id": "abc123",
      "number": "INV0001",
      "client_name": "Acme Corp",
      "invoice_date": "2026-03-01T00:00:00Z",
      "due_date": "2026-03-15T00:00:00Z",
      "amount": 150000,
      "status": "paid",
      "currency": "ZAR"
    }
  ],
  "expenses": [
    {
      "id": "exp-001",
      "date": "2026-03-10T00:00:00Z",
      "supplier_name": "BP Garage Stellenbosch",
      "category_name": "Travel & Transport",
      "amount": 45000,
      "tax_amount": 6522,
      "currency": "ZAR"
    }
  ]
}

VAT report

GET /report/vat

Returns a VAT return for a date range: output tax on sales minus input (deductible) tax on expenses, split by VAT type. Requires the account to have a VAT number set; returns 403 Forbidden otherwise. Only documents in your account's default currency are counted - foreign-currency invoices and expenses are excluded and their count reported separately.

Query parameterRequiredTypeDescription
fromYesdateStart of range (YYYY-MM-DD)
toYesdateEnd of range; the whole day is included
basisNostringaccrual or cash; defaults to your account's VAT basis setting
curl "https://api.rebill.co.za/report/vat?from=2026-01-01&to=2026-03-31&basis=accrual" \
  -H "Authorization: Bearer sk_your_secret_key"

Response:

{
  "period": { "from": "2026-01-01", "to": "2026-03-31", "basis": "accrual" },
  "currency": "ZAR",
  "output": {
    "standard_excl": 300000,
    "standard_vat": 45000,
    "zero_rated_excl": 0,
    "exempt_excl": 0,
    "total_vat": 45000
  },
  "input": {
    "deductible_net": 38478,
    "total_vat": 6522
  },
  "net_vat": 38478,
  "excluded_foreign_count": 0,
  "lines": [
    {
      "type": "sale",
      "date": "2026-03-01",
      "ref": "INV0001",
      "party": "Acme Corp",
      "tax_type": "standard",
      "net": 300000,
      "vat": 45000
    }
  ]
}

lines contains one row per invoice (type sale) and per deductible expense (type purchase), for a full audit trail behind the totals.

Tax set-aside report

GET /report/tax-setaside

Estimates income tax to set aside each month based on year-to-date profit, projected annualised. ZA accounts with a ZAR base currency only. Requires an active premium subscription and your account's tax entity type to be set (individual · company · company_sbc) - returns 403 Forbidden with not_available or tax_entity_type_not_set otherwise, and 402 Payment Required on the free plan. Income is recognised on an accrual basis (issued invoices, excluding VAT for VAT-registered accounts) to match how SARS assesses trading income.

Query parameterRequiredDescription
yearNoA specific SARS tax year label (e.g. 2026); defaults to the tax year containing today
curl "https://api.rebill.co.za/report/tax-setaside?year=2026" \
  -H "Authorization: Bearer sk_your_secret_key"

Response:

{
  "entity_type": "individual",
  "tax_year_label": 2026,
  "from": "2025-03-01",
  "to": "2026-02-28",
  "current": true,
  "months_elapsed": 6,
  "currency": "ZAR",
  "months": [
    { "period": "2025-03", "label": "Mar 2025", "revenue": 80000, "expenses": 20000, "profit": 60000, "cumulative_profit": 60000 }
  ],
  "ytd_profit": 360000,
  "projected_profit": 720000,
  "estimated_tax": 90000,
  "monthly_set_aside": 7500,
  "should_have_by_now": 45000,
  "next_provisional_due": "2025-08-31",
  "excluded_foreign_count": 0
}

Team performance report

GET /report/team

Returns per-team-member quote win/loss and invoice outstanding/paid figures for a date range. Requires an admin-role user; every account has at least one admin (the original account owner), and API-key requests authenticate as that admin automatically, so this endpoint works with a plain API key the same as any other report.

Query parameterRequiredDescription
fromYesStart date (YYYY-MM-DD)
toYesEnd date; the whole day is included
curl "https://api.rebill.co.za/report/team?from=2026-01-01&to=2026-03-31" \
  -H "Authorization: Bearer sk_your_secret_key"

Response:

{
  "rows": [
    {
      "owner_user_id": "usr_001",
      "email": "[email protected]",
      "quotes_won_count": 4,
      "quotes_won_value_by_currency": { "ZAR": 300000 },
      "quotes_lost_count": 1,
      "quotes_lost_value_by_currency": { "ZAR": 50000 },
      "quotes_pending_count": 2,
      "quotes_pending_value_by_currency": { "ZAR": 120000 },
      "win_rate": 0.8,
      "invoices_outstanding_count": 3,
      "invoices_outstanding_by_currency": { "ZAR": 90000 },
      "invoices_paid_count": 5,
      "invoices_paid_by_currency": { "ZAR": 250000 }
    }
  ]
}

Quotes are filtered by quote_date within range; invoices_paid is filtered by invoice_date within range; invoices_outstanding is point-in-time (not date-filtered - it reflects what's outstanding right now). Documents with no owner are grouped under owner_user_id: "" with email: "Unassigned".

Was this article helpful?

Still need help?

Our support team is happy to help you get the most out of Rebill.

Contact support