curl --request POST \
--url https://api.snowseo.com/v3/cms/articles \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"title": "<string>",
"topicId": "<string>",
"articleOutline": {},
"teamId": "<string>",
"content": "<string>",
"type": "listicle",
"status": "draft",
"contentArticleType": "<string>",
"authors": [
"<string>"
],
"keywords": [
{
"keyword": "<string>"
}
],
"prompts": [
{
"prompt": "<string>"
}
],
"metaData": {
"metaDescription": "<string>",
"metaTitle": "<string>",
"ogTitle": "<string>",
"ogDescription": "<string>",
"twitterTitle": "<string>",
"twitterDescription": "<string>",
"canonicalUrl": "<string>"
},
"internalLinkTargetSlugs": [
"<string>"
],
"internalLinkTargetUrls": [
"<string>"
],
"language": "<string>"
}
'import requests
url = "https://api.snowseo.com/v3/cms/articles"
payload = {
"title": "<string>",
"topicId": "<string>",
"articleOutline": {},
"teamId": "<string>",
"content": "<string>",
"type": "listicle",
"status": "draft",
"contentArticleType": "<string>",
"authors": ["<string>"],
"keywords": [{ "keyword": "<string>" }],
"prompts": [{ "prompt": "<string>" }],
"metaData": {
"metaDescription": "<string>",
"metaTitle": "<string>",
"ogTitle": "<string>",
"ogDescription": "<string>",
"twitterTitle": "<string>",
"twitterDescription": "<string>",
"canonicalUrl": "<string>"
},
"internalLinkTargetSlugs": ["<string>"],
"internalLinkTargetUrls": ["<string>"],
"language": "<string>"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
title: '<string>',
topicId: '<string>',
articleOutline: {},
teamId: '<string>',
content: '<string>',
type: 'listicle',
status: 'draft',
contentArticleType: '<string>',
authors: ['<string>'],
keywords: [{keyword: '<string>'}],
prompts: [{prompt: '<string>'}],
metaData: {
metaDescription: '<string>',
metaTitle: '<string>',
ogTitle: '<string>',
ogDescription: '<string>',
twitterTitle: '<string>',
twitterDescription: '<string>',
canonicalUrl: '<string>'
},
internalLinkTargetSlugs: ['<string>'],
internalLinkTargetUrls: ['<string>'],
language: '<string>'
})
};
fetch('https://api.snowseo.com/v3/cms/articles', 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://api.snowseo.com/v3/cms/articles",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'title' => '<string>',
'topicId' => '<string>',
'articleOutline' => [
],
'teamId' => '<string>',
'content' => '<string>',
'type' => 'listicle',
'status' => 'draft',
'contentArticleType' => '<string>',
'authors' => [
'<string>'
],
'keywords' => [
[
'keyword' => '<string>'
]
],
'prompts' => [
[
'prompt' => '<string>'
]
],
'metaData' => [
'metaDescription' => '<string>',
'metaTitle' => '<string>',
'ogTitle' => '<string>',
'ogDescription' => '<string>',
'twitterTitle' => '<string>',
'twitterDescription' => '<string>',
'canonicalUrl' => '<string>'
],
'internalLinkTargetSlugs' => [
'<string>'
],
'internalLinkTargetUrls' => [
'<string>'
],
'language' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"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://api.snowseo.com/v3/cms/articles"
payload := strings.NewReader("{\n \"title\": \"<string>\",\n \"topicId\": \"<string>\",\n \"articleOutline\": {},\n \"teamId\": \"<string>\",\n \"content\": \"<string>\",\n \"type\": \"listicle\",\n \"status\": \"draft\",\n \"contentArticleType\": \"<string>\",\n \"authors\": [\n \"<string>\"\n ],\n \"keywords\": [\n {\n \"keyword\": \"<string>\"\n }\n ],\n \"prompts\": [\n {\n \"prompt\": \"<string>\"\n }\n ],\n \"metaData\": {\n \"metaDescription\": \"<string>\",\n \"metaTitle\": \"<string>\",\n \"ogTitle\": \"<string>\",\n \"ogDescription\": \"<string>\",\n \"twitterTitle\": \"<string>\",\n \"twitterDescription\": \"<string>\",\n \"canonicalUrl\": \"<string>\"\n },\n \"internalLinkTargetSlugs\": [\n \"<string>\"\n ],\n \"internalLinkTargetUrls\": [\n \"<string>\"\n ],\n \"language\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
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.post("https://api.snowseo.com/v3/cms/articles")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"title\": \"<string>\",\n \"topicId\": \"<string>\",\n \"articleOutline\": {},\n \"teamId\": \"<string>\",\n \"content\": \"<string>\",\n \"type\": \"listicle\",\n \"status\": \"draft\",\n \"contentArticleType\": \"<string>\",\n \"authors\": [\n \"<string>\"\n ],\n \"keywords\": [\n {\n \"keyword\": \"<string>\"\n }\n ],\n \"prompts\": [\n {\n \"prompt\": \"<string>\"\n }\n ],\n \"metaData\": {\n \"metaDescription\": \"<string>\",\n \"metaTitle\": \"<string>\",\n \"ogTitle\": \"<string>\",\n \"ogDescription\": \"<string>\",\n \"twitterTitle\": \"<string>\",\n \"twitterDescription\": \"<string>\",\n \"canonicalUrl\": \"<string>\"\n },\n \"internalLinkTargetSlugs\": [\n \"<string>\"\n ],\n \"internalLinkTargetUrls\": [\n \"<string>\"\n ],\n \"language\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.snowseo.com/v3/cms/articles")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"title\": \"<string>\",\n \"topicId\": \"<string>\",\n \"articleOutline\": {},\n \"teamId\": \"<string>\",\n \"content\": \"<string>\",\n \"type\": \"listicle\",\n \"status\": \"draft\",\n \"contentArticleType\": \"<string>\",\n \"authors\": [\n \"<string>\"\n ],\n \"keywords\": [\n {\n \"keyword\": \"<string>\"\n }\n ],\n \"prompts\": [\n {\n \"prompt\": \"<string>\"\n }\n ],\n \"metaData\": {\n \"metaDescription\": \"<string>\",\n \"metaTitle\": \"<string>\",\n \"ogTitle\": \"<string>\",\n \"ogDescription\": \"<string>\",\n \"twitterTitle\": \"<string>\",\n \"twitterDescription\": \"<string>\",\n \"canonicalUrl\": \"<string>\"\n },\n \"internalLinkTargetSlugs\": [\n \"<string>\"\n ],\n \"internalLinkTargetUrls\": [\n \"<string>\"\n ],\n \"language\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"article": {
"slug": "<string>",
"teamId": "<string>",
"title": "<string>",
"status": "<string>",
"type": "<string>"
}
}{
"success": true,
"error": "<string>",
"code": "<string>",
"message": "<string>",
"reference": "<string>"
}{
"success": true,
"error": "<string>",
"code": "<string>",
"message": "<string>",
"reference": "<string>"
}Create Article
Create a draft article from a pre-built outline.
curl --request POST \
--url https://api.snowseo.com/v3/cms/articles \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"title": "<string>",
"topicId": "<string>",
"articleOutline": {},
"teamId": "<string>",
"content": "<string>",
"type": "listicle",
"status": "draft",
"contentArticleType": "<string>",
"authors": [
"<string>"
],
"keywords": [
{
"keyword": "<string>"
}
],
"prompts": [
{
"prompt": "<string>"
}
],
"metaData": {
"metaDescription": "<string>",
"metaTitle": "<string>",
"ogTitle": "<string>",
"ogDescription": "<string>",
"twitterTitle": "<string>",
"twitterDescription": "<string>",
"canonicalUrl": "<string>"
},
"internalLinkTargetSlugs": [
"<string>"
],
"internalLinkTargetUrls": [
"<string>"
],
"language": "<string>"
}
'import requests
url = "https://api.snowseo.com/v3/cms/articles"
payload = {
"title": "<string>",
"topicId": "<string>",
"articleOutline": {},
"teamId": "<string>",
"content": "<string>",
"type": "listicle",
"status": "draft",
"contentArticleType": "<string>",
"authors": ["<string>"],
"keywords": [{ "keyword": "<string>" }],
"prompts": [{ "prompt": "<string>" }],
"metaData": {
"metaDescription": "<string>",
"metaTitle": "<string>",
"ogTitle": "<string>",
"ogDescription": "<string>",
"twitterTitle": "<string>",
"twitterDescription": "<string>",
"canonicalUrl": "<string>"
},
"internalLinkTargetSlugs": ["<string>"],
"internalLinkTargetUrls": ["<string>"],
"language": "<string>"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
title: '<string>',
topicId: '<string>',
articleOutline: {},
teamId: '<string>',
content: '<string>',
type: 'listicle',
status: 'draft',
contentArticleType: '<string>',
authors: ['<string>'],
keywords: [{keyword: '<string>'}],
prompts: [{prompt: '<string>'}],
metaData: {
metaDescription: '<string>',
metaTitle: '<string>',
ogTitle: '<string>',
ogDescription: '<string>',
twitterTitle: '<string>',
twitterDescription: '<string>',
canonicalUrl: '<string>'
},
internalLinkTargetSlugs: ['<string>'],
internalLinkTargetUrls: ['<string>'],
language: '<string>'
})
};
fetch('https://api.snowseo.com/v3/cms/articles', 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://api.snowseo.com/v3/cms/articles",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'title' => '<string>',
'topicId' => '<string>',
'articleOutline' => [
],
'teamId' => '<string>',
'content' => '<string>',
'type' => 'listicle',
'status' => 'draft',
'contentArticleType' => '<string>',
'authors' => [
'<string>'
],
'keywords' => [
[
'keyword' => '<string>'
]
],
'prompts' => [
[
'prompt' => '<string>'
]
],
'metaData' => [
'metaDescription' => '<string>',
'metaTitle' => '<string>',
'ogTitle' => '<string>',
'ogDescription' => '<string>',
'twitterTitle' => '<string>',
'twitterDescription' => '<string>',
'canonicalUrl' => '<string>'
],
'internalLinkTargetSlugs' => [
'<string>'
],
'internalLinkTargetUrls' => [
'<string>'
],
'language' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"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://api.snowseo.com/v3/cms/articles"
payload := strings.NewReader("{\n \"title\": \"<string>\",\n \"topicId\": \"<string>\",\n \"articleOutline\": {},\n \"teamId\": \"<string>\",\n \"content\": \"<string>\",\n \"type\": \"listicle\",\n \"status\": \"draft\",\n \"contentArticleType\": \"<string>\",\n \"authors\": [\n \"<string>\"\n ],\n \"keywords\": [\n {\n \"keyword\": \"<string>\"\n }\n ],\n \"prompts\": [\n {\n \"prompt\": \"<string>\"\n }\n ],\n \"metaData\": {\n \"metaDescription\": \"<string>\",\n \"metaTitle\": \"<string>\",\n \"ogTitle\": \"<string>\",\n \"ogDescription\": \"<string>\",\n \"twitterTitle\": \"<string>\",\n \"twitterDescription\": \"<string>\",\n \"canonicalUrl\": \"<string>\"\n },\n \"internalLinkTargetSlugs\": [\n \"<string>\"\n ],\n \"internalLinkTargetUrls\": [\n \"<string>\"\n ],\n \"language\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
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.post("https://api.snowseo.com/v3/cms/articles")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"title\": \"<string>\",\n \"topicId\": \"<string>\",\n \"articleOutline\": {},\n \"teamId\": \"<string>\",\n \"content\": \"<string>\",\n \"type\": \"listicle\",\n \"status\": \"draft\",\n \"contentArticleType\": \"<string>\",\n \"authors\": [\n \"<string>\"\n ],\n \"keywords\": [\n {\n \"keyword\": \"<string>\"\n }\n ],\n \"prompts\": [\n {\n \"prompt\": \"<string>\"\n }\n ],\n \"metaData\": {\n \"metaDescription\": \"<string>\",\n \"metaTitle\": \"<string>\",\n \"ogTitle\": \"<string>\",\n \"ogDescription\": \"<string>\",\n \"twitterTitle\": \"<string>\",\n \"twitterDescription\": \"<string>\",\n \"canonicalUrl\": \"<string>\"\n },\n \"internalLinkTargetSlugs\": [\n \"<string>\"\n ],\n \"internalLinkTargetUrls\": [\n \"<string>\"\n ],\n \"language\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.snowseo.com/v3/cms/articles")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"title\": \"<string>\",\n \"topicId\": \"<string>\",\n \"articleOutline\": {},\n \"teamId\": \"<string>\",\n \"content\": \"<string>\",\n \"type\": \"listicle\",\n \"status\": \"draft\",\n \"contentArticleType\": \"<string>\",\n \"authors\": [\n \"<string>\"\n ],\n \"keywords\": [\n {\n \"keyword\": \"<string>\"\n }\n ],\n \"prompts\": [\n {\n \"prompt\": \"<string>\"\n }\n ],\n \"metaData\": {\n \"metaDescription\": \"<string>\",\n \"metaTitle\": \"<string>\",\n \"ogTitle\": \"<string>\",\n \"ogDescription\": \"<string>\",\n \"twitterTitle\": \"<string>\",\n \"twitterDescription\": \"<string>\",\n \"canonicalUrl\": \"<string>\"\n },\n \"internalLinkTargetSlugs\": [\n \"<string>\"\n ],\n \"internalLinkTargetUrls\": [\n \"<string>\"\n ],\n \"language\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"article": {
"slug": "<string>",
"teamId": "<string>",
"title": "<string>",
"status": "<string>",
"type": "<string>"
}
}{
"success": true,
"error": "<string>",
"code": "<string>",
"message": "<string>",
"reference": "<string>"
}{
"success": true,
"error": "<string>",
"code": "<string>",
"message": "<string>",
"reference": "<string>"
}articleOutline, then call Start Generation with the returned slug to write the content.
Required body fields: title, topicId, and articleOutline. articleOutline.generateOutlineResponse is optional: pass the whole object returned by Generate Outline to keep your reviewed outline, or omit it and the outline is planned during generation. A supplied generateOutlineResponse that is malformed (no outline in it) returns 400. Optional: type (listicle default, comparison, news, review, guide), content, status, contentArticleType (pillar or cluster), authors (names to credit in byline order, primary first; names of AI assistants or models, such as “Snowy” or “ChatGPT”, are dropped), keywords and prompts (each item { keyword | prompt, type: "primary" | "secondary" }; mark one keyword and one prompt primary, and any extra primaries are stored as secondary), metaData, internalLinkTargetSlugs, internalLinkTargetUrls, and language. Returns 201 with { article: { slug, teamId, title, status, type } }. Limited to 30 requests per minute.
topicId is required - every article belongs to a topic cluster. Use Topics to find one, or Create Topic to add one. The topic name is taken from the cluster.language field sets the language code the article is written in (e.g. es). Codes are usually two letters, with three exceptions: Chinese is zh_CN or zh_TW, and Hebrew is iw (the legacy code, not he). Omit it to use your brand’s Writing Language (Settings → Content & CMS → Articles), falling back to your brand’s default language. Whichever applies is recorded on the article, so later refreshes, audits, and editor commands stay in that language even if the brand setting changes.Authorizations
Your SnowSEO API key (sk_...).
Body
Article title.
Required. Id of the team's topic cluster this article belongs to - get it from topic_clusters_list, or create one with create_topic_cluster. The topic name is taken from this cluster (do not pass a free-text topic).
Outline wrapper consumed by the generator. Shape: { generateOutlineResponse: , outlineAdditionalElements: { targetWordCount:number, _shouldIncludeFAQ:boolean, _shouldIncludeTldr:boolean, _shouldIncludeConclusion:boolean, _shouldIncludeCTA:boolean }, outlineNonEditable: { introduction: { estimated_words:150 }, conclusion?: { estimated_words:150 } }, audience_level:'auto', content_tone?:string }. Do NOT hand-author the outline - obtain it from generate_outline and pass it through unchanged.
Team id (falls back to the session's active project).
Optional markdown body. Leave empty for a draft you will fill via start_article_generation.
Article type. Defaults to listicle. Must match the outline's structure.
listicle, comparison, news, review, guide draft, processing, generated, scheduled, published, failed, manual "pillar" or "cluster" for pillar/cluster content, null/omitted for generic posts.
Names to credit in the byline (real people or the user's brand), primary first. Omitted, the brand's default authors are used. Names of AI assistants or models (e.g. Snowy, ChatGPT) are dropped.
STRONGLY RECOMMENDED: the keywords from generate_keywords. Optional only because a user can legitimately deselect them all in the UI wizard - an agent should always pass them, since they are what the generated body targets. Mark exactly one as primary (the main search term the article targets) and the rest secondary; extra primaries are stored as secondary.
Show child attributes
Show child attributes
STRONGLY RECOMMENDED: the prompts from generate_prompts. Optional for the same reason as keywords; pass them so the article answers the questions it is meant to rank for. Mark exactly one as primary; extra primaries are stored as secondary.
Show child attributes
Show child attributes
Show child attributes
Show child attributes
Slugs of existing SnowSEO articles to link out to (internal links).
Absolute http(s) URLs to link out to that are not SnowSEO articles.
Language code to write this article in (e.g. 'es'). Usually two letters; Chinese is 'zh_CN'/'zh_TW' and Hebrew is 'iw'. Defaults to the brand's Writing Language, then its brand language. Only pass this when the user asks for a specific language for this one article.
Response
Default Response
Show child attributes
Show child attributes
Was this page helpful?

