Events

List runs across every check in the project

Changed on

Returns runs project-wide, one row per run rather than per ping, newest-started first by default (see sort): the console's Runs page and any automation that wants a single feed across every monitor instead of walking GET /api/v1/checks/{id}/runs per check.

A run is any (check, rid) pair that has at least one ping carrying that rid; started_at is that run's earliest ping. outcome is computed by the same rule the single-run/incident endpoints use: a terminal ping (success, fail, or cancel) on the run decides (succeeded / failed / cancelled); with no terminal ping, an incident opened while this run was the check's armed run means failed — this covers a silence/stalled/overrun incident that aborts a run without a fail ping of its own; a last-seen blocked ping means blocked; with none of those, a run whose start is older than its monitor's max_runtime_s (24 hours when unset), plus, for the monitor's current run, the time that run spent blocked, is unfinished: it started and never ended, which is not a failure and pages no one, and it is derived when read, so an old orphaned start is named this way with no change to stored data; otherwise running. Two entries with the same outcome are therefore comparable across every monitor in the project, regardless of what kind of job produced them.

A run can also exist only as OTLP spans, with no ping at all: a trace sent with no lastping.run_id (one run per trace, rid trace- followed by the hex trace id), or an explicit lastping.run_id that never pinged. Such a run is listed with traced: true, counted in counts, and graded from its spans (see Run.traced); it is in the window when its latest span is. A run with both pings and spans is listed once, from its pings. The set-up's test span is listed with is_test: true and the title LastPing test span, and is left out of counts.

Pagination is keyset: on (started_at, check_id, rid) descending by default, and under another sort on that sort's value with the same tie-break, so a run written while a caller is mid-page can never shift another run into or out of the page it already fetched. next_cursor is an opaque token; pass it back as cursor verbatim, with the same sort and dir, to fetch the next page, and stop when next_cursor is null. A cursor this API did not issue (or one that has been altered), or one issued for a different sort or dir, is rejected with 400 INVALID_CURSOR rather than silently starting over.

?sort= orders the list. cost, tokens and spans order by the run's total cost, its tokens in and out together, or its span count, and list only runs that hold spans: a run with no spans has none of the three. Under them a run is in the window when its latest span is at or after since, and counts describes the runs they list. A run whose cost is unknown (cost_usd null) is last in both directions. Ties are broken by check_id, then rid, both descending, so every run has exactly one place in the order.

?since= bounds the scan and defaults to the last 7 days; it is clamped to 90 days back regardless of what is asked for, because pings are pruned at 90 days and an earlier bound finds nothing further anyway. It selects runs, never reshapes them: a run with any ping at or after since is listed with its real started_at, duration and outcome, even when it started earlier. ?until= (RFC 3339) bounds started_at from above.

Every filter — monitor, outcome (all six values, including the incident-without-a-terminal-ping case described above), since, until and cursor — is applied before paging, so the page size is exact: a page shorter than ?limit= means there are no more matching runs, and next_cursor is null on exactly that page.

counts reports how many runs the requested window holds by outcome, alongside the page itself. It is not narrowed by ?outcome= or by the cursor, so one request answers both "which runs am I looking at" and "how many of each does this window hold". See the RunCounts schema, including why it is absent rather than zeroed on a page that matched nothing. Additive: every field this response carried before is unchanged.

?project= keeps the runs whose Claude Code hook start carried that project label (Run.project), and narrows counts too.

Traced filters (the console's Traced view): traced, agent, dependency, operation, model, has_error, min_duration_ms, min_cost_usd, trace_id and q. traced=true keeps only runs that hold spans (a non-null span_count). Each narrows the window the way monitor does, so counts describes the filtered runs. The span filters (dependency, operation, model) match spans that started inside the window. Each row also carries the run's agent_id, agent_name, source_name and multi_trace (see RunListItem).

Trace-level fields (individual ping bodies, step timelines, assertion verdicts) are not part of this list response; fetch GET /api/v1/checks/{id}/runs/{rid} for one run's full detail.

get/api/v1/runs

Request

  • Base URL: https://app.lastping.dev
  • URL: https://app.lastping.dev/api/v1/runs
  • Auth: API key in header X-Api-Key

Query parameters

monitorstring uuid

Restrict to one check's runs.

outcome'succeeded' | 'failed' | 'cancelled' | 'blocked' | 'running' | 'unfinished'

Restrict to one outcome. failed includes a run an incident closed with no terminal ping of its own; unfinished is a run that started and never ended within its monitor's maximum runtime (see the endpoint description's outcome rule).

sincestring date-time

RFC 3339 timestamp. Defaults to 7 days ago; clamped to 90 days ago.

untilstring date-time

RFC 3339 timestamp. Restricts to runs whose started_at is at or before it.

projectstring

Restrict to runs whose Claude Code hook start carried this project label: the name of the folder that holds the git repository the session worked in, or the folder's name when it is not in a repository, as each run reports it in project. The value is cleaned by the rule the label was stored by (a path is reduced to its last element), and a value that cleans to nothing (.) is refused with 400 INVALID_FILTER. A run with no label, and a run that exists only as spans, never matches. Narrows counts like monitor.

agentstring

Restrict to one agent's runs: runs of monitors attached to it, and runs of unattached monitors whose trace source resolves to it (by slug, name or adoption). The agent's id or its slug, as GET /api/v1/agents/{id} takes it; a reference that names no agent of this project returns no runs.

dependencystring

Runs with at least one span calling this dependency, by its exact name (Dependency.name).

operationstring

Runs with at least one span whose name STARTS WITH this text (a prefix, not a substring).

modelstring

Runs with at least one span that used exactly this model id.

has_errorboolean

true for runs with at least one span that reported an error, false for runs with none.

tracedboolean

true keeps only runs that hold spans (a run whose span_count is not null), whether they also pinged or not; counts then describes those runs only. false or absent keeps every run.

min_duration_msinteger

Runs that lasted at least this long; a run still in flight has no duration and is excluded. For a run with pings it filters on the duration the pings measured, not on the displayed duration_ms, which can be longer when spans arrive after the terminal ping.

min_cost_usdstring

Runs whose total cost is at least this many US dollars; a run with no cost is excluded.

trace_idstring

A trace id, or its first 8 or more hex digits: finds the run made from that trace (whose rid is trace- followed by the trace id). A run with its own lastping.run_id is found by q with that id instead.

qstring

Search by run id or trace id; matches runs whose rid contains it.

sort'started' | 'cost' | 'tokens' | 'spans'

The order of the list. started (the default) is newest-started first. cost, tokens and spans list only runs that hold spans, ordered by that figure (see the endpoint description).

dir'desc' | 'asc'

desc (the default) is highest first; asc is lowest first. sort=started is newest first only and refuses dir=asc.

limitinteger
Example:20

Maximum number of runs to return (default 20, max 100).

cursorstring

Opaque next_cursor value from a previous page.

Response

A page of runs (may be empty), in the order sort and dir name (newest-started first by default).

next_cursorstring nullable required

Opaque cursor for the next page; pass back as ?cursor=. Null when this is the last page.

Example response

{
  "runs": [
    {
      "check_id": "550e8400-e29b-41d4-a716-446655440000",
      "check_name": "Nightly backup",
      "project": "reddit-bot",
      "failure_cause": "upstream",
      "upstream_error": "server_error",
      "rid": "9876543210",
      "title": "nightly backup",
      "outcome": "failed",
      "started_at": "2026-09-12T08:00:00Z",
      "ended_at": "2026-09-12T08:14:00Z",
      "duration_ms": 840000,
      "step_count": 3,
      "exit_code": 1,
      "incident": {
        "id": 4821,
        "cause": "fail",
        "opened_at": "2026-09-12T08:14:00Z",
        "closed_at": "2026-09-12T08:45:00Z"
      },
      "span_count": 14,
      "tokens_in": 1820,
      "tokens_out": 340,
      "cost_usd": "0.021400",
      "cost_source": "estimated",
      "agent_id": "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
      "agent_name": "Nightly ETL bot",
      "source_name": "claude-code"
    }
  ],
  "next_cursor": "MjAyNi0wOS0xNVQxNjo1NTozNC41MzA0MzVafDgwMTc5YjJlLTc1YTAtNGYwNS1hM2EwLTQxZmEyZjBiZGI1MXxyMg",
  "counts": {
    "total": 412,
    "succeeded": 351,
    "failed": 38,
    "cancelled": 3,
    "blocked": 12,
    "running": 8
  }
}

Changes

    • info

      added the new optional query request parameter dir

    • info

      added the new optional query request parameter sort