Transactions Sync migration guide
Learn how to migrate from the /transactions/get endpoint to the /transactions/sync endpoint
/transactions/sync is a newer endpoint that replaces
/transactions/get and provides a simpler and easier model for managing transactions updates. While
/transactions/get provides all transactions within a date range,
/transactions/sync instead uses a cursor to provide all new, modified, and removed transactions that occurred since your previous request. With this cursor-based pagination, you do not need to worry about making redundant API calls to avoid missing transactions. Updates returned by
/transactions/sync can be patched into your database, allowing you to avoid a complex transaction reconciliation process or having to keep track of which updates have already been applied.
Looking for an example in code? Check out Pattern on GitHub for a complete, best-practice implementation of the Transactions Sync API within a sample app.
Update your client library
If you are using client libraries, you may need to update your current library to use
/transactions/sync. The following are the minimum Plaid client library versions required to support
/transactions/sync for each language:
- Python: 9.4.0
- Node: 10.4.0
- Ruby: 15.5.0
- Java: 11.3.0
- Go: 3.4.0
Detailed upgrade notes are language-specific may be found in the README and Changelog of the specific library. See the library's repo on the Plaid GitHub for more information.
Update callsites and pagination logic
Replace all instances of
/transactions/sync has a slightly different call signature from
/transactions/get and does not have the
count parameter inside the
options object and uses a
cursor instead of a
end_date. Pagination logic is also different and relies on the
has_more flag instead of the
transactions_count value. Note that when requesting paginated updates with
/transactions/sync, unlike when using
/transactions/get, it is important to retrieve all available updates before persisting the transactions updates to your database.
If a call to
/transactions/sync fails when retrieving a paginated update as a result of the
TRANSACTIONS_SYNC_MUTATION_DURING_PAGINATION error, the entire pagination request loop must be restarted beginning with the cursor for the first page of the update, rather than retrying only the single request that failed.
Update callsites for account and Item data
/transactions/sync does not return an account object or Item object. If your app relies on getting account data, such as balance, from the
/transactions/get call, use
/accounts/get instead to retrieve this information. If it relies on Item data, such as Item health status, use
Update webhook handlers
/transactions/sync, you should not listen for the webhooks
TRANSACTIONS_REMOVED. While these webhooks will still be sent in order to maintain backwards compatibility, they are not required for the business logic used by
Update initial call trigger
/transactions/get webhooks, the
SYNC_UPDATES_AVAILABLE webhook will not be fired for an Item unless
/transactions/sync has been called at least once for that Item. For this reason, you must call
/transactions/sync at least once before any sync webhook is received. After that point, rely on the
/transactions/sync will not return the
PRODUCT_NOT_READY error if transactions data is not yet ready when
/transactions/sync is first called. Instead, you will receive a response with no transactions and a null cursor. Even if no transactions data is available, this call will still initialize the
SYNC_UPDATES_AVAILABLE webhook, and it will fire once data becomes available.
The first call to
/transactions/sync once historical updates are available will often have substantially higher latency (up to 8x) than the equivalent call in a
/transactions/get-based implementation. Depending on your application's logic, you may need to adjust user-facing messaging or hard-coded timeout settings.
Update transaction reconciliation logic
The response to
/transactions/sync includes the patches you will need to apply in the
modified arrays within its response. You should apply these to your transactions records. Any additional logic required to fetch or reconcile transactions data can be removed.
Migrating existing Items
You likely already have transactions stored for existing Items. If you onboard an existing Item onto
"cursor": "" in the request body, the endpoint will return all historical transactions data associated with that Item up until the time of the API call (as "adds"). You may reconcile these with your stored copy of transactions to ensure that it reflects the the Item's true state.
If you have a large number of Items to update, this reconciliation process may be slow and generate excessive system load. One other option for onboarding existing Items onto
/transactions/sync is using
"cursor": "now" in the request body. The endpoint will return a response containing no transaction updates, but only a cursor that will allow you to retrieve all transactions updates associated with that Item going forward, after the time of the API call. Accordingly, you should ensure that your local copy of transactions for an Item is up-to-date at the time you call
"cursor": "now" for it, or else any transaction updates that occurred between the time that you last pulled fresh data and the time of your
/transactions/sync call may be missing.
"cursor": "now" will work exactly like a cursor that was found by starting with
"cursor": "" and paginating through all updates, with the only difference being that a transaction created before, but modified after, those requests would be returned as "added" if using
"cursor": "now", and "modified" if using
If you ever want to completely rebuild your local copy of transactions for an Item previously onboarded with
"cursor": "now", you may still do so with
Note that we strongly recommend that this cursor only be used with Items for which you've already used with
/transactions/get, and not any new Items, which should always be onboarded with
Test your integration
You can perform basic testing of your integration's business logic in Sandbox, using the
/sandbox/item/fire_webhook endpoint to simulate
SYNC_UPDATES_AVAILABLE. If this testing succeeds, you should then test your integration with internal test accounts in Development or Production before releasing it to your full Production userbase.