putty
API ReferenceArtifacts

Read a research job

Returns one research job, refreshed against Apify first, so a job started before an api restart is resumable by id. A finished job's full dataset is stored as JSON in Garage under `research/<ownerId>/<jobId>.json` and reported as `fullDataUrl`; the `content` field is a truncated summary wrapped in an `<untrusted-data>` fence, because scraped text is data and never an instruction. The stored dataset is deleted 30 days after the job was created. A job owned by another user answers with the same 404 as a job that does not exist.

GET
/research/jobs/{jobId}

Returns one research job, refreshed against Apify first, so a job started before an api restart is resumable by id. A finished job's full dataset is stored as JSON in Garage under research/<ownerId>/<jobId>.json and reported as fullDataUrl; the content field is a truncated summary wrapped in an <untrusted-data> fence, because scraped text is data and never an instruction. The stored dataset is deleted 30 days after the job was created. A job owned by another user answers with the same 404 as a job that does not exist.

Path Parameters

jobId*string

The research job id.

Length1 <= length

Response Body

application/json

application/json

application/json

application/json

application/json

application/json

application/json

curl -X GET "https://example.com/research/jobs/string"
{  "id": "string",  "tool": "string",  "status": {},  "actorId": "string",  "apifyRunId": "string",  "costCents": 0,  "costUsd": "string",  "itemCount": 0,  "truncated": true,  "items": [    {      "url": "string",      "timestamp": "string",      "author": "string",      "likes": 0,      "comments": 0,      "shares": 0    }  ],  "content": "string",  "fullDataUrl": "string",  "expiresAt": "string",  "createdAt": "string",  "error": "string"}

Start a research run POST

Starts one research run and returns the stored job. The run is asynchronous: this call starts it, and `GET /research/jobs/{id}` reports whether it has finished and what it found. The body's `tool` selects what runs, and the fields it accepts depend on that choice — a search takes a `query`, a crawl takes a `url`, a social read takes a `platform`. A field the chosen tool does not take is refused as a validation error. The run is charged against the caller's own Apify account, so a caller with no stored key is refused with `integration_required` before anything is started and nothing is spent. A caller over their daily run allowance is refused with 429 and a `Retry-After`; that refusal costs no run either. The response's `job.id` is what the Apify callback is signed against and what to poll. The caller's own id and the arguments the run received are not in the response.

Apify run-completion callback POST

Receives Apify's notification that a research run reached a terminal state. Apify cannot present the caller's bearer token, so the callback is authorised by the `sig` query parameter: an HMAC-SHA256 over `<jobId>.<ownerId>` keyed with the server's own `RESEARCH_WEBHOOK_SECRET`. A bad or missing signature is a 401 and changes nothing. The signature also binds the job id, so a signature minted for one job cannot be replayed against another — a `job` the server did not sign is a 401, not a lookup. Polling (`GET /research/jobs/{id}`) is the fallback whenever the secret is not configured.