Downloading Data

Export and download data from TitanRDM as pandas DataFrames. The SDK supports full and incremental export patterns, with or without version history, and has built-in polling for export completion.

TitanRDM keeps every version of every record. A change closes the current version (trdm_state goes from C to H, trdm_effective_to is set) and inserts a new current version carrying the same trdm_record_id; a delete writes a tombstone (trdm_is_deleted = true). trdm_sk identifies a version, trdm_record_id identifies a record across all of its versions.

Note: If you are using Databricks or another Spark-based environment, consider using the SparkSync class instead of manually creating downloads. The SparkSync class provides a more convenient way to download data by automatically discovering tables and creating downloads for them. Note: SparkSync class uses the ConventionSync class internally. You can use ConventionSync directly with pandas DataFrames if you prefer more control over the download process.


Concepts

TermDescription
Download (Export)An asynchronous operation that prepares data for download from TitanRDM.
Patternfull exports the complete dataset; incremental exports only rows changed since a given timestamp.
High Water MarkAn ISO 8601 timestamp used with incremental exports to fetch only newer data.
Include HistoryFalse exports current versions only; True exports every version of every record, deleted records included.

What each combination exports

patterninclude_historyRows in the file
fullFalseCurrent version of each record, excluding deleted records
fullTrueEvery version of every record, first insert through current, deleted records included
incrementalFalseCurrent version of each record changed since the high water mark, including tombstones so deletes can be applied downstream
incrementalTrueEvery version written since the high water mark — a record changed twice contributes two rows

Rows are ordered by trdm_record_id, then by trdm_effective_from oldest → newest.


Full Download Workflow

from titan_rdm_sdk import TitanRDMClient

client = TitanRDMClient(
    url="https://your-tenant.titanrdm.com",
    client_id="your_client_id",
    client_secret="your_client_secret",
)

# Step 1: Create an export
download = client.get_download(
    branch_id=174,
    table_definition_key=50,
    pattern="full",
    correlation_code="daily-export-2025-01-22",
)
print(f"Export created: ID={download.id}, Status={download.status}")

# Step 2: Wait for the export to complete
final_status = download.wait_until_ready(poll_interval=2.0, max_wait=300.0)
print(f"Export ready: status={final_status}")

# Step 3: Download the data
df = download.receive()
print(f"Downloaded {len(df)} rows, {len(df.columns)} columns")
print(f"Duration: {download.duration_secs} seconds")

Incremental Download

Use pattern="incremental" with a high_water_mark to export only rows modified after a given timestamp:

download = client.get_download(
    branch_id=174,
    table_definition_key=50,
    pattern="incremental",
    high_water_mark="2025-01-21T00:00:00Z",
    correlation_code="incremental-sync",
)

download.wait_until_ready()
df = download.receive()
print(f"Downloaded {len(df)} changed rows since 2025-01-21")

The high_water_mark must be an ISO 8601 datetime. Include a UTC offset (e.g. 2026-09-09T19:00:00+10:00); the server treats a value with no offset as UTC, and the SDK always sends an offset-aware string, so a naive value or datetime is sent as explicit UTC. An unparseable value is rejected client-side with ValidationError, and by the server with 400 {"error": "high_water_mark must be an ISO 8601 datetime"}.

A datetime can be passed directly:

from datetime import datetime, timedelta, timezone

download = client.get_download(
    branch_id=174,
    table_definition_key=50,
    pattern="incremental",
    high_water_mark=datetime.now(timezone.utc) - timedelta(days=1),
)

Exporting Version History

Set include_history=True to receive every version of every record rather than only the current one:

# Every version of every record, deleted records included
download = client.get_download(
    branch_id=174,
    table_definition_key=50,
    pattern="full",
    include_history=True,
)
download.wait_until_ready()
history = download.receive()

# The audit trail of one record
record = history[history["trdm_record_id"] == 5].sort_values("trdm_effective_from")
print(record"name", "trdm_state", "trdm_effective_from", "trdm_effective_to",
              "trdm_tran_code", "trdm_actor_type", "trdm_actor_id")

Combined with pattern="incremental", it returns every version written since the high water mark — a record changed twice in that window contributes two rows.


Exported CSV Shape

The downloaded frame carries the user-defined columns first, in table-definition order, then these system columns in this fixed order:

ColumnDescription
trdm_skIdentifies a version
trdm_record_idIdentifies a record, stable across all of its versions
trdm_stateC (current) or H (historical)
trdm_created_atWhen this version was written (the change time)
trdm_effective_fromStart of this version's effective period
trdm_effective_toEnd of the period; 9999-12-31T00:00:00+00 for a current version
trdm_is_deletedtrue for a tombstone
trdm_actor_typeU (user), A (OAuth application) or I (import)
trdm_actor_idId of that actor
trdm_tran_codei insert, u update, d delete — populated for every incremental export and for any export with include_history=True; empty for a plain full export
from titan_rdm_sdk import SYSTEM_COLUMNS, EXPORT_SYSTEM_COLUMNS, TRAN_CODE_COLUMN, HIGH_DATE

Breaking change: trdm_updated_at and trdm_updated_by are no longer exported. Use trdm_created_at (the version's change time) and trdm_actor_type / trdm_actor_id instead.

Boolean columns — the table's own and trdm_is_deleted — are written as true/false (previously PostgreSQL t/f). receive() coerces trdm_is_deleted to a Python bool and accepts both spellings; pass your own boolean columns to have them coerced too:

df = download.receive(bool_columns=["is_active"])

Polling Behaviour

Exports are asynchronous — the server prepares the data in the background. The wait_until_ready() method polls the export status at regular intervals:

download.wait_until_ready(
    poll_interval=2.0,   # Check every 2 seconds
    max_wait=300.0,      # Time out after 5 minutes
)

Manual Status Checking

For more control, poll the status yourself:

import time

download = client.get_download(
    branch_id=174,
    table_definition_key=50,
    pattern="full",
)

while True:
    status = download.check_status()
    print(f"Status: {status}")

    if status == "completed":
        df = download.receive()
        break
    elif status == "failed":
        print(f"Export failed: {download.message}")
        break

    time.sleep(2)

Export Statuses

StatusDescription
startedExport is being prepared — this is the status a newly created export has
completedExport is complete and available for download
failedExport failed (check download.message for details)

download.is_ready is True once the file is available. (The legacy ready status is still recognised.)


Download Object Properties

PropertyTypeDescription
idintExport ID
statusstr'started', 'completed', or 'failed'
is_readyboolWhether the file is available for download
include_historyboolWhether every version was exported
branch_idintBranch ID
table_definition_keyintTable definition key
patternstrExport pattern
correlation_codestrUser-defined tracking ID
rows_exportedintNumber of rows exported
files_exportedintNumber of files generated
bytes_exportedintTotal bytes exported
messagestrStatus/error message
duration_secsintExport duration in seconds
started_datetimestrWhen the export started
completed_datetimestrWhen the export completed

Parameters Reference

client.get_download()

ParameterTypeRequiredDescription
branch_idintYesTarget branch ID
table_definition_keyintYesTable definition key
patternstrNo'full' or 'incremental' (default: 'full')
correlation_codestrNoUser-defined tracking identifier
high_water_markstr \datetimeConditionalISO 8601 datetime (required for 'incremental'); sent with a UTC offset
include_historyboolNoExport every version of every record, deleted records included (default: False)

download.wait_until_ready()

ParameterTypeDefaultDescription
poll_intervalfloat2.0Seconds between status checks
max_waitfloat300.0Maximum seconds to wait

Next Steps

  • Uploading Data — Send data to TitanRDM, including the export → import round trip
  • Convention Sync — Automate downloads for entire domains
  • Spark Sync — Write downloaded data directly to Spark tables