Skip to main content
Expense List API
curl --request GET \
  --url https://api.pazy.io/v1.0/expenses

Authentication

All requests require an API key in the request headers. Headers:
Authorization: Api-Key YOUR_API_KEY

Request

Query Parameters

ParameterTypeRequiredDescription
limitintegerNoNumber of records to return. Min 1, Max 100, Default 30.
cursorstringNoCursor for pagination. Use the nextCursor value returned by the previous response.
startDatestringNoStart date for filtering (ISO 8601 UTC format: YYYY-MM-DDTHH:mm:ssZ).
endDatestringNoEnd date for filtering (ISO 8601 UTC format: YYYY-MM-DDTHH:mm:ssZ).
statearrayNoExpense states to filter by. Allowed values: DRAFTED, PENDING, APPROVED, OUT_OF_POLICY. Currently supports exactly one state.
employeeNamestringNoFilter by the name of the employee who raised the expense (partial match).
tagIdintegerNoFilter expenses containing this organization tag id.
minAmountnumberNoFilter expenses with amount greater than or equal to this value.
maxAmountnumberNoFilter expenses with amount less than or equal to this value.

Code Examples

curl -X GET "https://api.pazy.io/v1.0/expenses?limit=30&state=APPROVED" \
  -H "Authorization: Api-Key YOUR_API_KEY"

Success Response

HTTP Status: 200 OK Response Fields:
FieldTypeDescription
okbooleanIndicates whether the request was successful
dataobjectContains the expense list response data
data.expensesarrayList of expenses
data.expenses[].idstringUnique identifier (slug) for the expense
data.expenses[].statestringCurrent state of the expense. Possible values: DRAFTED, PENDING, APPROVED, OUT_OF_POLICY
data.expenses[].syncStatestringSynchronization state with the accounting system (e.g., SYNCED, NOT_SYNCED)
data.expenses[].expenseTypestringType of the expense
data.expenses[].paymentStatestringPayment state of the expense
data.expenses[].currencystringCurrency code (e.g., INR)
data.expenses[].totalAmountnumberTotal amount of the expense
data.expenses[].itemCountintegerNumber of line items in the expense
data.expenses[].descriptionstringDescription of the expense
data.expenses[].identifierstringHuman-readable identifier / number for the expense
data.expenses[].selfUrlstringShareable URL for viewing the expense in the web interface
data.expenses[].transactionDataobjectTransaction details for the expense
data.expenses[].transactionData.utrstringUnique Transaction Reference (UTR) of the payment
data.expenses[].transactionData.dateConfirmedstringDate the transaction was confirmed (falls back to expense date or creation date)
data.expenses[].transactionData.narrationstringBank narration for the transaction
data.expenses[].transactionData.ledgerIdstring or nullLedger / instrument identifier the transaction is mapped to
data.expenses[].transactionData.isSelfTransferbooleanWhether the transaction is a self-transfer
data.expenses[].merchantobjectMerchant details
data.expenses[].merchant.namestring or nullMerchant name
data.expenses[].merchant.vpastring or nullMerchant VPA (UPI address)
data.expenses[].initiatorobjectDetails of the user who raised the expense
data.expenses[].initiator.slugstringUnique slug identifier of the initiator
data.expenses[].initiator.namestringFull name of the initiator
data.expenses[].initiator.emailstringEmail address of the initiator
data.expenses[].initiator.idstring or nullInternal id of the initiator
data.expenses[].cardInfoobject or nullCard details if the expense was paid by card. null otherwise
data.expenses[].cardInfo.maskedCardNumberstringMasked card number
data.expenses[].cardInfo.descriptionstringCard description
data.expenses[].cardInfo.namestringCard name
data.expenses[].syncStatusstringHuman-readable accounting sync status label
data.expenses[].statusstringHuman-readable status label for the expense
data.totalAmountAndCountByCurrencyarrayAggregated total amount and count of matching expenses, grouped by currency
data.totalAmountAndCountByCurrency[].currencystringCurrency code for the aggregate
data.totalAmountAndCountByCurrency[].amountnumber or stringSum of expense amounts in this currency
data.totalAmountAndCountByCurrency[].countnumber or stringNumber of expenses in this currency
data.contextobjectPagination metadata
data.context.countnumberNumber of expenses returned in this page
data.context.hasMorebooleanIndicates whether there are more expenses to fetch
data.context.nextCursorstring or nullCursor to fetch the next page. null when there are no more results

Expense State Values

ValueDescription
DRAFTEDExpense is in draft state
PENDINGExpense is pending approval
APPROVEDExpense has been approved
OUT_OF_POLICYExpense was flagged as out of policy

Response Example

{
  "ok": true,
  "data": {
    "expenses": [
      {
        "id": "expense_identifier",
        "state": "APPROVED",
        "syncState": "SYNCED",
        "expenseType": "GENERAL",
        "paymentState": "PAID",
        "currency": "INR",
        "totalAmount": 1500.00,
        "itemCount": 2,
        "description": "Team lunch",
        "identifier": "EXP-2026-001",
        "selfUrl": "https://app.pazy.io/p/expense/expense_identifier",
        "transactionData": {
          "utr": "123456789012",
          "dateConfirmed": "2026-01-15T10:30:00Z",
          "narration": "UPI/abc@bank/Team lunch",
          "ledgerId": "ledger_identifier",
          "isSelfTransfer": false
        },
        "merchant": {
          "name": "ABC Restaurant",
          "vpa": "abc@bank"
        },
        "initiator": {
          "slug": "user_identifier",
          "name": "John Doe",
          "email": "john@example.com",
          "id": "123"
        },
        "cardInfo": {
          "maskedCardNumber": "XXXX XXXX XXXX 1234",
          "description": "Corporate Card",
          "name": "John Doe"
        },
        "syncStatus": "Synced",
        "status": "Approved"
      }
    ],
    "totalAmountAndCountByCurrency": [
      {
        "currency": "INR",
        "amount": "1500.00",
        "count": "1"
      }
    ],
    "context": {
      "count": 30,
      "hasMore": true,
      "nextCursor": "30"
    }
  }
}

Error Responses

Validation Error

HTTP Status: 400 Bad Request
{
  "ok": false,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Validation failed: /query/state: must be equal to one of the allowed values"
  }
}

Access Denied

HTTP Status: 403 Forbidden
{
  "ok": false,
  "error": {
    "code": "ACCESS_DENIED",
    "message": "Access denied: You can only view vendors you own"
  }
}

Authentication Errors

HTTP Status: 401 Unauthorized
{
  "ok": false,
  "error": {
    "code": "MISSING_CREDENTIALS",
    "message": "Missing Credentials"
  }
}
{
  "ok": false,
  "error": {
    "code": "INVALID_API_KEY",
    "message": "Invalid API Key"
  }
}

Permission Errors

HTTP Status: 403 Forbidden
{
  "ok": false,
  "error": {
    "code": "INSUFFICIENT_PERMISSIONS",
    "message": "Permission check failed - PERMISSION_CHECK_FAILED"
  }
}

Internal Error

HTTP Status: 500 Internal Server Error
{
  "ok": false,
  "error": {
    "code": "INTERNAL_ERROR",
    "message": "Internal error"
  }
}

Best Practices

  • Use cursor and limit together to paginate; pass the nextCursor from the response as the cursor value for the next call
  • startDate and endDate are filtered against the expense date — provide both for a bounded range
  • Use the id from the response as the input to the Expense Details API for full expense information
  • The API returns at most 100 expenses per call regardless of the limit value
  • Access is limited to users with expense read permission and an admin or bookkeeper role