Welcome to the v2 Lunch Money API. This API was built with help from our Developer Community and addresses many feature requests. including:
In addition to adding features requested by our developer community, we had several goals for this release:
This update focuses on staying consistent in several key areas:
We followed a "Design First" approach for this version of the API. This means we planned and documented the API using a standard OpenAPI specification before we built anything. The same specification was then used to generate our documentation and the types used in the API itself.
This helps ensure that our documentation always stays in sync with how the API works. It also gives our Developer Community a reliable spec to build their own language-specific SDKs and other tools.
We changed the way errors are handled so they follow standard RESTful API best practices. Error messages are now written to give you all the information you need to understand and fix the issue.
All Error responses will now use a 4XX Response Code:
400 - Bad Request - something was wrong with the parameters or request body401 - Unauthorized - there was an issue with the token sent in the Authorization header404 - Not Found - the object you’re trying to access was not found429 - Too Many Requests - too many requests were made during a short period; you’ll need to wait before trying again Retry-After telling you how many seconds to wait before making another API call. See the Rate Limiting GuideError response bodies will be JSON objects with two fields:
message - a general explanation of the errorerrors - an array of objects describing specific errors. Each object has at least the following:errMsg - a message explaining the error with a specific query, path parameter, or request propertyGenerally, if a request has multiple issues, the error response will list as many as possible in the errors array. However, it’s not guaranteed that all errors will be caught in a single request.
We added strict request validation so developers can quickly catch mistakes while building. In general, a request will return a 400 response if:
integer instead of number in the various request and response schemas to allow for better validation.We have attempted to name (or rename) object properties to apply consistent behavior as follows:
id) will simply be called that property, and not use object name in the property (ie: there is nocategory_id property on the category object).category_id)created_at, updated_at, archived_at).tag_ids).children which will contain a list of the child objects.Successful GETrequests will now return a 200 OK response. Response bodies will generally include either a:
Successful POST requests will now return a 201 Created response and use the following rules for request and response bodies:
amount and a date but may not include properties that are set by the system, such as id or date_created.Successful PUT requests will return a 200 OK response and use the following rules for request and response bodies:
PUT request, will completely replace any previous value for these properties. Therefore, if you wish to, for example, add a new tag to a transaction or a new child category to a category group, you should first query the existing object and then update the property appropriately.PUT requests using the following pattern:
GET /endpoint/{id} request.PUT /endpoint/{id} request, passing in the complete modified object.DELETE requests will return a 204 No Content response when successful. No response body will be returnedThe v2 Lunch Money API is versioned using a modified version of SEMVER as follows:
This initial public change log encapsulates the changes in the Lunch Money API from the v1 API to the initial public release of version v2.8.0 of the v2 API.
This document encapsulates the sum of changes vis-a-vis the v1 version of the API as of the initial release, but you can also view a detailed version history, which will be maintained going forward.
This section outlines elements that have changed from the existing v1 API behavior.
If you have an app that uses the v1 API you may wish to review these changes in the Migration Guide
{
"user_id": 123,
"user_name": "John Doe",
"user_email": "john@example.com"
}
{
"id": 123,
"name": "John Doe",
"email": "john@example.com",
}
collapsed property which may be set to true for Category Groups when the group has been collapsed in the Web UI.children property, which is present only when the is_group property is true, is no longer nullable. children property will always be the list of categories that belong to the group.children property of a Category Group will always have value of false for is_group and will never have a children property.children property with an empty list, ie: [], and not null.group_category_name property which only existed under certain circumstances in the V1 API has been removed.GET /categories changes: View DocsGET /v1/categories?format=nested // or "flattened" - default: flattened
GET /v2/categories // Default: nested alphabetized list
GET /v2/categories?format=flattened // All categories including duplicates
GET /v2/categories?is_group=false // Only assignable categories (no groups)
GET /v2/categories?is_group=true // Only category groups
children property of the Category Group.format is set to nested) is NOT the total number of categories and category groups. It is the number of category groups and ungrouped categories.format property is set to flattened, the response will be a list that includes all categories and category groups. Category groups will have a children property that is a list of category objects that belong to that category. flattened response.flattened response is the total number of category and category groups.category_group_name (which is now renamed group_name) property for grouped categories in response when the flattened query parameter was set to nestedgroup_name attribute is present only on categories that belong to a groupgroup_name will be present in all returned categories that belong to a group across all APIs and query parameter combinationsGET /categories/{id} changes: View Docs
created_at and updated_at properties in the returned category object.POST /categories changes: View Docs
This single endpoint is now used to create either a new category or a category group.
POST /v1/categories
{ "name": "Groceries", ... }
POST /v1/categories/group
{ "name": "Food & Drink", "category_ids": [...], ... }
POST /v2/categories
{ "name": "Groceries", ... }
// Returns: Full category object
POST /v2/categories
{
"name": "Food & Drink",
"is_group": true,
"children": [...] // Optional
}
// Returns: Full category group object with complete children
true if the category was created.children array.PUT /categories/{id} changes: View DocsPUT /v1/categories/:id
// Returns: true
POST /v1/categories/group/:group_id/add
{ "category_ids": [1, 2, 3] }
PUT /v2/categories/:id
{ "name": "Updated Name", ... }
// Returns: Full updated category object
PUT /v2/categories/:id
{ "children": [1, 2, 3] } // Replaces all children
// Returns: Full updated category group
is_group property of a category or category group will result in an error.children property specified will completely replace the existing child categories of the target category group.DELETE /categories/{id} changes: View DocsDELETE /v1/categories/:id/force // Force delete endpoint
// Returns: true or error with dependents object
DELETE /v2/categories/:id?force=true // Use query parameter
// Returns: 204 No Content (success) or 422 with dependents (if has dependencies and force=false)
force parameter is not set to true and the requested category has dependencies, a 422 "Unprocessable Content" status is returned.category_name property and a dependents property.updated_at - Date and time the tag was last updated (in the ISO 8601 extended format).created_at - Date and time the tag was created (in the ISO 8601 extended format).archived_at - Date and time the tag was archived (or null if not not archived).GET /v1/tags // List only, no order guarantee
GET /v2/tags // ✅ Alphabetical order (matches GUI)
GET /v2/tags/:id // ✅ New
POST /v2/tags // ✅ New
{ "name": "Work Expense", ... }
PUT /v2/tags/:id // ✅ New
{ "name": "Business Expense", ... }
DELETE /v2/tags/:id // ✅ New
The transaction object has changed significantly in v2. A transaction has properties that reference other objects in Lunch Money such as categories, accounts, and tags. In v1 the details for these related object were "hydrated". In the context of RESTful APIs, "hydration" generally refers to populating an object with all its data. In the Lunch Money APIs, hydration generally relates to providing the details of associated objects that are also referred to by their IDs in the response body.
Specific changes are:
amount continues to be a string that represents the amount of the transaction in whatever currency is specified in the currency property of the transaction.debits_as_negative query param controlled how to interpret the amount. If this parameter was set to false (the default), a negative amount value would indicate a credit transaction.amount field is always positive for debit transactions and negative for credit transactions, regardless of how the user has set the "Show debits as negative, credits as positive" toggle in the user settings of the Web UI.Migration: Modify existing code to expect that positive transaction amounts values are debit transactions, while negative values indicate credit transactions.
payee, category_id, and notes fields in the returned transaction objects now match the values seen on the transactions page in the GUI.overrides object will provide info on what the previous values for these fields were.display_name and display_notes fields have been removed.GET /recurring/{id} method and passing the value from the transaction's recurring_id propertycategory_id, no other category related fields are returned. This includes all the category_* fields in the v1 object as well as the is_income, exclude_from_budget, and exclude_from_totals properties which are derived from the category.status property now has only three supported values, reviewed, unreviewed, and deleted_pending.is_pending property is true will always have a status of unreviewed.has_children property has been removed.is_split_parent property has been added. It is set to true for transactions that been split.is_group property has been renamed to is_group_parent.group_id property has been renamed to group_parent_id.children property is no longer present on transactions unless they are the parent of a split transaction or a transaction group created by grouping multiple transactions together.GET /transactions endpoint.is_parent set to trueGET /transactions/{id}, it will include a children property which is an array of of the objects for the split transactions.parent_id property of split transactions which are returned by default.GET /transactions endpointis_group set to true.GET /transactions/group/{id} endpoint.manual_account_id replaces the asset_id property. The other v1 Transaction Object properties related to manual accounts, which were named asset_*, are no longer included and can be found by calling GET /manual_account/{id} endpoint using the value returned in the manual_account_id property.plaid_account_id property, the other v1 Transaction Object properties related to plaid accounts, which were named plaid_*, are no longer included and can be found by calling GET /plaid_account/{id} endpoint using the value returned in the plaid_account_id property.plaid_metadata property is no longer returned by default as part of the v2 Transaction Object. This can be added by explicitly setting the new query parameter, include_metadata to true on the GET /transactions/{id} API.tags property has been replaced by a tag_ids property which is a list of tag IDs associated with the transaction. If no tags are associated with the transaction this will be an empty list. The other elements of the tag object which were included in the v1 Transaction Object are no longer returned and can be found by calling the GET /tags endpoint.custom_metadata has been added to the Transaction Object. This can be added and updated in transactions that are created or modified via the API. files has been added to the Transaction Object. It is an array of objects that describe any files that have been attached to the transaction.View v2 Transaction Object Docs
The transactions endpoint is one of the most complex in Lunch Money. To simplify the documentation, it is broken down into four subsections:
GET /transactions/{id} has the following changes View DocsGET /transactions endpoint. These include:
plaid_metadata custom_metadatafileschildren - if the returned transaction has either the is_parent or is_group property set to truePUT /transactions/{id} has the following changes View DocsPUT /v1/transactions/:id
{
"transaction": { ... },
"split": [...], // ❌ Removed
"debit_as_negative": false, // ❌ Removed
"skip_balance_update": true // ❌ Renamed
}
PUT /v2/transactions/:id?update_balance=false // Query parameter
{
// Transaction properties to update
// System properties like "id" or "updated_at" are ignored
}
split parameter has been removed. Use POST /transactions/split/{id} to split transactions instead.DELETE /transactions/{id} has been added View Docs204 No Content response when successful.GET /transactions has the following changes View DocsGET /v1/transactions
// Requests with no start_date and end_date return transactions for current month
GET /v1/transactions?pending=true
// Response will include pending transactions
GET /v2/transactions?debits_as_negative=true
// Query param controlled the sign of amounts for debits/credits
GET /v1/transactions
// Requests with no start_date and end_date returns all recent transactions
// Positive amount values indicate debit transactions,
// negative amount values indicate credit transitions.
GET /v2/transactions?include_pending=true
// Optional parameters:
// ?include_metadata // Include plaid_metadata, custom_metadata
// ?include_files // Include file attachments
// ?include_children // Include children for split/grouped
// ?include_split_parents // Include parent transactions
// ?include_group_children // Include grouped transactions
// ?created_since=2024-01-01 // Filter by creation timestamp (date or datetime)
// ?updated_since=2024-01-01T12:00:00Z // Filter by update timestamp (date or datetime)
plaid_metadata / custom_metadata - enable via include_metadatachildren - enable via include_childrenfiles - enable via include_filesParents of split transactions and grouped transactions are no longer returned by default, matching the GUI behavior.
created_since and updated_since query parameters allow you to filter transactions based on when they were created or last updated:
created_at or updated_at is greater than the provided timestampGET /v2/transactions?created_since=2024-01-01&updated_since=2024-01-15T10:00:00ZPOST /transactions has the following changes View Docsasset_id is now manual_account_idtags is now tag_idsplaid_account_id property on the Create Transaction Objectmanual_account_id property must not also be set on the same transaction.POST /tags API to create the tag, and then add the new tag's ID to the tag_ids property in the new transaction in the request body.debit_as_negative property in the request body is no longer supported. Developers should examine the value of the debit_as_negative property of the user object to determine what negative and positive amounts represent and perform any transformations, as needed, on the client side.external_id fields has changedexternal_ids are found on multiple transactions with the same manual_account_id within the request body, this is treated as an error. A 400 will be returned with an error response indicating which transactions were duplicates.external_ids in the request body conflict with external_ids on existing transactions with the same manual_account_id, data on these will be returned in a 201 response, but any non duplicate transactions in the request will still be inserted.transactions array.PUT /transactions has been added View DocsFooFoo7874jpESPNDELETE /transactions has been added View Docsids to be deleted in a single request.
204 No Content response when successful.POST /transactions/group has the following changes View DocsPOST /v1/transactions/group
{
"transactions": [id1, id2, ...],
"tags": ["Work"],
// other properties
}
POST /v2/transactions/group
{
"ids": [id1, id2,...], // ✅ Renamed from "transactions"
"tag_ids": [1, 2], // ✅ Renamed from "tags"
"status": "unreviewed" // ✅ New optional property
}
transactions → idstags → tag_idsDELETE /transactions/group/{group_id} has the following changes View Docs204 No Content on success (instead of 200 with transaction IDs). The transaction group is deleted and the transactions are restored to "normal" transactions.POST /transactions/split/{id} endpoint has been added View DocsPUT /v1/transactions/:id
{ "split": [...] }
// Deletes existing children if already split
POST /v2/transactions/split/:id
{ "child_transactions": [
// objects with at least "amount" and "payee" fields
] }
// Returns: Full transaction with is_parent: true and children array
// Fails if transaction already split
DELETE /transactions/split/{parent_id} endpoint has been added View DocsPOST /v1/transactions/unsplit
{ "parent_ids": [...] }
DELETE /v2/transactions/split/:parent_id
// Returns: 204 No Content
The transactionAttachmentObject is a new object type that represents a file attached to a transaction. It has the following properties:
id (integer) - The unique identifier for the attachmentuploaded_by (integer) - The ID of the user who uploaded the filename (string) - The original filename of the uploaded filetype (string) - The MIME type of the filesize (integer) - The size of the file in bytesnotes (string, optional) - Any notes associated with the attachmentcreated_at (string) - The date and time when the file was uploaded (in ISO 8601 format)POST /transactions/{transaction_id}/attachments View Docs
multipart/form-dataGET /transactions/attachments/{file_id}/url View Docs
DELETE /transactions/attachments/{file_id} View Docs
204 No Content on successEssentially the object's properties are organized into one of four categories:
id, description and source.transaction_criteria object contains the properties that are used to determine if a transaction matches the recurring item, such as payee, amount and an optional account id.overrides object contains properties that will be updated in matching transactions such as payee, notes and category_idmatches object contains details about the dates that matching transactions are expected, the transaction_ids for transactions that are found, and the dates when transactions are expected, but have not been foundSpecific changes are:
status property has been added. Recurring items with a status of suggested do not modify transactions, while recurring items with a status of reviewed do.start_date and end_date properties now belong to the transaction_criteria object. These represent the date range that a transaction must fall into to match.granularity and quantity properties now belong to the transaction_criteria object. These define how frequently recurring transactions are expected to occur.billing_date property has been renamed to anchor_date and moved to the transaction_criteria object. This date is used in conjunction with the granularity and quantity properties to identify matching transactions.original_name property has been renamed to payee and is now part of the transaction_criteria.amount, currency, to_base and plaid_account_id properties are now all part of the transaction_criteria object. The asset_id property has been renamed to manual_account_id and is also part of transaction_criteria.payee, notes and category_id properties are now part of the overrides object. When present, they represent the values that will be displayed in matching transactions.category_group_id, is_income, and exclude_from_totals properties have been removed. These are attributes of the category which can be discovered by examining the category with overrides.category_id.date property which was populated with the date that the request was run, has been replaced by request_start_date and request_end_date in the matches object. These specify the range that was set in the request and was used to populate the dates and transactions in the rest of the matches object.occurrences property has been renamed to expected_occurrence_dates and is now part of the matches object. It contains an array of dates within the request range where recurring transactions are expected. In v1 the occurrences property would include the last expected date prior to the range, and the next expected date after the range. In v1, the dates are limited by the range.transactions_within_range property has been renamed to found_transactions and is now part of the matches object. It is now populated by an array of objects that include a date and a transaction_id property which describe the found matching transactions in the range. It is now populated solely by transactions within the request range and will not include the last transaction prior to the range as it did in v1.missing_dates_within_range has been renamed to missing_transaction_dates and is now part of the matches object. It is populated by the subset of dates in the expected_occurrence_dates where no matching transaction was found.GET /recurring_items/{id} has been added View DocsGET /v1/recurring_items?start_date=2024-06-01
// Returns: Reviewed items only
GET /v2/recurring_items?start_date=2024-06-01&end_date=2024-06-30
// Both start and end are required if using dates
GET /v2/recurring_items?include_suggested=true
// Include suggested recurring items
GET /v2/recurring_items/:id // ✅ New: Get single recurring item
asset which was used for an endpoint and an object that represented manually created accounts has been changed to simply manual_account.{
"type_name": "depository", // ❌ Renamed
"subtype_name": "...", // ❌ Renamed
"exclude_transactions": false // ❌ Renamed
}
{
"type": "cash", // ✅ Renamed (depository → cash)
"subtype": "...", // ✅ Renamed
"exclude_from_transactions": false, // ✅ Renamed
"updated_at": "...", // ✅ New
"external_id": "...", // ✅ New (API-only)
"custom_metadata": {} // ✅ New (API-only, < 4MB)
}
View v2 Manual Account Object Docs
GET /manual_accounts replaces v1 GET /assets View DocsGET /manual_accounts/{id} has been added View DocsPOST /manual_accounts replaces v1 POST /assets View DocsPUT /manual_accounts replaces v1 PUT /assets View DocsDELETE /manual_accounts has been added View DocsThe properties of the Plaid Account Object are unchanged from the v1 version of the API with the following exception:
allow_transaction_modification property has been added. This represents the state of the "Allow Modification To Transactions" toggle seen in the Plaid Accounts Detail view. This is enabled by default. When disabled, attempts to insert new transactions associated with this account will fail.View v2 Plaid Account Object Docs
GET /plaid_accounts/{id} has been added View DocsPOST /plaid_accounts/fetch endpoint has the following changes View DocsA new v2/summary endpoint replaces the v1/budgets endpoint.
This endpoint significantly refactors the response and aligns with the recently released v2 Budgets feature.