SDKs

Python SDK (generated, typed, sync+async) — TypeScript next

Python

bash
pip install deepsieve

Fully typed, sync + async, generated from the /v1 contract:

python
from deepsieve import SDK

with SDK(bearer_auth="ds_live_...") as sdk:
    me = sdk.v1_meta.v1_meta_get_me().result

    # Free test run — exercises the real poll loop
    run = sdk.v1_research.v1_research_create_run(query="...", dry_run=True).result

    # Discover the schema, then read cited data
    catalog = sdk.v1_data.v1_data_data_catalog().result
    page = sdk.v1_data.v1_data_read_entity_data(
        entity_key=catalog.entities[0].key, receipts=True
    ).result

Change history

From deepsieve 0.3.4, the SDK carries the two lineage reads. One is the log of a column across records; each entry carries a persistent cell_id, which the other takes to return every value that one cell has ever held:

python
    bp = sdk.v1_blueprints.v1_blueprints_list_blueprints().result.data[0]
    log = sdk.v1_lineage.v1_lineage_read_column_changes(
        blueprint_id=bp.id, entity_key="companies", column="headcount"
    ).result
    first = log.data[0]            # first.change_kind: initial | changed | unchanged | amended | observed | unknown
    # amended: QA corrected the value in place; first.pre_amend_kind / first.pre_amend_value say what it replaced
    history = sdk.v1_lineage.v1_lineage_read_cell_changes(
        cell_id=first.cell_id, include="rechecks"
    ).result

Both are keyset-paged newest first; keep next_cursor to resume, and read truncated before treating a page as complete on a free-preview account.

v1_changes.v1_changes_list_changes returns them newest first, and v1_changes.v1_changes_undo_change undoes one (data:write, member role). Pass idempotency_key so a retry is safe:

python
    page = sdk.v1_changes.v1_changes_list_changes(live=True).result
    change = page.data[0]          # change.kind: merge | renamed | succeeded_by
    if change.undoable:
        sdk.v1_changes.v1_changes_undo_change(
            change_id=change.id, idempotency_key=f"undo-{change.id}"
        )

Cells a person set on purpose

v1_holds.v1_holds_list_holds(pending=True) lists the cells research will not overwrite, each with the pending_finding it found since. v1_holds.v1_holds_apply_cell_finding takes one (pass the finding_evidence_id your human saw; a newer one since is refused finding_changed), v1_holds.v1_holds_keep_cell_value keeps your human's value and dismisses it (same argument; research does not ask again unless it cites a new page), and v1_holds.v1_holds_release_cell stops protecting the cell. All three need data:write and the member role:

python
    page = sdk.v1_holds.v1_holds_list_holds(pending=True).result
    for cell in page.data:
        print(cell.column, cell.value, "->", cell.pending_finding.value)

Marking rows as duplicates

v1_merges.v1_merges_preview_merge writes nothing; v1_merges.v1_merges_create_merge takes the same body plus the preview's preview_digest, so it applies exactly the merge you previewed. If anything changed since, the merge is refused merge_changed with the merge as it stands now (and its own digest) in the error's details, and nothing is written. To merge without a preview, pass apply_without_preview=True; passing neither is preview_required. Settle every tie in choices (the preview lists unresolved_ties), or the merge is refused ties_unresolved. Rows under different parents (an entity unique per parent) are refused merge_across_parents: merge only within one parent. Two rows' lists of links are combined, never a tie: the preview says how under links_combined.

After a run starts (0.5.0)

These need deepsieve 0.5.0 or later:

  • v1_research.v1_research_retry_run retries a failed, stopped or empty run. A retry that would charge is refused retry_confirmation_required with the quote in details; ask your human, then call again with expected_total_cents set to that total.
  • v1_research.v1_research_get_reruns and v1_research_approve_reruns read and approve a report's waiting re-research, against the quote_id and total you were shown. v1_research_set_reruns_auto_approve turns automatic approval on (with a monthly cap) or off; on is standing authority to spend, so ask first.
  • v1_research.v1_research_set_monitoring turns a report's monitoring on or off, and v1_research_list_monitoring shows what monitoring costs across a Blueprint.
  • v1_links.v1_links_remove_link removes a wrong link; research will not add it back.

Runs has the rules each one follows.

Domain knowledge and conflicting values (0.8.0)

These need deepsieve 0.8.0 or later:

  • v1_knowledge lists, adds, edits, retires, restores and removes a Blueprint's domain knowledge, and v1_knowledge_knowledge_usage shows how each entry was used across recent runs. Writes need the knowledge:write scope.
  • v1_blueprints has the same calls for a Blueprint still being created (v1_blueprints_list_draft_knowledge and its siblings), and a follow-up answer can carry skip for the domain_knowledge question. Ask your human before you skip it.
  • v1_research.v1_research_get_run_knowledge shows what knowledge a run carried and what it decided.
  • v1_holds.v1_holds_list_conflicts lists the cells a run stated more than one way, and v1_holds_choose_cell_value shows the value your human chose.

Changelog has the details of each.

Merged lists, closed accounts and search counts (0.9.0)

These need deepsieve 0.9.0 or later:

  • A list cell a run stated more than one way can read selection.state merged, with selection.items saying which version stated each item and its sources. Each entry from v1_lineage.v1_lineage_read_cell_changes and v1_lineage_read_column_changes carries its own selection.
  • v1_meta.v1_meta_get_me's subscription carries expired_from_plan, expired_from_plan_label and retention_until, so a closed or expired account no longer reads as a new one. Check expired_from_plan first.
  • A run's metrics.search_queries is typed as nullable: None means not reported, never zero searches.

Changelog has the details of each.

Name the Blueprint

v1_research_create_run, v1_research_estimate_run and v1_webhooks_create_webhook need blueprint_id (an id from v1_blueprints_list_blueprints). The signature marks it optional only so that a dry_run=True call can leave it out; any other call without it is refused by the API with 400 invalid_request and param: "blueprint_id", before anything is charged.

Enforced sources

v1_research_create_run and v1_research_estimate_run take allowed_sources, a list of hosts, wildcard suffixes, URL prefixes or blueprint:primary. The run refuses to write any cell it cannot cite from that list, and the run resource echoes the resolved list it enforced.

Every call returns .result (typed models) plus .headers (RateLimit-*, X-Request-Id). Errors raise typed exceptions carrying the same {code, retriable, request_id} envelope the REST API returns.

The SDK is generated from /openapi.json's /v1 subset and regenerated whenever the contract changes, and CI fails a PR that leaves the two out of step. That keeps the SDK matching the CONTRACT; where the contract and the running API disagree, the API is the truth and the gap is our bug.

TypeScript

Next up, generated from the same source. Until then the REST API is deliberately easy to consume raw — every list is {data, has_more, next_cursor}, every error is typed — and your coding assistant can build a thin client from the Quickstart prompt block in one shot.

SDKs — DeepSieve API