Get a run
const url = 'https://api.hyperresearch.ai/v1/runs/example';const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request GET \ --url https://api.hyperresearch.ai/v1/runs/example \ --header 'Authorization: Bearer <token>'One run: live progress, counters, failure reason, and what it cost. price_cents, billed, included, included_in_trial, trial_tier and charged_cents carry the same values under the same names as the GET /v1/runs row for this run, so a detail screen never has to fetch the list to say “Included in trial” or print a price (E2E5-11).
Authorizations
Section titled “Authorizations”Parameters
Section titled “ Parameters ”Path Parameters
Section titled “Path Parameters”Responses
Section titled “ Responses ”Run
object
The project this run is filed under, or null when it is unfiled.
The verbatim research question this run was launched with, not truncated, with its line breaks kept (LF line endings; control characters, trailing spaces and runs of more than two blank lines removed). Null for legacy runs whose question cannot be recovered.
The title a person chose for this run at launch (the console’s Sharpen box). Null when none was chosen. Never a model-written title: see fallback_title.
A short title written by a model at launch for a run with no chosen title, or null. The report keeps its own heading when that heading reads as a title; this names the run only where it does not.
object
Position of chapter in this run’s chapter plan, 1-based. Null on a flat run and while the chapter is not yet registered. With chapters_total this is what lets a surface say “Chapter 2 of 4” rather than printing an internal chapter id.
How many chapters this run planned. Null on a flat run (Light).
The unit inside this step that is running now, in customer words: ‘Chapter 2 of 4 · Depth investigation’, ‘Writing section 3 of 9’. Present only where a step is more than one act — a chaptered run’s steps 2–10 and the sectioned synthesis — and null otherwise, because on a flat run the step name is the whole truth. Never an internal unit id (Wave C item 1).
object
Sources this run KEPT — fetched successfully or served from the vault — which is exactly how many rows its Sources panel has. The single source count: spend.sources_fetched, the counters stream event and the list summary report the same number, and a reload does not change it. A failed fetch attempt is not a source (E2E4-02).
Notes actually written to the vault; a source served from the vault writes none.
Candidates this run CONSIDERED — every URL put to the fetch ladder, including the ones it could not read and the ones the budget refused. Always >= sources_fetched. Null while unknown (E2E4-08).
Physical fetch attempts across all rungs — requests that actually went out. A vault lookup is not one, so a run that served every source from the vault reports 0 here against a positive candidates_seen (E2E7-02). Null while unknown.
DEPRECATED spelling of candidates_seen, emitted in step with it so a console built against the old name never disagrees with one built against the new. Read candidates_seen.
The run’s counters as every surface prints them, identical in shape and value to the counters object on the GET /v1/runs row for this run. sources_fetched is null when the run predates the counter unification and its kept-source count cannot be verified — an attempt count is not a source count, and unknown is the honest answer.
object
Why a terminal run ended badly: the sanitised, single-line reason from the run log for a failed or aborted run, the gate for a blocked run, null otherwise. An aborted run reads “Stopped by you.” for the person who requested the abort and “Stopped by a workspace member.” for everyone else; the list row and this field always agree.
The user id that asked for the abort, or null for a run nobody stopped, a run stopped by an unattributed API key, or one stopped before this field existed. Published so a client can say “Stopped by you.” on its own terms rather than inferring it from the sentence; failure_reason is already narrowed for the caller.
The raw platform/vendor text behind failure_reason. Present ONLY for a signed-in owner or admin of the workspace, and only when there is one — an API key never receives it. Support material, never customer copy.
Identifies that terminal event in the run log; quote it to support.
One customer-safe sentence saying what a blocked run is waiting for and what to do about it; null on every other status. Branch on blocked_code, not on this string.
The machine gate id behind blocked_on — step:<id>:<check>, verify:<check>, fetch_budget, model_budget or provider:<name>. Stable across copy changes.
Why a blocked run is waiting on US rather than on a person: it is clearing the condition by itself and will carry on with no action from the caller, and nothing has been charged. Present only while a run is waiting; absent or null on every other run, including a block that does need the customer to act. Branch on its PRESENCE, not on the class.
Present, non-null, while a running/blocked run is being RECOVERED: our infrastructure interrupted it and we are resuming it from where it stopped, on the same run id, at no extra charge (Deep automatic recovery). Absent or null on every other run. While set, wait_reason is null and blocked_on is not a gate: the run needs nothing from anyone. Branch on its PRESENCE.
object
Which automatic recovery this is (1, 2, 3), or null while a stalled run is being handed over and the attempt is not yet claimed.
Wall time from start (or creation) to finish; null while the run is unfinished. The SAME number, from the same computation, that the list row serves as duration_ms — a client must not subtract its own timestamps, which is how one run read “15m 57s” here and “16m 0s” on the list (E2E7-11).
object
object
How many chapters this run’s plan has — the same field, the same name and the same value as the GET /v1/runs row, so a detail page can say “Deep · 4 chapters” without fetching the list. A property of the RUN, not of the step that is moving: step.chapters_total can only answer while a chapter-scoped step is running, which a finished Deep run has none of. Null means not chaptered (or settled before the column existed) — never zero.
This run’s frozen plan: the steps it was going to run, in plan order, each with the completion state a rerun’s prerequisite check applies, and the step it stopped on. Null for a run with no plan yet (queued, clarifying). Read it before offering POST /v1/runs/{id}/rerun-from: a tier runs a SUBSET of the pipeline, so a resume point guessed from the step numbering is refused — a console that defaulted to 8 sent it to a run whose plan does not include step 8 (fault 98).
object
The steps this run planned, in plan order, deduplicated — a chaptered plan runs steps 2–10 once per chapter and the manifest keys each one once.
object
The pipeline’s own step id — 1, 14.5. Never an ordinal.
The step’s human name, the same one step.name carries.
This step’s status on the run manifest (done, skipped, running, failed, pending), or null for a step the run never reached.
The run is PAST this step: it ran, or it was skipped. A resume point whose predecessors are all completed cannot be refused for an unfinished prerequisite.
The step the run stopped on: the one whose status is failed, else the one still running/blocked when the run ended. Null for a healthy run. This is the resume point to default to — the run failed there and everything before it is done, so it is the one step the API is certain to accept.
object
The levers.reader_expertise this run was started with (beginner, intermediate or expert), read from its frozen launch packet. Null when the run did not set one, in which case the report is written for an informed non-specialist.
object
Deprecated. Still accepted and stored on the run, but the research planner sets its own depth for each question and no longer reads this value. Omit it.
Deprecated and ignored. Still accepted so existing callers keep working, but no longer stored on the run: every report uses the same citation markers whatever this says. Omit it.
What the report is for. decide opens with the answer and what it means for the decision, sets out the trade-offs and closes with a recommendation; understand explains the subject and its mechanisms without pushing a recommendation; brief_someone makes the opening summary a stand-alone brief you can forward. It changes how the report is organised and summarised, never what it researches, claims or cites, and it does not change the price, the sources or the run time. Omitted, the report is organised as before.
The language the report is written in, as a language tag (en, zh, ja, ko, fr, …) or a bilingual pair (zh+en). Omitted, the report follows the language the question asks for, or the language it is written in. Headings and the labels the pipeline adds are localised for English, Chinese, Japanese and Korean, and are in English for other languages.
Who the report is written for. beginner defines technical terms and acronyms in plain words and explains the background a newcomer needs; intermediate uses the field’s standard terms and defines only specialist ones; expert uses the field’s terminology without definitions and spends the words on specifics. It changes how the report explains things, never what it researches, claims or cites, and it does not change the price, the sources or the run time. Omitted, the report is written for an informed non-specialist, as before.
List price of the run at admission, in cents. Null for an auto run not yet classified.
Whether this run has been settled on the usage ledger.
Server truth for “Included in trial”: an allowance was claimed at admission. Not a guess from a zero price — a fixture run is also $0 and is not included.
Same value as included; both spellings are published so no client has to pick.
Which included-run allowance was claimed, or null when the run was billable.
True when an operator grant comped this run (migration 0041): it ran for real, at its real price_cents, and its ledger row settles without reaching Stripe. Frozen on the run at admission, so it is unaffected by the grant being revoked or expiring later. Never inferred from a zero amount — a comped run’s ledger row carries the full price.
What the usage ledger actually metered for this run, refunds included. Null until a ledger row exists, which is NOT the same as zero. Exactly the field and the value GET /v1/runs publishes for this run.
Word count of the finished report; null while there is no report. The SAME field, name and value the GET /v1/runs row and the report cards publish — the run detail was the one screen that showed the report and would not say how long it was (E2E8-06).
The newest run in this workspace asking the same question, or null when this run IS that run. Label a run “Earlier version” if and only if this is set. Grouped by normalised question text, or by an explicit rerun parent — never by project, which is a body of sources and says nothing about which report replaces which (E2E8-10).
Stable key for the version group, so a client can bucket rows in one pass.
True when this run was a DRY RUN: the orchestration was real but the model provider and the fetch/search vendors were mocked, so the report is synthetic and the price is $0. Server truth (the run’s dry_run channel), never inferred from a zero price — a trial run and a fixture run are also $0 and are not dry. Always false in production, where dry runs cannot be created.
What this run’s verification receipt came to, in four counts — the same object and the same values the GET /v1/runs row publishes (E2E10-07). Null means no receipt is known for this run; null is NOT a pass.
object
Checks that ran and met their bar. A warning is NOT a pass (E2E8-01).
Checks that ran and recorded a finding below our own target. Informational; see attention for what a reader should act on.
Checks that ran and did not meet their bar.
Checks with nothing to judge (“not applicable”). Counted apart from the three above.
Findings a reader should act on before relying on the report: cited statements that say more than their sources support (above 3 and 3% of those checked), a chapter without its full research pass, a retracted source, or final checks that did not run. Absent on a summary cached before this field existed.
The clarification interview, when this run had one: what was asked before anything was reserved, what the customer answered, the assumptions the run is proceeding on, and — while the status is clarifying — when its time-box runs out (expires_at) and what happens then (on_timeout). Null on a run whose question was specific enough to skip the interview.
object
object
The named dimension this question is about (scope, time_window, geography, …). Answer by axis, not by position.
What the run will do if this question is not answered. Shown behind “You decide” and in the skip-all confirmation, and printed in the report’s Assumptions paragraph.
The span of the customer’s own question that prompted this one, when it could be quoted verbatim.
object
The literal you_decide when the customer left this to us; the assumption then records the decision.
Every default this run is proceeding on — verbatim the sentences its report prints.
When an unanswered interview times out; what happens then is on_timeout. Null once the interview is settled.
Present while status is asking: what happens at expires_at if nobody answers. start admits the run on its stated assumptions (or closes it without charge if it cannot be admitted then — spend cap, no payment method); close ends it without starting and without charge, which is what an interview that found nothing to research does.
A sentence about the interview rather than about the question: that the ambiguity assessment could not be made (resolution: "unassessed"), that nothing has started while we ask what to research, or that the time-box closed the run without charge.
Example
{ "status": "clarifying", "wait_reason": "provider_billing", "reader_expertise": "beginner", "levers": { "register": "teach", "inference_depth": "surface", "citation_style": "numbered", "purpose": "decide", "report_language": "zh", "reader_expertise": "beginner" }, "clarification": { "status": "asking", "on_timeout": "start", "resolution": "answered" }}Headers
Section titled “Headers”Example
"reads";q=60;w=60The quota policy this request was counted against (draft-ietf-httpapi-ratelimit-headers): "<bucket>";q=<requests>;w=<window seconds>, e.g. "reads";q=60;w=60. Buckets: reads 60/min, status 600/min, creates 10/min, writes 20/min per credential; public 300/min per network on keyless routes.
Example
"reads";r=57;t=41Remaining quota in the current window: "<bucket>";r=<remaining>;t=<seconds until reset>, e.g. "reads";r=57;t=41.
Requests allowed per window in this bucket (legacy form of RateLimit-Policy).
Requests left in this window (legacy form of RateLimit).
Unix time (seconds) when the window resets.
Which bucket counted the request: reads, status, creates, writes or public.
Invalid request
object
object
Example
{ "error": { "type": "invalid_request", "code": "missing_api_key", "request_id": "req_01J9ZQ7V6H0000000000000000" }}Authentication failed
object
object
Example
{ "error": { "type": "invalid_request", "code": "missing_api_key", "request_id": "req_01J9ZQ7V6H0000000000000000" }}Insufficient scope or plan
object
object
Example
{ "error": { "type": "invalid_request", "code": "missing_api_key", "request_id": "req_01J9ZQ7V6H0000000000000000" }}Not found
object
object
Example
{ "error": { "type": "invalid_request", "code": "missing_api_key", "request_id": "req_01J9ZQ7V6H0000000000000000" }}Rate limited or over quota
object
object
Example
{ "error": { "type": "invalid_request", "code": "missing_api_key", "request_id": "req_01J9ZQ7V6H0000000000000000" }}Headers
Section titled “Headers”Seconds to wait before retrying. Always present on a 429.
Example
"reads";q=60;w=60The quota policy this request was counted against (draft-ietf-httpapi-ratelimit-headers): "<bucket>";q=<requests>;w=<window seconds>, e.g. "reads";q=60;w=60. Buckets: reads 60/min, status 600/min, creates 10/min, writes 20/min per credential; public 300/min per network on keyless routes.
Example
"reads";r=57;t=41Remaining quota in the current window: "<bucket>";r=<remaining>;t=<seconds until reset>, e.g. "reads";r=57;t=41.
Requests allowed per window in this bucket (legacy form of RateLimit-Policy).
Requests left in this window (legacy form of RateLimit).
Unix time (seconds) when the window resets.
Which bucket counted the request: reads, status, creates, writes or public.
Internal error
object
object
Example
{ "error": { "type": "invalid_request", "code": "missing_api_key", "request_id": "req_01J9ZQ7V6H0000000000000000" }}