curl --request GET \
--url https://app.formbricks.com/api/v3/feedbackRecords \
--header 'x-api-key: <api-key>'import requests
url = "https://app.formbricks.com/api/v3/feedbackRecords"
headers = {"x-api-key": "<api-key>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {'x-api-key': '<api-key>'}};
fetch('https://app.formbricks.com/api/v3/feedbackRecords', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://app.formbricks.com/api/v3/feedbackRecords",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"x-api-key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://app.formbricks.com/api/v3/feedbackRecords"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("x-api-key", "<api-key>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://app.formbricks.com/api/v3/feedbackRecords")
.header("x-api-key", "<api-key>")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.formbricks.com/api/v3/feedbackRecords")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["x-api-key"] = '<api-key>'
response = http.request(request)
puts response.read_body{
"data": [
{
"id": "018e1234-5678-7abc-8ef0-123456789abc",
"tenant_id": "ph7zv3w2u1x5k9d8m4q6c0bn",
"collected_at": "2026-08-15T10:30:00Z",
"created_at": "2026-08-15T10:30:02Z",
"updated_at": "2026-08-15T10:30:02Z",
"source_type": "survey",
"source_id": "cm2k7q9x00003v8h1a2b3c4d5",
"source_name": "Post-match survey",
"submission_id": "cm2k7qa1z0007v8h1e6f7g8h9",
"field_id": "q1",
"field_label": "How satisfied were you with the stadium experience?",
"field_type": "rating",
"value_number": 4,
"user_id": "fan-8813",
"language": "en"
},
{
"id": "018e1234-5678-7abc-8ef0-123456789abd",
"tenant_id": "ph7zv3w2u1x5k9d8m4q6c0bn",
"collected_at": "2026-08-15T10:30:00Z",
"created_at": "2026-08-15T10:30:02Z",
"updated_at": "2026-08-15T10:31:40Z",
"source_type": "survey",
"source_id": "cm2k7q9x00003v8h1a2b3c4d5",
"source_name": "Post-match survey",
"submission_id": "cm2k7qa1z0007v8h1e6f7g8h9",
"field_id": "q2",
"field_label": "What could we improve?",
"field_type": "text",
"value_text": "Queues at the north gate were far too long.",
"user_id": "fan-8813",
"language": "en",
"sentiment": "negative",
"sentiment_score": -0.62,
"emotions": [
"anger"
]
}
],
"limit": 100,
"next_cursor": "eyJ0IjoiMjAyNi0wOC0xNVQxMDozMDowMFoiLCJpIjoiMDE4ZTEyMzQtNTY3OC03YWJjLThlZjAtMTIzNDU2Nzg5YWJkIn0="
}List feedback records
Lists the feedback records of one dataset, newest first, with optional filters and keyset
pagination. Served by the API gateway, which authorizes the call against tenant_id and
forwards it to the feedback store — see the introduction for how the two layers answer.
Requires an API key with read permission (or higher) on a workspace the dataset is assigned to. The organization must hold the Unify Feedback entitlement.
Multi-value filters are OR-ed within a parameter and AND-ed across parameters. Repeat the
parameter to pass several values (?field_type=text&field_type=rating); comma-separated values
are not split. Filters on enrichment members (sentiment, emotions, sentiment_score_*)
never match records that have not been enriched — use has_sentiment=false / has_emotions=false
to find those.
curl --request GET \
--url https://app.formbricks.com/api/v3/feedbackRecords \
--header 'x-api-key: <api-key>'import requests
url = "https://app.formbricks.com/api/v3/feedbackRecords"
headers = {"x-api-key": "<api-key>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {'x-api-key': '<api-key>'}};
fetch('https://app.formbricks.com/api/v3/feedbackRecords', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://app.formbricks.com/api/v3/feedbackRecords",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"x-api-key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://app.formbricks.com/api/v3/feedbackRecords"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("x-api-key", "<api-key>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://app.formbricks.com/api/v3/feedbackRecords")
.header("x-api-key", "<api-key>")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.formbricks.com/api/v3/feedbackRecords")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["x-api-key"] = '<api-key>'
response = http.request(request)
puts response.read_body{
"data": [
{
"id": "018e1234-5678-7abc-8ef0-123456789abc",
"tenant_id": "ph7zv3w2u1x5k9d8m4q6c0bn",
"collected_at": "2026-08-15T10:30:00Z",
"created_at": "2026-08-15T10:30:02Z",
"updated_at": "2026-08-15T10:30:02Z",
"source_type": "survey",
"source_id": "cm2k7q9x00003v8h1a2b3c4d5",
"source_name": "Post-match survey",
"submission_id": "cm2k7qa1z0007v8h1e6f7g8h9",
"field_id": "q1",
"field_label": "How satisfied were you with the stadium experience?",
"field_type": "rating",
"value_number": 4,
"user_id": "fan-8813",
"language": "en"
},
{
"id": "018e1234-5678-7abc-8ef0-123456789abd",
"tenant_id": "ph7zv3w2u1x5k9d8m4q6c0bn",
"collected_at": "2026-08-15T10:30:00Z",
"created_at": "2026-08-15T10:30:02Z",
"updated_at": "2026-08-15T10:31:40Z",
"source_type": "survey",
"source_id": "cm2k7q9x00003v8h1a2b3c4d5",
"source_name": "Post-match survey",
"submission_id": "cm2k7qa1z0007v8h1e6f7g8h9",
"field_id": "q2",
"field_label": "What could we improve?",
"field_type": "text",
"value_text": "Queues at the north gate were far too long.",
"user_id": "fan-8813",
"language": "en",
"sentiment": "negative",
"sentiment_score": -0.62,
"emotions": [
"anger"
]
}
],
"limit": 100,
"next_cursor": "eyJ0IjoiMjAyNi0wOC0xNVQxMDozMDowMFoiLCJpIjoiMDE4ZTEyMzQtNTY3OC03YWJjLThlZjAtMTIzNDU2Nzg5YWJkIn0="
}Authorizations
Management API key; must include workspaceId as an allowed workspace with read, write, or manage permission.
Query Parameters
The feedback dataset to operate on (the dataset id, as shown by the MCP list_feedback_datasets tool). Required by the gateway on this operation: a missing or malformed value answers 400 before the request reaches the feedback store, and a dataset the API key's workspace is not assigned to answers 403. The two POST operations take the same value in the request body instead, and the single-record operations derive it from the record.
Records belonging to any of these logical submissions (e.g. response ids).
100255Records from any of these source types, e.g. survey, review.
100255Records from any of these sources (e.g. survey ids).
100255Records whose source display name is any of these. Prefer source_id where records carry one: a name can be edited or translated, the id is stable.
100255Every answer to any of these questions.
100255Records in any of these field groups (ranking / matrix questions).
100255Records of any of these field types. An empty value is ignored, so ?field_type= equals omitting the filter.
9The type of a feedback field, which determines which value_* member carries the answer: text → value_text (the only enrichable type), categorical → value_text + value_id, nps / csat / ces / rating / number → value_number, boolean → value_boolean, date → value_date.
text, categorical, nps, csat, ces, rating, number, boolean, date Records whose selected option id is any of these (e.g. every pick of one survey choice).
100255Everything any of these end users submitted.
100255Records given in any of these languages (ISO codes).
10010collected_at >= since (ISO 8601, inclusive). Must be between 1970-01-01 and 2080-12-31.
collected_at <= until (ISO 8601, inclusive).
created_at >= created_since (ISO 8601, inclusive). created_at is when the record was stored; collected_at (see since) is when the feedback was given. They diverge on a historical re-import, so this is the filter for "what did this import bring in".
created_at <= created_until (ISO 8601, inclusive).
value_number >= value_number_min (inclusive), e.g. NPS promoters with value_number_min=9. Records without a numeric answer are excluded. A max below the min answers 400.
value_number <= value_number_max (inclusive). Paired with the min it selects a band: 9..10 promoters, 0..6 detractors.
value_date >= value_date_min (inclusive) — bounds the answer to a date question, not when the feedback was collected.
value_date <= value_date_max (inclusive).
Records carrying any of these sentiment labels. An empty value is ignored.
6Sentiment polarity label inferred from value_text by the sentiment enrichment: five ordinal levels plus a distinct mixed. Server-generated and read-only.
very_negative, negative, neutral, positive, very_positive, mixed Records tagged with any of these emotions: a record tagged {joy} and one tagged {joy, anger} both match ?emotions=joy&emotions=anger. There is no "all of them" form.
6A single emotion label inferred from value_text by the emotion enrichment (the six Ekman basic emotions). Emotions are multi-label, so a record carries zero or more of these; "mixed" is not a label, it is two or more present at once. Server-generated and read-only.
joy, anger, sadness, fear, surprise, disgust sentiment_score >= sentiment_score_min (inclusive). The score is continuous where the label is bucketed, so this is the filter for "the most negative feedback".
-1 <= x <= 1sentiment_score <= sentiment_score_max (inclusive).
-1 <= x <= 1true selects enriched records (a sentiment label is present), false the not-yet-enriched ones. Omit for no constraint.
true selects records carrying emotion labels, false those without. Note false covers both "not yet classified" and "classified, no emotion detected".
true selects translated records, false untranslated ones. Omit for no constraint.
Column to order by. Only columns that are non-null and immutable after insert are offered: a mutable sort key would let a row move across the cursor between pages and be skipped.
collected_at, created_at Sort direction. Rows tied on the sort column are ordered by id ascending.
asc, desc Page size. Out-of-range and non-numeric values are rejected — -5 and 1001 answer 400 — with one exception: limit=0 is indistinguishable from omitting the parameter, so it is accepted and the default of 100 applies. Send no limit rather than 0 if you are computing it. The search operations behave differently again: they clamp instead of rejecting.
1 <= x <= 1000Opaque keyset cursor returned as next_cursor by the previous page. Omit on the first request. A cursor is a position within one specific ordering: presenting it with a different sort or order answers 400 — restart without a cursor instead.
Response
Feedback records retrieved successfully
Was this page helpful?