curl --request PATCH \
--url https://app.surferseo.com/api/v2/workspaces/{workspace_id}/content_editors/{id} \
--header 'API-KEY: <api-key>' \
--header 'Content-Type: application/json' \
--data '
{
"custom_instructions": "Write from perspective of Y",
"custom_template_id": 2,
"custom_voice_id": 123,
"notes": "Updated notes",
"surfer_template": "blog_post",
"use_brand_knowledge": false
}
'import requests
url = "https://app.surferseo.com/api/v2/workspaces/{workspace_id}/content_editors/{id}"
payload = {
"custom_instructions": "Write from perspective of Y",
"custom_template_id": 2,
"custom_voice_id": 123,
"notes": "Updated notes",
"surfer_template": "blog_post",
"use_brand_knowledge": False
}
headers = {
"API-KEY": "<api-key>",
"Content-Type": "application/json"
}
response = requests.patch(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'PATCH',
headers: {'API-KEY': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
custom_instructions: 'Write from perspective of Y',
custom_template_id: 2,
custom_voice_id: 123,
notes: 'Updated notes',
surfer_template: 'blog_post',
use_brand_knowledge: false
})
};
fetch('https://app.surferseo.com/api/v2/workspaces/{workspace_id}/content_editors/{id}', 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.surferseo.com/api/v2/workspaces/{workspace_id}/content_editors/{id}",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "PATCH",
CURLOPT_POSTFIELDS => json_encode([
'custom_instructions' => 'Write from perspective of Y',
'custom_template_id' => 2,
'custom_voice_id' => 123,
'notes' => 'Updated notes',
'surfer_template' => 'blog_post',
'use_brand_knowledge' => false
]),
CURLOPT_HTTPHEADER => [
"API-KEY: <api-key>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://app.surferseo.com/api/v2/workspaces/{workspace_id}/content_editors/{id}"
payload := strings.NewReader("{\n \"custom_instructions\": \"Write from perspective of Y\",\n \"custom_template_id\": 2,\n \"custom_voice_id\": 123,\n \"notes\": \"Updated notes\",\n \"surfer_template\": \"blog_post\",\n \"use_brand_knowledge\": false\n}")
req, _ := http.NewRequest("PATCH", url, payload)
req.Header.Add("API-KEY", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.patch("https://app.surferseo.com/api/v2/workspaces/{workspace_id}/content_editors/{id}")
.header("API-KEY", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"custom_instructions\": \"Write from perspective of Y\",\n \"custom_template_id\": 2,\n \"custom_voice_id\": 123,\n \"notes\": \"Updated notes\",\n \"surfer_template\": \"blog_post\",\n \"use_brand_knowledge\": false\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.surferseo.com/api/v2/workspaces/{workspace_id}/content_editors/{id}")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Patch.new(url)
request["API-KEY"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"custom_instructions\": \"Write from perspective of Y\",\n \"custom_template_id\": 2,\n \"custom_voice_id\": 123,\n \"notes\": \"Updated notes\",\n \"surfer_template\": \"blog_post\",\n \"use_brand_knowledge\": false\n}"
response = http.request(request)
puts response.read_body{
"inserted_at": "2024-04-12T15:23:45Z",
"ai_article": {
"id": 123,
"status": "completed"
},
"ai_search_guidelines": {
"status": "completed"
},
"auto_optimize": {
"status": "completed"
},
"content_score": {
"ai_search": 62,
"seo": 75,
"total": 75
},
"created_by": "[email protected]",
"device": "mobile",
"id": 123,
"location": "United States",
"main_keyword": "seo tools",
"outline": {
"status": "completed"
},
"permalinks": [
{
"hash": "abc123xyz",
"type": "edit",
"url": "https://app.surferseo.com/drafts/s/abc123xyz"
}
],
"secondary_keywords": [
"keyword research",
"ai visibility"
],
"seo_guidelines": {
"status": "completed"
},
"state": "completed",
"workspace_id": 123,
"updated_at": "2024-06-01T09:17:33Z",
"custom_template_id": 2,
"custom_voice_id": 123,
"error": {
"details": [
{}
],
"reason": "<string>",
"message": "<string>"
},
"import_content_url": "https://example.com/article",
"surfer_template": "blog_post",
"target_word_count": 2000,
"use_brand_knowledge": true,
"custom_instructions": "Write from perspective of a senior developer",
"meta_description": "A curated list of the top SEO tools for 2025.",
"meta_title": "Best SEO Tools in 2025",
"notes": "Draft for Q2 campaign"
}{
"error": {
"details": [
{
"field": "keywords",
"reason": "required"
}
],
"reason": "validation_error"
}
}{
"error": {
"details": [],
"reason": "unauthorized"
}
}{
"error": {
"details": [],
"reason": "permission_denied"
}
}{
"error": {
"details": [],
"reason": "not_found"
}
}{
"error": {
"details": [
{
"allowed": [
"application/json"
],
"field": "accept",
"reason": "unsupported"
}
],
"message": "Accept must be one of: application/json",
"reason": "not_acceptable"
}
}{
"error": {
"details": [],
"message": "The request conflicts with the current resource state.",
"reason": "conflict"
}
}{
"error": {
"details": [],
"message": "Set Content-Type: application/json.",
"reason": "unsupported_media_type"
}
}{
"error": {
"details": [
{
"retry_after": 30
}
],
"reason": "rate_limit_exceeded"
}
}Customize Content Editor
Updates Content Editor settings. Only provided fields are changed. Requires the Content Editor
to be in completed state.
Rate limit: 10 requests/sec
curl --request PATCH \
--url https://app.surferseo.com/api/v2/workspaces/{workspace_id}/content_editors/{id} \
--header 'API-KEY: <api-key>' \
--header 'Content-Type: application/json' \
--data '
{
"custom_instructions": "Write from perspective of Y",
"custom_template_id": 2,
"custom_voice_id": 123,
"notes": "Updated notes",
"surfer_template": "blog_post",
"use_brand_knowledge": false
}
'import requests
url = "https://app.surferseo.com/api/v2/workspaces/{workspace_id}/content_editors/{id}"
payload = {
"custom_instructions": "Write from perspective of Y",
"custom_template_id": 2,
"custom_voice_id": 123,
"notes": "Updated notes",
"surfer_template": "blog_post",
"use_brand_knowledge": False
}
headers = {
"API-KEY": "<api-key>",
"Content-Type": "application/json"
}
response = requests.patch(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'PATCH',
headers: {'API-KEY': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
custom_instructions: 'Write from perspective of Y',
custom_template_id: 2,
custom_voice_id: 123,
notes: 'Updated notes',
surfer_template: 'blog_post',
use_brand_knowledge: false
})
};
fetch('https://app.surferseo.com/api/v2/workspaces/{workspace_id}/content_editors/{id}', 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.surferseo.com/api/v2/workspaces/{workspace_id}/content_editors/{id}",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "PATCH",
CURLOPT_POSTFIELDS => json_encode([
'custom_instructions' => 'Write from perspective of Y',
'custom_template_id' => 2,
'custom_voice_id' => 123,
'notes' => 'Updated notes',
'surfer_template' => 'blog_post',
'use_brand_knowledge' => false
]),
CURLOPT_HTTPHEADER => [
"API-KEY: <api-key>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://app.surferseo.com/api/v2/workspaces/{workspace_id}/content_editors/{id}"
payload := strings.NewReader("{\n \"custom_instructions\": \"Write from perspective of Y\",\n \"custom_template_id\": 2,\n \"custom_voice_id\": 123,\n \"notes\": \"Updated notes\",\n \"surfer_template\": \"blog_post\",\n \"use_brand_knowledge\": false\n}")
req, _ := http.NewRequest("PATCH", url, payload)
req.Header.Add("API-KEY", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.patch("https://app.surferseo.com/api/v2/workspaces/{workspace_id}/content_editors/{id}")
.header("API-KEY", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"custom_instructions\": \"Write from perspective of Y\",\n \"custom_template_id\": 2,\n \"custom_voice_id\": 123,\n \"notes\": \"Updated notes\",\n \"surfer_template\": \"blog_post\",\n \"use_brand_knowledge\": false\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.surferseo.com/api/v2/workspaces/{workspace_id}/content_editors/{id}")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Patch.new(url)
request["API-KEY"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"custom_instructions\": \"Write from perspective of Y\",\n \"custom_template_id\": 2,\n \"custom_voice_id\": 123,\n \"notes\": \"Updated notes\",\n \"surfer_template\": \"blog_post\",\n \"use_brand_knowledge\": false\n}"
response = http.request(request)
puts response.read_body{
"inserted_at": "2024-04-12T15:23:45Z",
"ai_article": {
"id": 123,
"status": "completed"
},
"ai_search_guidelines": {
"status": "completed"
},
"auto_optimize": {
"status": "completed"
},
"content_score": {
"ai_search": 62,
"seo": 75,
"total": 75
},
"created_by": "[email protected]",
"device": "mobile",
"id": 123,
"location": "United States",
"main_keyword": "seo tools",
"outline": {
"status": "completed"
},
"permalinks": [
{
"hash": "abc123xyz",
"type": "edit",
"url": "https://app.surferseo.com/drafts/s/abc123xyz"
}
],
"secondary_keywords": [
"keyword research",
"ai visibility"
],
"seo_guidelines": {
"status": "completed"
},
"state": "completed",
"workspace_id": 123,
"updated_at": "2024-06-01T09:17:33Z",
"custom_template_id": 2,
"custom_voice_id": 123,
"error": {
"details": [
{}
],
"reason": "<string>",
"message": "<string>"
},
"import_content_url": "https://example.com/article",
"surfer_template": "blog_post",
"target_word_count": 2000,
"use_brand_knowledge": true,
"custom_instructions": "Write from perspective of a senior developer",
"meta_description": "A curated list of the top SEO tools for 2025.",
"meta_title": "Best SEO Tools in 2025",
"notes": "Draft for Q2 campaign"
}{
"error": {
"details": [
{
"field": "keywords",
"reason": "required"
}
],
"reason": "validation_error"
}
}{
"error": {
"details": [],
"reason": "unauthorized"
}
}{
"error": {
"details": [],
"reason": "permission_denied"
}
}{
"error": {
"details": [],
"reason": "not_found"
}
}{
"error": {
"details": [
{
"allowed": [
"application/json"
],
"field": "accept",
"reason": "unsupported"
}
],
"message": "Accept must be one of: application/json",
"reason": "not_acceptable"
}
}{
"error": {
"details": [],
"message": "The request conflicts with the current resource state.",
"reason": "conflict"
}
}{
"error": {
"details": [],
"message": "Set Content-Type: application/json.",
"reason": "unsupported_media_type"
}
}{
"error": {
"details": [
{
"retry_after": 30
}
],
"reason": "rate_limit_exceeded"
}
}Authorizations
Path Parameters
x >= 1123
x >= 1123
Body
Customize params
Max 5000 words
"Write from perspective of Y"
Workspace-level custom template ID. Mutually exclusive with surfer_template.
x >= 1x >= 1123
"Updated notes"
Surfer template preset. Mutually exclusive with custom_template_id.
blog_post, category_description, product_description, comparison, listicle, landing_page, local_business, llm_optimized_article "blog_post"
When enabled, workspace brand knowledge is used where available; set false for unbranded output.
false
Response
Updated Content Editor
"2024-04-12T15:23:45Z"
Active AI article. null if no AI article has been generated for the Content Editor.
Show child attributes
Show child attributes
AI Search guidelines state. null when no AI Search guidelines exist for the Content Editor (e.g. created before the workflow existed) — distinct from {status: "executing"} (generation in progress).
Show child attributes
Show child attributes
Latest Auto-Optimize job state. null when no Auto-Optimize has ever been run on this Content Editor.
Show child attributes
Show child attributes
Show child attributes
Show child attributes
mobile, desktop "mobile"
x >= 1123
Location; get the list of supported locations from /api/v1/locations (no auth required)
"United States"
"seo tools"
Outline state. null when no outline exists for the Content Editor — distinct from {status: "scheduled"} (generation in progress).
Show child attributes
Show child attributes
Show child attributes
Show child attributes
["keyword research", "ai visibility"]
SEO guidelines state. null when no SEO guidelines exist for the Content Editor — distinct from {status: "executing"} (generation in progress).
Show child attributes
Show child attributes
completed, executing, failed, scheduled "completed"
x >= 1123
"2024-06-01T09:17:33Z"
Workspace-level custom template ID. Mutually exclusive with surfer_template.
x >= 1x >= 1123
Show child attributes
Show child attributes
"https://example.com/article"
Surfer template preset. Mutually exclusive with custom_template_id.
blog_post, category_description, product_description, comparison, listicle, landing_page, local_business, llm_optimized_article "blog_post"
2000
Whether workspace brand knowledge is enabled for this Content Editor.
true
"Write from perspective of a senior developer"
Draft meta description.
"A curated list of the top SEO tools for 2025."
Draft meta title.
"Best SEO Tools in 2025"
"Draft for Q2 campaign"
Was this page helpful?