Research

One finding with its full test history

GET/data-api/v2/research/findings/{id}
Scope
research:read
Freshness
catalog read
Group
Research
Platforms
any

What this endpoint answers

A single finding plus the per-pass record behind it: every time the hypothesis was tested, what the effect measured, its p-value, both q-values, the account split-half result, whether it passed, and which rule stopped it when it did not. This is the evidence trail that makes a published claim checkable rather than assertable.

This is a catalog read: it is served from the CRM Solid database in milliseconds, costs one budget unit, and reports how old the reading is in meta.cache_age_s. It needs the research:read scope (Research): Findings, runs and events from the autonomous research engine.

Good to know

  • Only promoted, retired and reversed hypotheses are addressable, matching what the list endpoint serves.
  • The test history is capped at the 24 most recent passes.
  • q_value is corrected inside the pre-registered family; q_pooled applies the same correction over the whole run as one family. Both are returned so nobody has to take the family definition on trust - if they disagree for a published row, that is worth knowing.
  • Failed passes are included. A finding whose only visible history is its wins is indistinguishable from one that is never re-checked.

Parameters

Every value the call accepts, with the example the spec ships so the request runs as written.

NameInRequiredExampleWhat it does
idpathrequiredmedia%7Cyes%7C*%7C*%7Cengagement%7CeffectHypothesis id: dimension|bucket|stratum|peer_group|metric|kind. URL-encode it - it contains | and * characters.

Call it

Authenticate with a bearer token or the x-api-key header. Keys are server-to-server credentials. Never embed one in front-end code - call the API from your own backend and forward the result.

curl
curl "https://crmsolid.com/data-api/v2/research/findings/media%257Cyes%257C*%257C*%257Cengagement%257Ceffect" \
  -H "Authorization: Bearer psk_live_..."
JavaScript
const res = await fetch("https://crmsolid.com/data-api/v2/research/findings/media%257Cyes%257C*%257C*%257Cengagement%257Ceffect", {
  headers: {
    Authorization: `Bearer ${process.env.CRM_SOLID_DATA_API_KEY}`,
  },
});

if (!res.ok) {
  const { error } = await res.json();
  throw new Error(`${error.code}: ${error.message} (${error.request_id})`);
}

const { data, meta } = await res.json();
Python
import os

import requests

res = requests.get(
    "https://crmsolid.com/data-api/v2/research/findings/media%257Cyes%257C*%257C*%257Cengagement%257Ceffect",
    headers={"Authorization": f"Bearer {os.environ['CRM_SOLID_DATA_API_KEY']}"},
    timeout=30,
)
res.raise_for_status()

payload = res.json()
data, meta = payload["data"], payload["meta"]

Keys look like psk_live_... for production keys, psk_test_... for test keys and are minted in the panel.

What comes back

Success is { data, meta }. Failure is { error: { code, message, request_id } }. The body below is the spec's own example: the values in it are illustrative readings, not live numbers.

Finding hypothesis id
media|yes|*|*|engagement|effect
Finding status
promoted
Finding effect lift pct
53
Finding effect lift lo pct
45
Finding effect lift hi pct
61
Finding evidence accounts sampled
1,862
Finding evidence q value
0
Finding evidence half concordant
yes
Tests
1 items
Gate alpha
0.05
Gate min accounts
50
Gate min lift pct
10
Gate min consecutive runs
3
200 GET /research/findings/{id}
{
  "data": {
    "finding": {
      "hypothesis_id": "media|yes|*|*|engagement|effect",
      "status": "promoted",
      "effect": {
        "lift_pct": 53,
        "lift_lo_pct": 45,
        "lift_hi_pct": 61
      },
      "evidence": {
        "accounts_sampled": 1862,
        "q_value": 1.2e-56,
        "half_concordant": true
      }
    },
    "tests": [
      {
        "run_id": 6,
        "run_started_at": "2026-08-19T02:00:00.000Z",
        "family": "engagement:core",
        "lift_pct": 53,
        "p_value": 4.6e-59,
        "q_value": 1.2e-56,
        "q_pooled": 3.9e-56,
        "split_half": {
          "lo_pct": 48.2,
          "hi_pct": 57.9,
          "p_max": 1.1e-21,
          "concordant": true
        },
        "passed": true,
        "fail_reason": null
      }
    ],
    "gate": {
      "alpha": 0.05,
      "min_accounts": 50,
      "min_lift_pct": 10,
      "min_consecutive_runs": 3
    }
  },
  "meta": {
    "request_id": "req_9f2c41a8b3d5",
    "generated_at": "2026-08-23T09:14:02.317Z",
    "took_ms": 42
  }
}

The meta block

  • request_idstringrequired

    Unique id for this request. Quote it in a support ticket.

  • generated_atstringrequired

    Server time the response was produced.

  • took_msintegerrequired

    Milliseconds spent server-side.

  • pagePageoptional
  • sourcestringoptional

    Which backend served the payload, for endpoints with more than one.

  • cache_age_sintegeroptional

    Age of the underlying data in seconds. 0 for live reads.

When it fails

GET /research/findings/{id} documents 8 failure statuses. Branch on error.code, which is stable and enumerated; message is prose and may change.

  • 401Unauthorizedunauthorized | invalid_key

    No key was presented, or the key is unknown, revoked or expired.

  • 402PaymentRequiredpayment_required | subscription_inactive

    The key is valid but the plan behind it cannot serve the call: the included requests are spent and overage is switched off, capped or unfunded (payment_required), or the billing period lapsed and was not renewed (subscription_inactive). Retrying does not help; paying does. The X-Plan-* headers on this response say how far past the line you are.

    Carries X-Plan, X-Plan-Limit, X-Plan-Overage, X-Plan-Period-End, X-Plan-Remaining.

  • 403Forbiddenforbidden_scope | forbidden_ip

    The key is valid but not allowed to make this call: it lacks the scope, or the request came from an address outside the key's allowlist.

  • 404NotFoundnot_found

    The addressed resource does not exist.

  • 422InvalidRequestinvalid_request

    A parameter is malformed, out of range or mutually exclusive with another. `details` names the offending fields.

  • 429RateLimitedrate_limited | quota_exceeded

    Either the burst ceiling for the current minute or the daily quota is spent. Distinguish with the code: rate_limited clears within the minute, quota_exceeded does not clear until 00:00 UTC.

    Carries Retry-After, X-Quota-Limit, X-Quota-Remaining, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-Request-Id.

  • 500InternalErrorinternal_error

    Something failed on our side. Internals are never leaked; quote the request id.

  • 503Unavailableupstream_timeout | upstream_error | not_configured

    The request could not be served right now. BRANCH ON error.code, not on the status: 'upstream_timeout' means a source was too slow (this is what a catalog query hitting its 15-second statement timeout returns, so it is reachable from any endpoint that reads the corpus, not only the live-scrape ones) and the same call is worth retrying with backoff - narrowing it with a smaller limit, a filtered scope or a less popular account makes it far less likely; 'upstream_error' means a source was unreachable, so back off further; 'not_configured' means the capability has no backing service in this deployment, and retrying will never help.

Every response carries X-Request-Id and meta.request_id. Quote it in support requests.

Coverage and limits

This endpoint is not platform-specific. Rate limits come from the tier on your key.

TierRequests a minuteRequests a dayLive reads a minute
free301,0005
standard12025,00020
pro600250,00060
unlimited6,00010,000,000600

This call only draws on the ordinary per-minute and per-day columns. Every response reports where you stand in X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset and the X-Plan headers.

Start calling it

A key takes a minute to mint in the panel, no card. The reference covers authentication, the envelope, scopes, rate limits and every error code in one page.

Reference path: /data-api/reference/research-findings-get

We value your privacy

We use cookies to improve our site, analyze traffic, and personalize ads. You can accept all, reject non-essential, or customize your choices. Read our Cookie Policy.