> ## Documentation Index
> Fetch the complete documentation index at: https://help.honestly.com/llms.txt
> Use this file to discover all available pages before exploring further.

# GET /v1/scores — Retrieve Aggregated Engagement Scores

> Retrieve a single aggregated, anonymity-protected engagement score for a set of questions over a time window. Available as of Export API v1.2.0.

The `/v1/scores` endpoint returns a single aggregated, anonymity-protected engagement score for a specified set of questions over a given time window. Use it to track engagement trends in your warehouse without exposing individual responses.

The score is aggregated over every response to the requested `question_ids` within the `period_from` / `period_to` window. When the response base is below the anonymity threshold, the score is suppressed (`score: null`, `suppressed: true`) so you can distinguish "hidden for privacy" from "no data". The threshold is configured per survey; when the requested questions span multiple surveys, the strictest threshold applies.

All requested questions must be of the **same kind** — either all eNPS questions or all non-eNPS questions. Mixing eNPS and non-eNPS questions in one request returns a `400` error. The `unit` field tells you which kind was aggregated.

## Endpoint

```
GET https://api.honestly.com/v1/scores
```

## Query Parameters

<ParamField query="question_ids" type="string" required>
  Comma-separated question IDs to aggregate into a single score, for example `121,122`. These are the same integer IDs returned by the [`/v1/questions`](/developers/api-reference/questions) endpoint.
</ParamField>

<ParamField query="period_from" type="string" required>
  RFC 3339 datetime. Only aggregate responses given at or after this date.
</ParamField>

<ParamField query="period_to" type="string" required>
  RFC 3339 datetime. Only aggregate responses given at or before this date.
</ParamField>

## Example Request

```bash theme={null}
curl -X GET "https://api.honestly.com/v1/scores?question_ids=121,122&period_from=2026-01-01T00%3A00%3A00Z&period_to=2026-03-31T23%3A59%3A59Z" \
  -H "X-Api-Key: YOUR_SECRET_API_KEY"
```

## Example Response

```json theme={null}
{
  "score": 72.5,
  "unit": "favorability_percent",
  "respondents": 48,
  "suppressed": false,
  "suppression_reason": null
}
```

When the response base is below the anonymity threshold, the score is suppressed:

```json theme={null}
{
  "score": null,
  "unit": "favorability_percent",
  "respondents": 3,
  "suppressed": true,
  "suppression_reason": "below_anonymity_threshold"
}
```

## Response Fields

<ResponseField name="score" type="number | null">
  The aggregated score. Its range depends on `unit`: `0`–`100` for `favorability_percent`, `-100`–`100` for `enps`. `null` when `suppressed` is `true`.
</ResponseField>

<ResponseField name="unit" type="string">
  The unit of the `score` value. `favorability_percent` (0–100) for non-eNPS questions — each question is normalized to 0–100 and the results are averaged; `enps` (-100–100) when all requested questions are eNPS questions.
</ResponseField>

<ResponseField name="respondents" type="integer">
  Number of unique respondents the score is based on.
</ResponseField>

<ResponseField name="suppressed" type="boolean">
  Indicates the score was hidden because the response base is below the account anonymity threshold.
</ResponseField>

<ResponseField name="suppression_reason" type="string | null">
  Reason the score was suppressed. Currently `below_anonymity_threshold`. `null` when `suppressed` is `false`.
</ResponseField>

<Note>
  Scores are anonymity-protected. When the response base is below the per-survey anonymity threshold, `score` is `null`, `suppressed` is `true`, and `suppression_reason` explains why — rather than returning an error.
</Note>

<Tip>
  URL-encode datetime parameters in GET requests. The `+` sign in timezone offsets must be encoded as `%2B`. See [Error Handling](/developers/concepts/error-handling) if you encounter 400 errors.
</Tip>
