Skip to content
English
  • There are no suggestions because the search field is empty.

Incremental API Syncs: Loading Only Changed Holistiplan Data

Use this guide when your integration regularly retrieves Holistiplan data and you want to avoid loading an entire firm on every run.

An incremental load, or delta, retrieves only records that changed since your integration's previous successful run. This reduces the number of records your integration needs to retrieve and process.
Why use incremental loads?

As the amount of data in a firm grows, repeatedly retrieving the entire dataset becomes increasingly inefficient. A full load requires your integration to transfer, process, and compare records that have not changed since the previous run.

An incremental load instead retrieves only the records that are new or have changed since the last successful run. This can:

  • Reduce API requests and data transfer by avoiding records that have not changed.
  • Improve sync performance by giving your integration fewer records to process.
  • Reduce processing and storage overhead in your own systems.
  • Scale more efficiently as the amount of data in a firm grows.
  • Keep data synchronized more frequently because each run has less data to process.

Incremental loading is a common data integration pattern. For additional background, see Microsoft's documentation on incremental data loading or AWS's guidance on incremental data pipelines. Microsoft describes the pattern as copying only new or changed data rather than copying everything on every run, while AWS notes that incremental loading reduces the volume of data moved and processed during each load.

Recommended approach: Use a full load for your initial synchronization, then use incremental loads for subsequent runs whenever the endpoint supports an appropriate timestamp filter.

API version: All examples in this guide use API v2 under /v2/. Version 1 is deprecated. Build new integrations on API v2.

How incremental syncs work

For most resources, an incremental sync follows this pattern:

  1. Store a timestamp from your last successful sync.
  2. Request records using the appropriate timestamp filter and __gte.
  3. Retrieve all pages until a page is empty.
  4. Process each record using its id as the unique identifier.
  5. Save the newest timestamp only after the entire sync completes successfully.
  6. Use that timestamp as the cursor for the next run.

Important: Use __gte

Use __gte rather than __gt when filtering by your cursor.

__gte means "this timestamp or later," so the record at the exact cursor may be returned again on the next run. Your integration should safely process the same record more than once by updating the existing record using its id.

This approach helps prevent records from being missed when multiple records share the same timestamp.

Pagination

Lists are paginated using limit and offset.

  • Default page size: 10
  • Maximum page size: 100

Continue requesting pages until the API returns an empty page.

A delta reduces the number of records you need to retrieve, but you should still paginate through the complete result set for each run.

Saving your cursor

On each successful run:

  1. Keep the newest timestamp returned by the API.
  2. Use the timestamp returned by Holistiplan rather than the clock on your server.
  3. Use that timestamp as the cursor for your next run.
  4. Save the new cursor only after all pages have been processed successfully.

If a sync fails partway through, retry using the previous successful cursor.

Sort by the same timestamp you filter on. When the endpoint supports it, add id as a second sort field so page boundaries remain stable.

Use ISO 8601 timestamps, for example:

2026-10-01T15:30:00Z

Example

GET /api/public/v2/households/?last_updated__gte=2026-10-01T15:30:00Z&ordering=last_updated&limit=100Choosing the right timestamp

The timestamp you use depends on what you are trying to synchronize.

  • last_updated: The record was saved or edited.
  • created: The record was first created.
  • uploaded_at: A document was uploaded.
  • action_at: An activity event occurred.
  • created_at: A resource was created or saved, depending on the endpoint.

Choose the filter that matches the question your integration is asking.

For example, use last_updated__gte when you need both newly created and subsequently edited records. Use created__gte when you only need records that were first created during the sync window.


Incremental sync reference
Resource Recommended filter Use it for Important note
Households last_updated__gte Household changes Deletions and status changes use Household Activity
Clients last_updated__gte Client changes Deletions use Household Activity
Income Tax last_updated__gte Parsed tax data Report layout changes are separate
Scenario Analyses last_updated__gte New and edited scenarios created only represents the original upload
Tax Returns uploaded_at__gte New document uploads Poll processing IDs separately
Tax Reports No delta filter Formatted reports Use income tax/scenario changes plus periodic full loads
Roth Projections No timestamp filter Current projections Use a full list or filter by household
Tax Letter Status No timestamp filter Current letter status Request by tax year
PDF Snapshots No date filter Snapshot jobs Poll using the snapshot job ID
Usage created__gte New usage events Refunds require a slower full comparison
Household Activity action_at__gte Household/client events Event rows do not change after creation
User Activity Report date__gte Daily advisor activity Always request the current day again
Client Upload Links created_at__gte New/refreshed upload links Deleted links require ID comparison
Users None Firm setup Full read
Units None Firm setup Full read
Permission Groups None Firm setup Full read
Subsidiaries None Firm setup Full read
Affiliation Firms created_at__gte for new firms Current affiliation data Counts/status are calculated at request time

Households

Endpoint: GET /households/

Filter: last_updated__gte

Use this endpoint when you maintain a CRM or warehouse copy of household information such as:

  • Name
  • Display name
  • State
  • CRM key
  • External reference
  • Subsidiary
  • Billed status
  • Primary advisor
  • Tax preparer

last_updated changes when the household record is saved, including updates made through the API.

created__gte answers a different question: which households were first created during the specified window. An older household that was edited later appears in a last_updated delta but not a created delta.

Pair with

Use Households alongside Clients, Income Tax, and Tax Reports. A change to a client, tax return, or report does not update the household timestamp.

Use Household Activity to identify:

  • Deletes
  • Deactivations
  • Reactivations
  • Merges

Clients

Endpoint: GET /clients/

Filter: last_updated__gte

Use this endpoint when you synchronize client information such as:

  • Name
  • Birth date
  • Primary contact
  • Email addresses
  • CRM key
  • Active status
  • Household association

last_updated changes when the client record is saved.

created__gte only identifies clients that were added during the specified window.

Pair with Household Activity

Use Household Activity to identify client removals.

  • client_created: A client was added.
  • client_edited: A client changed.
  • client_deleted: A client was removed.

The activity event does not contain the client's fields. For client_created and client_edited, read GET /clients/{id}/ to retrieve the current values.

A deleted client returns 404.


Income Tax

Endpoint: GET /income_tax/

Filter: last_updated__gte

Use this endpoint when you need parsed tax return data that is new or has been edited.

This is the primary feed for tax numbers.

A new return and an edited return both update last_updated.

You can also filter by:

  • household
  • year
  • tax_payer_1
  • tax_payer_2

The response includes created, which represents when the source document was uploaded. This endpoint does not accept created__gte.

You do not need created__gte because last_updated__gte includes new returns.

Note: This endpoint contains filed return data. Scenarios are available through Scenario Analyses.

Pair with

Use Tax Returns when you need upload status.

Use Tax Reports when you need the client-facing formatted report.

Changes to report layout, such as showing or hiding a section, changing the font, or changing the display name, do not update last_updated on the Income Tax record.


Scenario Analyses

Endpoint: GET /scenario_analyses/

Filters:

  • last_updated__gte for anything that changed
  • created__gte for scenarios whose source document was uploaded during the specified window

Use this endpoint when you store scenario results and want each sync to pick up new and edited scenarios.

last_updated changes when the scenario is saved.

created represents the original document upload time and does not change when someone later edits the scenario.

Filtering only on created can therefore cause your integration to miss scenario edits.

You can also filter by:

  • household
  • year
  • tax_payer_1
  • tax_payer_2

Pair with

Use Tax Reports and the scenario's Income Tax ID when you need the formatted report.

As with Income Tax, layout-only report changes are not represented by the scenario timestamp.


Tax Returns

Endpoint: GET /tax_returns/

Filter: uploaded_at__gte

Use this endpoint to discover newly uploaded tax documents.

After identifying new uploads, read each tax return by ID until its processing status is complete.

Tax return status can include:

  • Uploaded
  • Processing
  • Ready
  • Needs review

uploaded_at represents when the document was created. It does not change when processing finishes, or when the household or tax year associated with the document is changed.

Therefore:

  • Use uploaded_at__gte to discover new uploads.
  • Do not use it to determine whether an existing upload finished processing.

Processing workflow

  1. Use uploaded_at__gte to find new uploads.
  2. Store the IDs that are still processing.
  3. Request each ID using GET /tax_returns/{id}/.
  4. Continue polling until the status settles.
  5. Once processing succeeds, retrieve parsed field values from Income Tax.

Tax Reports

Endpoint: GET /tax_reports/

Tax Reports do not support a created or updated timestamp filter.

Use the following pattern instead:

  1. Run an incremental sync on Income Tax or Scenario Analyses.
  2. Identify the Income Tax IDs that changed.
  3. Request the corresponding Tax Report using GET /tax_reports/{income_tax_id}/.
  4. Run a full Tax Reports list on a slower schedule to capture layout-only changes.

Use Tax Reports when you need the formatted report, including:

  • Display name
  • Font
  • Layout
  • Sections shown

Changing the report layout does not update the Income Tax timestamp.

The report ID is the Income Tax ID.


Roth Projections

Endpoint: GET /roth_projections/

This endpoint does not provide a created or updated timestamp filter.

Use either:

  • A full list, or
  • The household filter when you already know which households need to be refreshed.

Use this endpoint when you need the projection name, household, and year.

The projection's detailed assumptions are not available on this endpoint.


Tax Letter Status

Endpoint: GET /tax_letter_status/

Request the list for a specific tax year:

GET /tax_letter_status/?year=2025

Use this endpoint when you need current tax letter status.

Each row includes:

  • tax_letter_status_last_modified
  • tax_letter_sent_datetime

These timestamps are not filters. Request the relevant tax year and keep rows whose timestamps are newer than your previous run.


PDF Snapshots

Endpoints:

GET /pdf_snapshots/ GET /pdf_snapshots/{id}/

PDF snapshots use a job-based workflow rather than a timestamp-based delta.

After you queue a snapshot, use the returned snapshot_job_id to poll the job.

The workflow is:

  1. Queue a snapshot from the Tax Report or Roth Projection using POST .../queue_pdf_snapshot/.
  2. Store the returned snapshot_job_id.
  3. Request GET /pdf_snapshots/{id}/.
  4. Continue polling until processing_status is successful.
  5. Download the completed PDF using GET /pdf_snapshots/{id}/download/.

The list includes created_at and last_updated, but it does not provide a date filter.

The job ID is the reliable handle for tracking a snapshot.


Usage

Endpoint: GET /usage/

Filter: created__gte

Use this endpoint when recording upload consumption for billing or reporting.

Each row represents a usage event and is written once.

You can also filter by:

  • firm
  • source
  • advisor

The advisor filter uses the advisor's username.

Update your local store using the usage row's id.

Important: Refunds

If an upload is later refunded, the original usage row is removed from the list. No new event is created to indicate the refund.

A created__gte delta therefore does not tell you about refunds.

If your integration needs to remove refunded usage, compare IDs during a slower full load.


Household Activity

Endpoint: GET /household_activity/

Filter: action_at__gte

Use Household Activity when you need an event feed for changes involving households or clients, including deletions and deactivations.

Each row represents a single event and does not change after it is created.

action_at represents when the event occurred.

You can also filter by action when you only want specific event types.

Useful sync actions

Action Meaning
create Household created
delete Household deleted
deactivate Household deactivated
reactivate Household reactivated
merge_out Household moved to another firm
details_updated Household details changed
client_created Client added
client_edited Client changed
client_deleted Client removed
tax_return_uploaded Tax return uploaded
irs_transcript_uploaded IRS transcript uploaded

View events such as tax_return_viewed and scenario_analysis_viewed are also included in this feed. Exclude those events from a data sync unless you are building an activity report.

Important: Household Activity tells you that a record changed. It does not contain the updated record fields. Read the corresponding Household or Client endpoint to retrieve the current values.


User Activity Report

Endpoint: GET /user_activity_report/

Filter: date__gte

Dates use the format:

YYYY-MM-DD

Use this endpoint when you want daily totals of advisor activity, including scenarios, tax reports, tax letters, and similar activity.

These rows represent totals for a date rather than individual events.

Important: Always request the current day again

Today's totals can continue increasing throughout the day.

Yesterday's totals are stable.

For this reason, even if your cursor is the last date you processed, request the current day again on every run.

The firm's User Activity Report must be enabled.

You can also filter by:

  • activity
  • advisor_id

Client Upload Links

Endpoint: GET /client_upload_links/

Filter: created_at__gte

Use this endpoint when tracking client upload links that were newly created, refreshed, or completed.

created_at changes when the link is saved. This includes:

  • Issuing the link
  • Refreshing the link
  • The client completing the upload (uploaded becomes true)

You can also filter by:

  • client
  • year

Deleted links are removed from the list. If your integration needs to detect deleted links, compare IDs during a slower full load.


Users, Units, Permission Groups, and Subsidiaries

These endpoints represent firm configuration and do not provide a created or updated cursor.

Load the full list when your integration starts and again when firm configuration changes.

Endpoint What it provides Helpful filter
GET /users/ Advisors who can log in external_key
GET /units/ Units in the firm None
GET /firm_permission_groups/ Permission groups that can be assigned to users None
GET /subsidiaries/ Subsidiaries, including disclaimer text name or firm

Affiliation Firms

Endpoint: GET /affiliation_firms/

Use a full list when you need current:

  • Household counts
  • Plan
  • Renewal date
  • Subscription status

Use created_at__gte only when you need firms that were added during the specified window.

Counts, plan information, renewal dates, and subscription status are calculated at request time. Changes to those values do not update created_at.

This endpoint supports a page size of up to 3000, so a full refresh can usually be completed in relatively few requests.


Handling deleted and removed records

Not every resource reports deletions through the same mechanism.

Households and clients

Use Household Activity to identify deletions, deactivations, and other relevant events.

Other resources

Some resources simply disappear from their list after deletion. If your integration needs to detect those removals, periodically compare the IDs from a full load against the IDs stored in your system.

Usage refunds

Refunded usage rows disappear from the Usage endpoint without generating a new event. Use a periodic full comparison if your integration needs to detect refunds.


Recommended sync schedule for a tax integration

A typical tax-data sync might look like this:

Data you store Call on each incremental run
Households GET /households/?last_updated__gte={cursor}
People GET /clients/?last_updated__gte={cursor}
Removals and deactivations GET /household_activity/?action_at__gte={cursor}
Parsed returns GET /income_tax/?last_updated__gte={cursor}
Scenarios GET /scenario_analyses/?last_updated__gte={cursor}
New PDF uploads GET /tax_returns/?uploaded_at__gte={cursor}, then re-read in-progress IDs
Tax reports whose numbers changed GET /tax_reports/{income_tax_id}/ for IDs from the Income Tax or Scenario Analysis run
Tax reports whose layout changed Full GET /tax_reports/ on a slower schedule
Upload usage GET /usage/?created__gte={cursor}

Common mismatches to avoid

Household last_updated is not a catch-all

Household last_updated covers changes to the household record itself. Client edits, tax uploads, and report layout changes are represented separately.

Tax Return uploaded_at is not processing status

uploaded_at identifies the upload. To determine whether processing has finished, re-read the Tax Return by ID.

Parsed tax values are available through Income Tax after processing succeeds.

Scenario created is not the scenario's last change

created represents the original document upload. Use last_updated to capture later scenario edits.

Income Tax last_updated does not cover report layout

Income Tax timestamps track tax-number changes. Tax Report layout changes are separate and cannot be captured with an Income Tax delta.

For that reason, integrations that need the complete Tax Report should combine incremental Income Tax or Scenario Analysis updates with periodic full Tax Report loads.

In summary

For most resources, use the resource's update timestamp with __gte, paginate until the response is empty, and save your newest timestamp only after the sync completes successfully.

Use specialized workflows for resources that do not support incremental filters, and use Household Activity when you need to detect household or client removals.

The most important principles are:

  1. Use __gte for cursor-based syncs.
  2. Process records idempotently using their IDs.
  3. Paginate through the entire result set.
  4. Do not advance your cursor until the run succeeds.
  5. Use the timestamp returned by Holistiplan, not your server's clock.
  6. Use the appropriate endpoint for deletions and other events.
  7. Use periodic full loads for resources that do not expose a reliable delta.