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_stategoes fromCtoH,trdm_effective_tois set) and inserts a new current version carrying the sametrdm_record_id; a delete writes a tombstone (trdm_is_deleted = true).trdm_skidentifies a version,trdm_record_ididentifies a record across all of its versions.Note: If you are using Databricks or another Spark-based environment, consider using the
SparkSyncclass instead of manually creating downloads. TheSparkSyncclass provides a more convenient way to download data by automatically discovering tables and creating downloads for them. Note:SparkSyncclass uses theConventionSyncclass internally. You can useConventionSyncdirectly with pandas DataFrames if you prefer more control over the download process.
Concepts
| Term | Description |
|---|---|
| Download (Export) | An asynchronous operation that prepares data for download from TitanRDM. |
| Pattern | full exports the complete dataset; incremental exports only rows changed since a given timestamp. |
| High Water Mark | An ISO 8601 timestamp used with incremental exports to fetch only newer data. |
| Include History | False exports current versions only; True exports every version of every record, deleted records included. |
What each combination exports
pattern | include_history | Rows in the file |
|---|---|---|
full | False | Current version of each record, excluding deleted records |
full | True | Every version of every record, first insert through current, deleted records included |
incremental | False | Current version of each record changed since the high water mark, including tombstones so deletes can be applied downstream |
incremental | True | Every 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_markmust 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 ordatetimeis sent as explicit UTC. An unparseable value is rejected client-side withValidationError, and by the server with400 {"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:
| Column | Description |
|---|---|
trdm_sk | Identifies a version |
trdm_record_id | Identifies a record, stable across all of its versions |
trdm_state | C (current) or H (historical) |
trdm_created_at | When this version was written (the change time) |
trdm_effective_from | Start of this version's effective period |
trdm_effective_to | End of the period; 9999-12-31T00:00:00+00 for a current version |
trdm_is_deleted | true for a tombstone |
trdm_actor_type | U (user), A (OAuth application) or I (import) |
trdm_actor_id | Id of that actor |
trdm_tran_code | i 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
| Status | Description |
|---|---|
started | Export is being prepared — this is the status a newly created export has |
completed | Export is complete and available for download |
failed | Export 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
| Property | Type | Description |
|---|---|---|
id | int | Export ID |
status | str | 'started', 'completed', or 'failed' |
is_ready | bool | Whether the file is available for download |
include_history | bool | Whether every version was exported |
branch_id | int | Branch ID |
table_definition_key | int | Table definition key |
pattern | str | Export pattern |
correlation_code | str | User-defined tracking ID |
rows_exported | int | Number of rows exported |
files_exported | int | Number of files generated |
bytes_exported | int | Total bytes exported |
message | str | Status/error message |
duration_secs | int | Export duration in seconds |
started_datetime | str | When the export started |
completed_datetime | str | When the export completed |
Parameters Reference
client.get_download()
| Parameter | Type | Required | Description | |
|---|---|---|---|---|
branch_id | int | Yes | Target branch ID | |
table_definition_key | int | Yes | Table definition key | |
pattern | str | No | 'full' or 'incremental' (default: 'full') | |
correlation_code | str | No | User-defined tracking identifier | |
high_water_mark | str \ | datetime | Conditional | ISO 8601 datetime (required for 'incremental'); sent with a UTC offset |
include_history | bool | No | Export every version of every record, deleted records included (default: False) |
download.wait_until_ready()
| Parameter | Type | Default | Description |
|---|---|---|---|
poll_interval | float | 2.0 | Seconds between status checks |
max_wait | float | 300.0 | Maximum 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