<!-- https://developers.silktide.com/v1/platform/results/get-several-results | Platform / Results -->

# Get several results in one request

`POST /v1/results`

Runs several [results](https://developers.silktide.com/v1/concepts/results.md) queries in one request, such as
a website's [scores](https://developers.silktide.com/v1/governance/scores/get-scores.md), the
[trend of one score](https://developers.silktide.com/v1/governance/scores/get-score-trend.md), its
[checks](https://developers.silktide.com/v1/governance/checks/get-checks.md) and
[one check in detail](https://developers.silktide.com/v1/governance/checks/get-check.md). The answer is the
same as calling each one on its own.

## Where each argument goes

The website goes at the top level, once: `websiteId` and `sectionId` (or
`view`, for Analytics) apply to every query. Everything else a query takes,
including `period`, goes in that query's `options`. A `period` at the top
level is ignored, and so is a `websiteId` inside `options`.

Use the argument names on each query's own page. Older documentation sent
`score` to the score trend; the name is `scoreId`, and without it that entry
fails.

## One failure does not stop the rest

The request answers `200` even when some of its queries fail. A query that
fails, because an option is missing or wrong, the query name is unknown, or no
website was sent, has an `error` in place of `result`, and the others still
answer. Check each entry for `error` before you read its `result`. Its
`status` is what the query would have answered on its own; some mistakes in
your request, such as a missing option, come back as `500` there.

`selector` is the name you sent, such as `score-trend`, not the path that a
single query returns.

A query for one edition of Silktide, such as the
[Silktide 4 scores](https://developers.silktide.com/v1/governance/scores/get-scores.md), fails with
`EditionNotAvailable` when the account does not have that edition. The others
still answer. See [Scores](https://developers.silktide.com/v1/concepts/scores.md).

Two mistakes fail the whole request: a website the API key cannot see
(`404`), and a `results` that is missing, empty or not an object (`400`). An
entry in `results` that is not an object, such as a bare query name, fails on
its own with `InvalidRequest`.

## Request

### Body

- `websiteId` (integer, optional, at least 1): The website every query is about. Governance queries need it.
- `sectionId` (integer, optional): A section of the website, for every query. Defaults to `0`, the whole website.
- `view` (string, optional, matches `^(all|[1-9]\d*)$`): For Analytics queries, the analytics view to read, or `all`. Send this or `websiteId`.
- `results` (map of objects, required): The queries to run, each under a key of your choosing. The answer uses the same keys.
  - `result` (string, required): The query: the part of its path after `/v1/results/`, such as `scores` or `score-trend`.
  - `options` (map of any JSON, optional): Every other argument the query takes, such as `scoreId` or `period`, by the names on its own page. Left out, none.

## Response

Status `200`.

- `results` (map of objects, required): Each query's answer or failure, under the key you gave it.
  - `result` (map of any JSON or list of any JSON, optional): The query's answer, the same as the `result` its own page describes. Absent when it failed.
  - `selector` (string, optional): The query's id, as you sent it in `result`, such as `score-trend`. Absent when it failed.
  - `duration` (number, optional): Seconds Silktide took to answer it. Absent when it failed.
  - `error` (object, optional): Why the query failed, in place of `result`. Present only when it failed.
    - `type` (string, required): What went wrong, such as `InvalidArgument` (a missing or bad option), `NotFound` (no query by that name), `InvalidRequest` (an entry that is not an object), `EditionNotAvailable` (a query for the edition of Silktide the account does not have), `InvalidContext` (no website) or `SelectorNotSpecified` (no `result`).
    - `message` (string, required): A readable explanation. It can change; never match on it.
    - `properties` (map of any JSON or empty list, optional): Details of the error, when it has any. An empty array when it has none. Absent for `SelectorNotSpecified`.
    - `status` (integer, optional): The HTTP status this failure would have had on its own. Absent for `SelectorNotSpecified`.

## Errors

| Status | Type | When |
| --- | --- | --- |
| 404 | `ModelNotFound` | No website or section with that id exists in the account, or the API key's user cannot see it. |
| 400 | `InvalidRequest` | `results` is missing, empty or not an object. |
| 401 | `NotLoggedIn` | No API key was sent, or it is not a key for this region. |
| 429 | `TooManyRequests` | Too many requests to this path in five seconds. Retry after the number of seconds in the `Retry-After` header, which `properties.retryAfter` repeats. |
| 503 | - | Silktide is busy. Retry after the number of seconds in the `Retry-After` header. |

## Access

- Permission: any user of the account
- Plan feature: none
- Edition: Silktide 4 and Silktide 5
- Cost: free
- Stability: stable, since 1.0.0

## Example: Two score trends at once, and one that fails

```bash
curl -X POST "https://api.eu.silktide.com/v1/results" \
  -H "Authorization: silktide token=$SILKTIDE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "websiteId": 2,
  "sectionId": 0,
  "results": {
    "overall-trend": {
      "result": "score-trend",
      "options": {
        "scoreId": "overall",
        "period": "last-90-days"
      }
    },
    "accessibility-trend": {
      "result": "score-trend",
      "options": {
        "scoreId": "wcag-22-aa",
        "period": "last-90-days"
      }
    },
    "no-score": {
      "result": "score-trend"
    }
  }
}'
```

Response:

```json
{
  "results": {
    "overall-trend": {
      "result": {
        "scores": [
          {
            "time": 1790216531,
            "score": 71.2
          },
          {
            "time": 1790821331,
            "score": 74.5
          }
        ]
      },
      "selector": "score-trend",
      "duration": 0.004
    },
    "accessibility-trend": {
      "result": {
        "scores": [
          {
            "time": 1790216531,
            "score": 58.9
          },
          {
            "time": 1790821331,
            "score": 63
          }
        ]
      },
      "selector": "score-trend",
      "duration": 0.003
    },
    "no-score": {
      "error": {
        "type": "InvalidArgument",
        "message": "An unspecified error occurred",
        "properties": [],
        "status": 500
      }
    }
  }
}
```
