Skip to main content
GET
Get one run with the records it delivered

Authorizations

Authorization
string
header
required

API key passed as a Bearer token in the Authorization header.

Headers

x-api-version
enum<string>
default:2025-11-01
required

API version to use. 2025-11-01 is the only accepted value. Send it on every call: it is enforced today on POST /watch/{dataset}/search, where a missing or unrecognized value returns 400, and the other watch routes accept a request without it rather than relying on that.

Available options:
2025-11-01
Example:

"2025-11-01"

Path Parameters

dataset
enum<string>
required

The dataset the watch is built on.

Available options:
person,
company,
job,
social_post
Example:

"person"

watch_id
integer
required

The watch's id, returned when it was created.

Example:

46936

run_id
integer
required

The run's id, from runs[].id on the run-history endpoint.

Example:

54811

Response

The run, its logs, and its deliveries.

One run in full, with its counts, its activity log, and its deliveries.

id
integer
Example:

64200

started_at
string<date-time>
Example:

"2026-07-16T03:19:00Z"

completed_at
string<date-time> | null
Example:

"2026-07-16T03:20:00Z"

status
enum<string>
Available options:
RUNNING,
SUCCESS,
FAILED,
SKIPPED
Example:

"SUCCESS"

failure_reason
string | null
Example:

null

records_searched
integer

What the run started from. On a discovery watch, everything the query returned before the new-record check and the cap; on a realtime discovery watch, the rows the live retrieval returned. On an entity watch, the identifiers you listed in entities.

Example:

723

records_matched
integer | null

Entity watches only. Identifiers that resolved to a record Crustdata holds. A gap between this and records_searched is the part of your list that matches nothing, which a run would otherwise pass over in silence. null on a discovery run.

Example:

null

records_stale
integer | null

Discovery watches only. Matches an earlier run already handled, so records_searched minus records_stale is records_after_filter. On a job or post watch it counts the records posted before the last run. null on an entity run.

Example:

0

records_after_filter
integer

New records the run found, before the cap. On a discovery watch, the new matches. On an entity watch, the entities that changed, and on its first run every subject Crustdata resolved.

Example:

723

new_records_count
integer

Records this run delivered. It is below records_after_filter when the cap cut the run short.

Example:

5

credits_deducted
number
Example:

5

logs
object[]

The run's per-stage activity log.

payload_delivery
object

Where the records of this delivery actually travelled. Present on every notification and on every run summary, so read type before reaching for url.

An inline delivery carries {"type": "inline"} and nothing else, and its records sit in the body under results. A link delivery carries the whole block and no records: fetch url with a plain GET and no authorization header, since the pre-signed S3 URL carries its own authentication in the query string.

The file is NDJSON, one complete JSON object per line with no wrapping array, so you can stream it without holding the run in memory. Each line is the record the inline body would have carried: the raw dataset record for a discovery watch, without the added wrapper, and the same {changes, record} object for an entity watch.

A watch set to link still reports inline here when the file could not be written. The delivery then carries its records in the body as usual, so an outage costs the link and not the notification.

notifications
object[]

Every delivery this run attempted, with the records it carried. Empty unless status is SUCCESS: a run can leave rows behind without ever paying for or delivering them, and those rows are not readable here.

When payload_delivery.type is link, each entry keeps its outcome (sent_at, http_status) and drops payload: the records are in the linked file, and repeating the link per entry would only be noise.