# List bulk job items

Returns the bulk job's items, discriminated by `type`.

**Downloading item artifacts.** For a `completed` item with a storage key,
`image_url` (`type: screenshot`) and both `content_url` and `markdown_url`
(`type: scrape`) are **pre-signed** URLs, minted fresh on every read of
this endpoint and valid for **600 seconds (10 minutes)** —
the same signing lifetime as `content_url` on `GET /v1/fetch/bulk/{id}`.
Download inside that window, or re-poll this endpoint for a fresh
signature.

**When no signature is minted.** Three cases mint none: an item that is not
`completed`, a `completed` item with no storage key, and a `completed` item
whose object could not be signed — during an object-storage outage, for
instance. In all three the item's stored value is returned unchanged: `null`
while nothing has been stored, and otherwise a **private, unsigned**
object-storage URL that answers `401`. These fields are neither nulled nor
flagged in that case, so treat an artifact URL as usable only once you have
downloaded it, and re-poll for a fresh signature otherwise.
`artifact_warnings` and its `artifact_unavailable` code describe the sibling
`artifacts` object on `type: scrape` items only — they say nothing about
`image_url`, `content_url`, or `markdown_url`.

**GET** `/v1/bulk/{id}/screenshots`

## Parameters

- `id` (path, string, required)
- `cursor` (query, string) — Opaque per-type seek cursor returned by the preceding page. It is bound to this bulk id, this mixed recovery route, and the exact status filter; cross-scope cursors return 422.
- `limit` (query, integer) — Maximum rows selected per requested item type; the 16-MiB response bound may stop sooner.
- `status` (query, string) — Optional item-status filter. A cursor may be reused only with the same filter.

## Responses

### 200 — Successful response.
- `bulk_id`
- `total`
- `next_cursor`
- `has_more`
- `job_counters`
- `job_counters.total`
- `job_counters.completed`
- `job_counters.failed`
- `items`
- `items.type`
- `items.id`
- `items.status`
- `items.url`
- `items.credits_charged`
- `items.billing`
- `items.billing.raw_bytes`
- `items.billing.factors`
- `items.billing.factors.name`
- `items.billing.factors.value`
- `items.billing.factors.reason`
- `items.billing.multiplier`
- `items.billing.billed_bytes`
- `items.error_code`
- `items.error_message`
- `items.created_at`
- `items.completed_at`
- `items.image_url`
- `items.page_title`
- `items.format`
- `items.video_url`
- `items.content_url`
- `items.content_type`
- `items.content_length`
- `items.markdown`
- `items.markdown_url`
- `items.artifacts`
- `items.artifacts.raw`
- `items.artifacts.markdown`
- `items.artifacts.json`
- `items.artifacts.links`
- `items.artifacts.links.url`
- `items.artifacts.links.text`
- `items.artifacts.links.isInternal`
- `items.artifacts.links.rel`
- `items.artifacts.images`
- `items.artifacts.images.url`
- `items.artifacts.images.alt`
- `items.artifacts.images.width`
- `items.artifacts.images.height`
- `items.artifacts.images.format`
- `items.artifacts.images.score`
- `items.artifact_warnings`
- `items.artifact_warnings.code`
- `items.artifact_warnings.message`
- `items.artifact_warnings.element`
- `items.terminal_outcome`

### 401 — Unauthorized. The request carried no valid credential, and no work was
performed. The unified `Error` envelope carries one of the codes below
in `error.code`; match on the code, never on the prose.

- `invalid_api_key` — a bearer API key was presented and rejected:
  unknown, revoked, expired or malformed. The bearer-only operations
  (`/v1/webhook-deliveries*` among them) also return it when no
  `Authorization` header is sent. `GET /v1/account` is the exception
  to "malformed": it returns `invalid_api_key` only for a key that
  starts `sk_test_` or `sk_live_` and is unknown, revoked or expired.
- `unauthorized` — the `Authorization` header is absent or malformed on
  an operation that also accepts a dashboard session (the dual-auth and
  billing operations). `GET /v1/account` also returns it, for no
  `Authorization` header and for any bearer value that does not start
  `sk_test_` or `sk_live_` (that value is checked as a dashboard
  session token).

Not retryable as-is: supply a valid API key first.
- `status`
- `error`
- `error.code`
- `error.message`
- `error.http_status`
- `error.failed_at`
- `error.details`