Changelog
API changes, newest first
2026-09-16 — the cost estimate now accounts for the free run
affordable changes value for trial accounts. If you branch on it, read this.
POST /v1/research/runs/estimate priced a run against plan credits and prepaid
dollars only. An unsubscribed org with its one free run unspent was therefore
quoted affordable: false with a shortfall_cents — and then
POST /v1/research/runs succeeded and charged nothing, because the free run
funds it. An autonomous caller obeying the estimate halted before reaching a run
it could in fact have made.
| Was | Now |
|---|---|
affordable: false, shortfall_cents: 1000 | affordable: true, shortfall_cents: 0 |
New on the estimate
uses_free_run — whether THIS request would consume the org's one free run.
run_price_cents still quotes the dollar price, exactly as it does for a
credit-covered run, so never infer funding from the price: uses_free_run is the
discriminator. null means eligibility could not be determined (billing inactive,
or the read failed) and never "not free" — when it is null, affordable and
shortfall_cents are null too, which is unknown, not unaffordable.
It is false for depth: max. The free run covers a Standard run; a Max
request is refused before the free run is ever considered, and allowed carries
that refusal.
free_run — present only when the free run's entity cap would trim THIS request.
A free run researches at most one interaction's worth of entities and drops the
rest rather than refusing, so the block names entity_cap, requested,
researched and dropped. The run response returns the same block and is
authoritative: dropped here is computed from the seeds as sent, while the run
narrows first (reuse_existing, additive_to).
New on GET /v1/me
free_run_available — is the org's one free run still unspent, so you can check
trial eligibility before committing to anything. Same nullability rule: null is
"could not determine", never "no free run".
2026-09-08 — breaking, before GA
Proposing a Blueprint change now names the Blueprint.
| Was | Now |
|---|---|
POST /v1/blueprints/propose | POST /v1/blueprints/{blueprint_id}/propose |
If you change nothing, this call returns 404. No alias is kept.
Get a blueprint_id from GET /v1/blueprints. The old path proposed
against whichever Blueprint happened to be active — a choice you never made
and the response never named. With two Blueprints in one account, it could
return a completely coherent proposal about the wrong one.
This is not only a path change. The id now reaches everything the answer depends
on: the Blueprint the proposal is built from, the Blueprint the tier is
classified against, and the live tables checked to decide whether a change would
lose data. A path that took an id and ignored it would have been worse than the
old one, because the id would have implied a control it did not have.
Also in the response
blueprint_id — the Blueprint this proposal is about, echoed in the form
GET /v1/blueprints uses. It is always present.
Name that Blueprint when you hand the change to a person. The
{app_url}/blueprint?ask=… link opens their active Blueprint, which is not
necessarily the one you addressed, and their chat re-derives the proposal from
your text alone. If those differ, the re-derivation succeeds against the wrong
Blueprint — no error. There is no flag to check instead: your active workspace
comes from your API key, theirs from their own switcher, and nothing on this API
reports the second.
Refusals
Addressing a Blueprint explicitly does not widen what you can reach. An id you
could not already read refuses exactly as the other by-id reads do: 404 for
unknown, malformed, or another organization's id (existence is never confirmed
across organizations), 403 for a scoped viewer without a grant, and 409 for
a Blueprint that is still a draft or has no tables yet. A refusal costs no model
call.
Applying is unchanged and still human-only. An agent may ask what a change would be; only a person may make it.
Clients
Upgrade to at least:
| Client | Version |
|---|---|
Python SDK (deepsieve) | 0.3.0 |
| MCP server — the stdio package and browser login alike | 0.3.0 |
The MCP tool propose_blueprint_change takes an optional blueprint_id; omit
it and it resolves the active Blueprint as before. The CLI does not call this
endpoint and is unaffected.
2026-09-07 — breaking, before GA
A dataset read now names the Blueprint it reads. Two endpoints are removed
and six are moved. This is a breaking change before GA, announced here as
Versioning & stability requires: /v1 becomes
additive-only at GA, and until then a break is allowed only when it is
deliberate and appears in this changelog.
If you change nothing, the calls below return 404. That is an ordinary
not-found, so a client that only checks for an error will look like it is
reading an empty dataset rather than calling a route that no longer exists.
Check the path before you check the data.
Removed
| Was | Now |
|---|---|
GET /v1/data | GET /v1/blueprints/{blueprint_id} |
GET /v1/data/{entity_key} | GET /v1/blueprints/{blueprint_id}/entities/{entity_key} |
Same response shape, same query parameters (limit, cursor, updated_since,
fields, receipts), same free-preview cap. One field differs: the catalog's
discriminator is now "object": "blueprint" rather than "object": "data_catalog".
Get a blueprint_id from GET /v1/blueprints, which lists the Blueprints
you can see and marks the active one. That listing is the reason for the change.
GET /v1/data answered for whichever Blueprint happened to be active — a
choice the caller never made and the response never named — so an account with
two Blueprints could receive a completely coherent answer to a question it had
not asked, with nothing in the payload to reveal which one it described. No
alias is kept: a shim with no consumer reads as a supported path.
GET /v1/export is unchanged, and still takes ?workspace_id=.
Moved
/v1/blueprints/onboarding/… → /v1/blueprints/drafts/… — all six: the
collection itself, {session_id}, and its /answers, /adjust, /chat and
/approve. Path only. Same handlers, same request and response bodies, and the
same operationIds, so generated SDK method names do not change.
Clients
Upgrade to at least:
| Client | Version |
|---|---|
Python SDK (deepsieve) | 0.2.0 |
CLI (deepsieve) | 0.2.0 |
| MCP server — the stdio package and browser login alike | 0.2.0 |
deepsieve data catalog and deepsieve data get resolve the active Blueprint
for you, and take --blueprint <id> to name one instead. The MCP tools
get_blueprint and query_entities take an optional blueprint_id, and a new
list_blueprints tool supplies the ids — on both transports.
2026-08 (later)
-
ApiKeygainsusageonGET /v1/keys—{runs, est_spent_cents}, ornullif the aggregation could not be completed. The API has returned it since the per-key usage work; the contract never declared it, so generated clients could not read it. Additive — existing fields unchanged. Scope worth knowing: it counts runs in your ACTIVE WORKSPACE. Keys are workspace-bound at mint time and the list is org-wide, so a key pinned to a different workspace reports zero rather than its real totals. -
CreateRunRequest.reuse_existingwas declaredstring; it is aboolean. A pasted property body had silently overwritten it in the contract, so SDK 0.1.1 types itOptional[str]andreuse_existing=Truedoes not type-check. Corrected — the next SDK release carries the right type. Two other properties had the same defect without reaching the SDK:MonitoringView.freshness(object, was resolving tonumber) andStartRunResponse.reused_existing(carried the wrong description). -
Freshness overage wording: the per-day rate and
auto_topup_suggesteddescribed a report; both are metered per monitored run — one per Deep Research dossier, so a report assembled from several dossiers accrues several. Wording only; no behaviour or price changed. -
CLI sign-in —
POST /v1/auth/device/codeand/v1/auth/device/token: the OAuth device flow behinddeepsieve login. Unauthenticated by design; uses RFC 8628's own vocabulary (authorization_pending,slow_down,expired_token,access_denied), and the two retriable codes are the normal path, not failures. -
New error codes: the four above.
invalid_api_keyis now returned for a credential that was presented but not accepted — previously indistinguishable fromauthentication_required, which means no credential was sent at all. -
Citationgainsverdict,verdict_noteandcited. The API already returned them; the contract did not declare them, so generated clients could not read a verdict. Additive — existing fields unchanged. SDK 0.1.1 carries them. -
MCP over browser login reaches tool parity with the stdio package (19 tools). No tool was removed.
2026-08
- Initial
/v1release: typed error envelope + request ids; scoped revocable API keys (/v1/keys); async research runs withIdempotency-Keyand query-first creation; outbound signed webhooks (run.*,dataset.updated); cursor-paginated dataset read (/v1/data) withupdated_sincesync and per-cell citations; NDJSON/JSON/CSV export; RateLimit headers + per-key budgets; MCP server;/llms.txt.