Skip to content

Build a survey with its questions through the API

You can build a survey and its questions entirely through the API. Create the survey and its questions in one request, add more questions later, and remove the ones you no longer need.

You need an API token to call these endpoints. API access is on Solo and above. See Authentication for how to create a token and send it, and Getting Started with the API if this is your first call. Your token needs the surveys:write scope to create a survey and to publish it, elements:write to add or remove questions, and elements:read to list a survey’s questions.

  1. Send a POST request to /api/v1/surveys with the survey’s details, as you would for an empty survey.
  2. Add an optional questions list to the request. Each item in the list describes one question.
  3. Read the response. data.questions lists each question in the order you sent it, with its new elementId. Keep these ids if you may want to remove a question later.

If you leave questions out, you get an empty survey, as before.

The list is all or nothing. SurveyRock checks the whole list first, and if any question is refused, no survey is created. You can send up to 100 questions in one request.

Here is a request that creates a survey with a rating question and an open-text question:

Terminal window
curl https://app.surveyrock.com/api/v1/surveys \
-H "Authorization: Bearer srk_live_..." \
-H "Content-Type: application/json" \
-d '{
"name": "Customer satisfaction",
"questions": [
{ "type": "rating_scale", "text": "How easy was it to get started?", "required": true },
{ "type": "text_input", "text": "What could we do better?" }
]
}'

The response gives each question’s id and the text respondents will see:

{
"success": true,
"data": {
"surveyId": "mv99t7e6HFj3SfPq",
"name": "Customer satisfaction",
"questions": [
{ "elementId": "mv99t7grUE5iYMcR", "type": "rating_scale", "text": "How easy was it to get started?" },
{ "elementId": "mv99t7gr2vhyQ3Qf", "type": "text_input", "text": "What could we do better?" }
]
}
}

The response also carries other fields, such as createdAt and meta. They’re left out here.

Field Needed What it does
type Yes One of text_input, multiple_choice, rating_scale, matrix, slider, nps, yes_no, email, date, ranking, file_upload.
text Yes The question as respondents read it, up to 5,000 characters.
helpText No Guidance shown under the question.
required No true if a respondent must answer. Defaults to false.
choices No The answer options, up to 50, for multiple_choice and ranking.
multipleAnswers No For multiple_choice: let respondents pick more than one.
rows No For matrix and slider: the statements to rate, up to 50.
columns No For matrix: the answer columns, up to 10. Leave it out for the standard scale.
subject No For nps only: who or what is being recommended, such as “our support team”.
pageTitle No Starts a new page at this question, with this title.

Each question comes out as the survey editor would make it for that type, with that type’s defaults. A field the type doesn’t use is ignored. A field SurveyRock doesn’t know is refused.

An NPS question always asks the standard sentence about its subject (“How likely is it that you would recommend us to a friend or colleague?”), so its text isn’t shown to respondents. The response gives the text respondents will see.

Dropdown questions aren’t available through the API.

The full field list, with limits, is in the OpenAPI schema at GET /api/v1/schema.

  1. Send a POST request to /api/v1/surveys/{id}/questions, where {id} is the survey’s id.
  2. Put the new questions in a questions list, described the same way as above. The list needs at least one question.
  3. Choose where they go:
    • No pageId: the questions are added at the end of the survey.
    • With pageId: the questions are added at the end of the page you name.
    • With pageTitle on a question: that question starts a new page with that title.
{
"questions": [
{ "type": "yes_no", "text": "Can we contact you?", "pageTitle": "Follow-up" }
]
}

The response gives each new question’s elementId and the pageId it landed on.

To find a page’s id, list the survey’s questions with GET /api/v1/surveys/{id}/elements. The response is a list of questions in order, and each carries a page_id.

pageTitle is refused when pageId names a page that isn’t the last one, because a new page always starts at the end of the survey.

Questions are only ever added after the last question of the survey or of the page you name. You can’t insert a question in the middle of a page, and you can’t move a question once it’s added.

Adding questions isn’t idempotent. Send an Idempotency-Key header with your request, so that a retry doesn’t add the questions twice. A retry with the same key and the same body replays the first response.

  1. Send a DELETE request to /api/v1/elements/{id}, where {id} is the elementId you got when you created or added the question.
  2. What happens next depends on whether anyone has answered it:
    • Nobody has answered it: the question is deleted for good.
    • It has responses: the question is retired. It leaves the survey, and it and its answers stay in your results.

The response says which one happened:

{ "success": true, "data": { "elementId": "mv99t7gr2vhyQ3Qf", "retired": false, "responses": 0 } }

retired is true when the question had answers, and responses is how many.

Skip logic that pointed at the question is cleaned up, and the rest of the page closes up. A question that a page loop takes its items from is refused until the loop no longer uses it.

Your plan sets how many questions one survey can hold. A request that would take a survey past that limit is refused with a 403 and the error code quota_exceeded, and nothing is created or added. The error’s details give the limit, the number requested and the plan that allows more.

Everything on this page changes the survey’s draft. If the survey is already published, respondents keep seeing the live version until you publish again. Republish the survey, in SurveyRock or with POST /api/v1/surveys/{id}/publish, to make your changes live.

Publishing through the API doesn’t create a link to share. A survey with no active link is live, but nobody can answer it yet. The publish response tells you this in data.sharing, and the next step is to create a web link with POST /api/v1/distribution/surveys/{surveyId} and activate it with POST /api/v1/distribution/{id}/activate. See Publishing and your first responses for what publishing does and how links work.

You’re setting up a customer satisfaction survey from your own system.

  1. You POST to /api/v1/surveys with the survey’s details and a questions list holding a rating question and an open-text “What could we do better?” question. The response returns an elementId for each question.
  2. Later you decide to add a short follow-up on a new page. You POST to /api/v1/surveys/{id}/questions with one question that has a pageTitle. It lands at the end of the survey, on a new page with that title.
  3. After the survey has been live for a week, you want to drop the open-text question. You DELETE it by its elementId. Because it has responses, it’s retired and the answers you’ve already collected stay in your results.
  4. You republish the survey so respondents see the updated version.

The same operations are available to Claude through the connector. See Use SurveyRock from Claude.

A new question doesn’t appear for respondents. The change is in the draft. Republish the survey.

I can’t add a question in the middle of a page. New questions go at the end of the survey or at the end of the page you name with pageId. Inserting mid-page isn’t available through the API.

I can’t move a question. Moving questions isn’t available through the API.

A dropdown question is refused. You get a 422 saying the question type can’t be created this way. Use one of the other types, or add the dropdown question in the survey builder.

The request is refused with a 422. The response names each question at fault. Check its type against the list above, and that it has no fields SurveyRock doesn’t know. The message lists the fields it does know.

The request is refused with quota_exceeded. The survey would hold more questions than your plan allows. Send fewer questions, or move to a plan with a higher limit.

I deleted a question but its answers are still in my results. That’s intended. A question with responses is retired, not erased, so the answers you collected aren’t lost.

Deleting a question a second time fails. Once a question is removed it can’t be removed again. A retired question returns a 404. A question that was deleted for good returns a 403, the same as an id your token can’t see.

The request is refused with another error. Check the response body against Error Format. If you see a rate-limit response, see Rate Limits. If the token is rejected, see Authentication.