> For the complete documentation index, see llms.txt.
Skip to main content

Check out Port for yourself ➜ 

Implement incremental sync

Incremental sync runs on a short interval in parallel to the full resync. It fetches only objects created or updated since the last run for the kinds you opt in.

Your job is to register @ocean.on_incremental_resync handlers that use the active cursor when calling the third-party API, and yield batches of raw objects (same shape as a full resync). Port applies your mapping to those objects. Incremental sync does not run reconciliation - deletions still rely on a full resync.

Register an incremental handler​

Use @ocean.on_incremental_resync for each kind that can sync incrementally. You can stack it with @ocean.on_resync when the same function supports both modes:

from port_ocean.context.ocean import ocean
from port_ocean.core.incremental.cursor_context import active_incremental_cursor
from port_ocean.core.ocean_types import ASYNC_GENERATOR_RESYNC_TYPE

from client import MyClient
from kinds import ObjectKind


@ocean.on_incremental_resync(ObjectKind.ISSUE)
@ocean.on_resync(ObjectKind.ISSUE)
async def resync_issues(kind: str) -> ASYNC_GENERATOR_RESYNC_TYPE:
client = MyClient()
cursor = active_incremental_cursor()
async for batch in client.get_paginated_issues(updated_since=cursor):
yield batch

active_incremental_cursor() returns a datetime during an incremental run, and None during a full resync. Pass that into your API client as a filter when it is set.

Only kinds with an @ocean.on_incremental_resync handler participate in incremental sync. Other kinds in the mapping are skipped on incremental runs and continue to update via full resync (and live events, if you implemented them).

Strategies​

Ocean provides helpers in port_ocean.core.incremental.strategies to turn a cursor into API parameters.

Server-side filter​

Use when the third-party API accepts a "changed since" query parameter:

from port_ocean.core.incremental.strategies import (
ServerSideTimestampStrategy,
paginate_with_strategy,
)

ISSUE_INCREMENTAL = ServerSideTimestampStrategy(param_key="since")


@ocean.on_incremental_resync(ObjectKind.ISSUE)
async def incremental_resync_issues(kind: str) -> ASYNC_GENERATOR_RESYNC_TYPE:
client = MyClient()
cursor = active_incremental_cursor()
params = ISSUE_INCREMENTAL.merge_params({"state": "all"}, cursor)
async for batch in paginate_with_strategy(
client.get_paginated_issues(params),
cursor=cursor,
strategy=ISSUE_INCREMENTAL,
):
yield batch

Client-side cutoff​

Use when the API has no time filter but can return items newest-first. Sort descending, yield items newer than the cursor, and stop paging when you hit older items:

from port_ocean.core.incremental.strategies import (
ClientSideCutoffStrategy,
paginate_with_strategy,
)

PR_INCREMENTAL = ClientSideCutoffStrategy(
stop_field="updated_at",
query_params={"sort": "updated", "direction": "desc"},
)


@ocean.on_incremental_resync(ObjectKind.PULL_REQUEST)
async def incremental_resync_pull_requests(
kind: str,
) -> ASYNC_GENERATOR_RESYNC_TYPE:
client = MyClient()
cursor = active_incremental_cursor()
params = PR_INCREMENTAL.merge_params({}, cursor)
async for batch in paginate_with_strategy(
client.get_paginated_pull_requests(params),
cursor=cursor,
strategy=PR_INCREMENTAL,
):
yield batch

Enable incremental sync​

For self-hosted and private deployments, set:

OCEAN__INTEGRATION__INCREMENTAL_SYNC_ENABLED=true
OCEAN__INTEGRATION__INCREMENTAL_SYNC_INTERVAL=15

INCREMENTAL_SYNC_INTERVAL must be 15, 30, or 60 (minutes). The default interval is 15.

For Port-hosted integrations, enable incremental sync and set the interval under Advanced configuration on the connectors page.

With a ONCE event listener, an enabled incremental sync runs an incremental pass instead of a full resync.

Limitations to design for​

  • No reconciliation on incremental runs - design handlers to fetch creates and updates only.
  • The first incremental run looks back one interval - rely on a preceding full resync for older data.
  • Keep incremental raw shapes consistent with full resync shapes so the same mapping works for both.

See incremental sync for platform behavior and sync interactions for how incremental runs overlap with full resyncs and live events.