# Quickstart

Read your survey feedback with a few HTTP calls. Check your API key, find a survey, get its public link and fetch the responses and statistics.

This guide takes you from an API key to the answers your respondents gave, in six calls. You will check your key, find a
survey, get the link respondents open, and read the responses and statistics. Every step is a plain `curl` command you
can paste into a terminal.

## Before you start

- Your organization's plan includes API access. If it doesn't, every call answers `403 API_ACCESS_NOT_IN_TIER`.
- You are an owner or admin of the organization. Only they can create API keys.
- A survey to read. This guide only reads, so if you have none yet, create one in the dashboard or through the API
  as [Building a survey](https://simplefeedback.de/developer/docs/building-a-survey.md) shows.

## Create an API key

In the dashboard, open **Settings → API keys** and create a key with the scopes `surveys:read` and
`responses:read`. That is everything this guide needs.

> **Warning:** The token is shown exactly once. Only a hash of it is stored, so a lost token can't be recovered,
> only replaced by a new key. Keep it in a password manager or your secret store, never in source code.

A token starts with `sfk_live_` and the key's 12-character public prefix. The prefix is safe to log and shows up in
the dashboard's key list; the rest of the token is the secret.

## Set up your shell

Put the API address and your token into two environment variables, so the commands below work as written:

```bash
export SF_API="https://simplefeedback.de/api"
export SF_KEY="sfk_live_..."
```

Every request sends the token as a bearer token: `Authorization: Bearer $SF_KEY`.

## Check the key

`GET /v1/me` needs no scope. It is the quickest way to tell a wrong token from a missing scope.

```bash
curl "$SF_API/v1/me" -H "Authorization: Bearer $SF_KEY"
```

```json
{
  "organizationId": "934c6a69-ed8e-4110-ba37-644a84047341",
  "tier": "BUSINESS",
  "scopes": ["surveys:read", "responses:read"],
  "venueId": null,
  "prefix": "V1StGXR8Z5jd"
}
```

A `401` means the token is wrong, revoked or expired. A `403` means the token is fine but the plan or the scopes are
not.

## Find your survey

List your surveys, newest first:

```bash
curl "$SF_API/v1/surveys?limit=5" -H "Authorization: Bearer $SF_KEY"
```

```json
{
  "data": [
    {
      "id": "a1b2c3d4-0001-4001-8001-000000000001",
      "name": "Customer satisfaction",
      "venueId": "35ce5fe3-69c6-4daf-a2b2-eb64e8840d43",
      "active": true,
      "responseCount": 34,
      "createdAt": "2026-10-07T20:01:05.870Z",
      "updatedAt": "2026-10-07T20:01:05.870Z"
    }
  ],
  "pagination": { "total": 1, "limit": 5, "offset": 0 }
}
```

Copy the `id` of the survey you want and keep it in a variable:

```bash
export SURVEY_ID="a1b2c3d4-0001-4001-8001-000000000001"
```

The list only carries summaries. `GET /v1/surveys/$SURVEY_ID` returns the whole survey, including its questions.

## Get the link respondents open

```bash
curl "$SF_API/v1/surveys/$SURVEY_ID/qr" -H "Authorization: Bearer $SF_KEY"
```

```json
{
  "publicUrl": "https://simplefeedback.de/p/a1b2c3d4-0001-4001-8001-000000000001",
  "qrDataUrl": "data:image/png;base64,iVBORw0KGgo..."
}
```

`publicUrl` is the page a respondent opens. `qrDataUrl` is a 512 px PNG of a QR code for that link.

Nothing is created by this call: the link and the image are the same every time. Responses that come in through them
are saved like any other, but opening the link is not counted as a scan. For printed codes that count their scans,
see [Distributing a survey](https://simplefeedback.de/developer/docs/distributing-a-survey.md).

> **Note:** `qrDataUrl` is tens of kilobytes. If you only need the link, for example to put it in an email or on a
> receipt, add `?includeImage=false` and the response carries `publicUrl` alone.

## Read the responses

```bash
curl "$SF_API/v1/surveys/$SURVEY_ID/responses?limit=25" -H "Authorization: Bearer $SF_KEY"
```

```json
{
  "data": [
    {
      "id": "cmuyjadna0002tl7dp2red0lc",
      "submittedAt": "2026-10-07T17:22:41.873Z",
      "answers": [
        { "questionId": "q_5d9e5ad2", "rating": 4 },
        { "questionId": "q_d39b94f8", "score": 9 },
        { "questionId": "q_2e8d9bb9", "text": "Lovely terrace, a bit slow at lunch." }
      ],
      "metadata": { "elapsedMs": 48210 }
    }
  ],
  "pagination": { "total": 34, "limit": 25, "offset": 0 }
}
```

Each answer names the question it belongs to by `questionId`. The question texts are in the survey itself, under
`modules[].questions[]` in `GET /v1/surveys/$SURVEY_ID`.

Responses come newest first, 25 per page by default and at most 100. Page through older ones with `offset`:

```bash
curl "$SF_API/v1/surveys/$SURVEY_ID/responses?limit=100&offset=100" -H "Authorization: Bearer $SF_KEY"
```

To keep a copy of every response in your own system, follow [Syncing results](https://simplefeedback.de/developer/docs/syncing-results.md).

## Get the numbers

If you want results rather than single answers, the statistics endpoint does the counting for you:

```bash
curl "$SF_API/v1/surveys/$SURVEY_ID/statistics" -H "Authorization: Bearer $SF_KEY"
```

```json
{
  "pollId": "a1b2c3d4-0001-4001-8001-000000000001",
  "totalResponses": 34,
  "analyzedResponses": 34,
  "truncated": false,
  "questionStats": [
    {
      "questionId": "q_5d9e5ad2",
      "questionText": "How satisfied were you with your visit?",
      "questionType": "rating",
      "totalAnswered": 34,
      "distribution": { "1": 1, "2": 0, "3": 7, "4": 16, "5": 10 },
      "percentages": { "1": 2.9, "2": 0, "3": 20.6, "4": 47.1, "5": 29.4 },
      "average": 4,
      "median": 4
    }
  ]
}
```

Unlike a response, every entry here already carries its `questionText`.

## Next steps

- Every route, request body and response shape is in the [API reference](https://simplefeedback.de/api/docs).
- Code generators can read the raw [OpenAPI document](https://simplefeedback.de/api/docs-json).
- Each key is limited to 60 requests per minute and route. Every response tells you where you stand in the
  `X-RateLimit-Remaining` and `X-RateLimit-Reset` headers.
- Before an integration runs unattended, read [Running an integration in production](https://simplefeedback.de/developer/docs/running-in-production.md).
