Content Workflows API Documentation

Content Workflows

List all content workflows

Parameters

No parameters.

Returns

Returns a list of content workflows for the user.

Request

GET /api/v2/content_workflows
curl https://app.contentharmony.com/api/v2/content_workflows \
  -X GET \
  -u {{token}}:{{secret}} \
  -H 'Content-Type: application/json'
fetch('https://app.contentharmony.com/api/v2/content_workflows', {
  method: 'GET',
  headers: {
    'Authorization': `Basic ${btoa('{{token}}:<span class="password-hidden" data-reveal-target="maskable">{{secret}}</span>')}`,
    'Content-Type': 'application/json'
  }
})
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(error => console.error('Error:', error));
import requests
import json

url = 'https://app.contentharmony.com/api/v2/content_workflows'
auth = ('{{token}}', '{{secret}}')
headers = {'Content-Type': 'application/json'}

response = requests.get(url, auth=auth, headers=headers)
print(response.json())
require 'net/http'
require 'json'

uri = URI('https://app.contentharmony.com/api/v2/content_workflows')
request = Net::HTTP::Get.new(uri)
request.basic_auth('{{token}}', '{{secret}}')
request['Content-Type'] = 'application/json'

response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
  http.request(request)
end

puts JSON.parse(response.body)

Response

{
  "object": "list",
  "data": [
    {
      "object": "keyword_brief",
      "id": 1,
      "keywords": "tatooine",
      "country_code": "US",
      "lang_code": "en",
      "project_id": 1,
      "status": "unstarted",
      "created_at": "2025-07-17T20:02:47.565Z",
      "started_at": null,
      "completed_at": null,
      "content_difficulty": null,
      "average_word_count": null,
      "search_volume": null,
      "cpc": null
    },
    {
      "object": "keyword_brief",
      "id": 2,
      "keywords": "moisture farming",
      "country_code": "US",
      "lang_code": "en",
      "project_id": 1,
      "status": "unstarted",
      "created_at": "2026-05-17T20:02:47.565Z",
      "started_at": null,
      "completed_at": null,
      "content_difficulty": null,
      "average_word_count": null,
      "search_volume": null,
      "cpc": null
    }
  ]
}

Create a content workflow

Parameters

project_id required
The ID of an existing project for the user.
keywords required
The keyword phrase for the content workflow.
country_code optional, default is project default country code
Two-letter country code for content workflow, in uppercase.
lang_code optional, default is project default language code
Language code for content workflow, in lowercase.

Returns

Returns the content workflow if it was created successfully.

Request

POST /api/v2/content_workflows
curl https://app.contentharmony.com/api/v2/content_workflows \
  -X POST \
  -u {{token}}:{{secret}} \
  -H 'Content-Type: application/json' \
  -d '{"project_id":272,"keywords":"tatooine","country_code":"US","lang_code":"en"}'
fetch('https://app.contentharmony.com/api/v2/content_workflows', {
  method: 'POST',
  headers: {
    'Authorization': `Basic ${btoa('{{token}}:<span class="password-hidden" data-reveal-target="maskable">{{secret}}</span>')}`,
    'Content-Type': 'application/json'
  }
  body: JSON.stringify({"project_id":272,"keywords":"tatooine","country_code":"US","lang_code":"en"})
})
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(error => console.error('Error:', error));
import requests
import json

url = 'https://app.contentharmony.com/api/v2/content_workflows'
auth = ('{{token}}', '{{secret}}')
headers = {'Content-Type': 'application/json'}
data = {"project_id":272,"keywords":"tatooine","country_code":"US","lang_code":"en"}

response = requests.post(url, auth=auth, headers=headers, json=data)
print(response.json())
require 'net/http'
require 'json'

uri = URI('https://app.contentharmony.com/api/v2/content_workflows')
request = Net::HTTP::Post.new(uri)
request.basic_auth('{{token}}', '{{secret}}')
request['Content-Type'] = 'application/json'
request.body = {"project_id":272,"keywords":"tatooine","country_code":"US","lang_code":"en"}

response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
  http.request(request)
end

puts JSON.parse(response.body)

Response

{
  "object": "keyword_brief",
  "id": 1,
  "keywords": "tatooine",
  "country_code": "US",
  "lang_code": "en",
  "project_id": 1,
  "status": "unstarted",
  "created_at": "2025-07-17T20:02:47.569Z",
  "started_at": null,
  "completed_at": null,
  "content_difficulty": null,
  "average_word_count": null,
  "search_volume": null,
  "cpc": null
}

Retrieve a content workflow

Parameters

No parameters.

Returns

Returns a content workflow if a valid ID was provided. The response includes:

  • Basic fields - id, keywords, country_code, lang_code, project_id, status, timestamps
  • General metrics (only for completed workflows with search_intent_report):
    • content_difficulty - Average content score of top-ranking pages (0-100)
    • average_word_count - Average word count of top-ranking pages
    • search_volume - Monthly search volume for the keyword
    • cpc - Cost per click in USD
  • links - Array of sharing links for this workflow
  • resources (only for completed workflows) - URLs to detailed report sections:
    • intent - Search intent analysis endpoint
    • topics - Topics analysis endpoint
    • related_keywords - Related keywords endpoint
    • outline - Outline/headings analysis endpoint
    • questions - Questions analysis endpoint
    • referenced_links - Referenced links endpoint
    • internal_links - Internal links endpoint
    • gov_edu_sources - Government and educational sources endpoint
    • news_sources - News sources endpoint
    • suggested_images - AI-suggested images endpoint
    • google_image_results - Image search results endpoint
    • competitor_images - Images from competing pages endpoint
    • shopping_images - Shopping product images endpoint
    • videos - Video search results endpoint
    • competitors - Competing URLs endpoint

Note: General metrics will be null for incomplete workflows or workflows without a search_intent_report.

Request

GET /api/v2/content_workflows/1
curl https://app.contentharmony.com/api/v2/content_workflows/1 \
  -X GET \
  -u {{token}}:{{secret}} \
  -H 'Content-Type: application/json'
fetch('https://app.contentharmony.com/api/v2/content_workflows/1', {
  method: 'GET',
  headers: {
    'Authorization': `Basic ${btoa('{{token}}:<span class="password-hidden" data-reveal-target="maskable">{{secret}}</span>')}`,
    'Content-Type': 'application/json'
  }
})
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(error => console.error('Error:', error));
import requests
import json

url = 'https://app.contentharmony.com/api/v2/content_workflows/1'
auth = ('{{token}}', '{{secret}}')
headers = {'Content-Type': 'application/json'}

response = requests.get(url, auth=auth, headers=headers)
print(response.json())
require 'net/http'
require 'json'

uri = URI('https://app.contentharmony.com/api/v2/content_workflows/1')
request = Net::HTTP::Get.new(uri)
request.basic_auth('{{token}}', '{{secret}}')
request['Content-Type'] = 'application/json'

response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
  http.request(request)
end

puts JSON.parse(response.body)

Response

{
  "object": "keyword_brief",
  "id": 1,
  "keywords": "tatooine",
  "country_code": "US",
  "lang_code": "en",
  "project_id": 1,
  "status": "completed",
  "created_at": "2025-07-17T20:02:47.573Z",
  "started_at": null,
  "completed_at": "2026-07-16T20:02:47.573Z",
  "content_difficulty": null,
  "average_word_count": null,
  "search_volume": null,
  "cpc": null,
  "links": [
    {
      "object": "link",
      "id": null,
      "show_discussion": true,
      "url": "https://app.contentharmony.com/l/sharediscussion01/brief"
    },
    {
      "object": "link",
      "id": null,
      "show_discussion": false,
      "url": "https://app.contentharmony.com/l/sharereadonly0001/brief"
    }
  ],
  "resources": {
    "intent": "https://app.contentharmony.com/api/v2/content_workflows/1/intent",
    "topics": "https://app.contentharmony.com/api/v2/content_workflows/1/topics",
    "related_keywords": "https://app.contentharmony.com/api/v2/content_workflows/1/related_keywords",
    "outline": "https://app.contentharmony.com/api/v2/content_workflows/1/outline",
    "questions": "https://app.contentharmony.com/api/v2/content_workflows/1/questions",
    "referenced_links": "https://app.contentharmony.com/api/v2/content_workflows/1/referenced_links",
    "internal_links": "https://app.contentharmony.com/api/v2/content_workflows/1/internal_links",
    "gov_edu_sources": "https://app.contentharmony.com/api/v2/content_workflows/1/gov_edu_sources",
    "news_sources": "https://app.contentharmony.com/api/v2/content_workflows/1/news_sources",
    "suggested_images": "https://app.contentharmony.com/api/v2/content_workflows/1/suggested_images",
    "google_image_results": "https://app.contentharmony.com/api/v2/content_workflows/1/google_image_results",
    "competitor_images": "https://app.contentharmony.com/api/v2/content_workflows/1/competitor_images",
    "shopping_images": "https://app.contentharmony.com/api/v2/content_workflows/1/shopping_images",
    "videos": "https://app.contentharmony.com/api/v2/content_workflows/1/videos",
    "competitors": "https://app.contentharmony.com/api/v2/content_workflows/1/competitors"
  }
}

Retrieve intent analysis for a content workflow

Parameters

No parameters.

Returns

Returns the search intent analysis for this content workflow. The response includes:

  • primary_intent - The dominant intent type identified (e.g., "Informational", "Transactional", "Research")
  • intent_scores - Intent scores on a 0-3 scale for each intent category
  • serp_features - Array of SERP feature names present (e.g., "people_also_ask", "knowledge_panel", "video_results", "ads")
  • featured_content - Special SERP elements including:
    • ai_overview - Google AI Overview content
    • answer_box - Featured snippet / answer box
    • knowledge_graph - Knowledge panel data
  • keyword_trends - Google Trends URLs:
    • timeseries_url - URL for time series trend data
    • geo_map_url - URL for geographic trend data
  • freshness - SERP freshness data including:
    • average_age - Average age in days of dates shown in SERP results
    • timeline - Desktop and mobile timeline data (newest, median, oldest dates)
    • age_brackets - Desktop and mobile age distribution (Past Day, Past Week, Past Month, Past Year, Years Ago, No Date)
  • metadata - Contains tab-specific sharing links

Note: This endpoint is available for content workflows in any status. If the search intent report hasn't been calculated yet, fields will be null.

Request

GET /api/v2/content_workflows/1/intent
curl https://app.contentharmony.com/api/v2/content_workflows/1/intent \
  -X GET \
  -u {{token}}:{{secret}} \
  -H 'Content-Type: application/json'
fetch('https://app.contentharmony.com/api/v2/content_workflows/1/intent', {
  method: 'GET',
  headers: {
    'Authorization': `Basic ${btoa('{{token}}:<span class="password-hidden" data-reveal-target="maskable">{{secret}}</span>')}`,
    'Content-Type': 'application/json'
  }
})
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(error => console.error('Error:', error));
import requests
import json

url = 'https://app.contentharmony.com/api/v2/content_workflows/1/intent'
auth = ('{{token}}', '{{secret}}')
headers = {'Content-Type': 'application/json'}

response = requests.get(url, auth=auth, headers=headers)
print(response.json())
require 'net/http'
require 'json'

uri = URI('https://app.contentharmony.com/api/v2/content_workflows/1/intent')
request = Net::HTTP::Get.new(uri)
request.basic_auth('{{token}}', '{{secret}}')
request['Content-Type'] = 'application/json'

response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
  http.request(request)
end

puts JSON.parse(response.body)

Response

{
  "object": "intent_report",
  "primary_intent": "Research",
  "intent_scores": {
    "Research": 3,
    "Transactional": 2,
    "Opinion": 1,
    "Fresh / News": 1,
    "Answers": 1,
    "Video": 1,
    "Visual": 1,
    "Brand / Entity": 1,
    "Local": 0
  },
  "serp_features": [
    "people_also_ask",
    "featured_snippet",
    "video_results",
    "ads"
  ],
  "featured_content": {
    "ai_overview": null,
    "answer_box": null,
    "knowledge_graph": null
  },
  "keyword_trends": {
    "timeseries_url": "https://trends.google.com/trends/explore?date=2004-01-01%202026-07-17&geo=US&q=tatooine",
    "geo_map_url": "https://trends.google.com/trends/explore?date=2004-01-01%202026-07-17&geo=US&q=tatooine"
  },
  "freshness": {
    "average_age": null,
    "timeline": {
      "desktop": {
        "newest_date": null,
        "median_date": null,
        "oldest_date": null
      },
      "mobile": {
        "newest_date": null,
        "median_date": null,
        "oldest_date": null
      }
    }
  },
  "metadata": {
    "links": [
      {
        "object": "link",
        "id": null,
        "show_discussion": true,
        "url": "https://app.contentharmony.com/l/sharediscussion01/intent"
      },
      {
        "object": "link",
        "id": null,
        "show_discussion": false,
        "url": "https://app.contentharmony.com/l/sharereadonly0001/intent"
      }
    ]
  }
}

List topics for a content workflow

Parameters

No parameters.

Returns

Returns a list of topics (IBM keywords) analyzed for this content workflow. The response includes:

  • metadata - Contains total_count, highlighted_count, and sharing links (points to /topic tab where both topics and related keywords are displayed)
  • topics - Array of topics with:
    • phrase - The topic phrase
    • documents_count - Number of top-ranking pages mentioning this topic
    • total_occurrences_count - Total mentions across all pages
    • priority - Importance score (0-100, based on percentage of pages containing topic)
    • variants - Alternative phrasings of the topic
    • highlighted - Whether topic was highlighted by user
    • ignored - Whether topic was ignored by user

Note: Topics are ordered by documents_count (descending), then total_occurrences_count (descending).

Request

GET /api/v2/content_workflows/1/topics
curl https://app.contentharmony.com/api/v2/content_workflows/1/topics \
  -X GET \
  -u {{token}}:{{secret}} \
  -H 'Content-Type: application/json'
fetch('https://app.contentharmony.com/api/v2/content_workflows/1/topics', {
  method: 'GET',
  headers: {
    'Authorization': `Basic ${btoa('{{token}}:<span class="password-hidden" data-reveal-target="maskable">{{secret}}</span>')}`,
    'Content-Type': 'application/json'
  }
})
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(error => console.error('Error:', error));
import requests
import json

url = 'https://app.contentharmony.com/api/v2/content_workflows/1/topics'
auth = ('{{token}}', '{{secret}}')
headers = {'Content-Type': 'application/json'}

response = requests.get(url, auth=auth, headers=headers)
print(response.json())
require 'net/http'
require 'json'

uri = URI('https://app.contentharmony.com/api/v2/content_workflows/1/topics')
request = Net::HTTP::Get.new(uri)
request.basic_auth('{{token}}', '{{secret}}')
request['Content-Type'] = 'application/json'

response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
  http.request(request)
end

puts JSON.parse(response.body)

Response

{
  "object": "list",
  "metadata": {
    "total_count": 2,
    "highlighted_count": 1,
    "links": [
      {
        "object": "link",
        "id": null,
        "show_discussion": true,
        "url": "https://app.contentharmony.com/l/sharediscussion01/topic"
      },
      {
        "object": "link",
        "id": null,
        "show_discussion": false,
        "url": "https://app.contentharmony.com/l/sharereadonly0001/topic"
      }
    ]
  },
  "topics": [
    {
      "object": "topic",
      "id": 1,
      "phrase": "tusken raiders",
      "documents_count": 8,
      "total_occurrences_count": 42,
      "priority": 85,
      "variants": [
        "tusken raider",
        "sand people"
      ],
      "highlighted": true,
      "ignored": false,
      "content_grader_uses": 0
    },
    {
      "object": "topic",
      "id": 2,
      "phrase": "moisture farming",
      "documents_count": 6,
      "total_occurrences_count": 28,
      "priority": 65,
      "variants": [
        "moisture farm",
        "water farming"
      ],
      "highlighted": false,
      "ignored": false,
      "content_grader_uses": 0
    }
  ]
}

Parameters

No parameters.

Returns

Returns a list of related search keywords from Google for this content workflow. These are the "related searches" that appear at the bottom of Google search results. The response includes:

  • metadata - Contains total_count, highlighted_count, and sharing links (points to /topic tab where both topics and related keywords are displayed)
  • related_keywords - Array of related searches with:
    • phrase - The related search phrase
    • position - Order in which Google displays them (0-indexed)
    • highlighted - Whether keyword was highlighted by user

Note: Related keywords are ordered by position (ascending), matching the order Google displays them.

Request

GET /api/v2/content_workflows/1/related_keywords
curl https://app.contentharmony.com/api/v2/content_workflows/1/related_keywords \
  -X GET \
  -u {{token}}:{{secret}} \
  -H 'Content-Type: application/json'
fetch('https://app.contentharmony.com/api/v2/content_workflows/1/related_keywords', {
  method: 'GET',
  headers: {
    'Authorization': `Basic ${btoa('{{token}}:<span class="password-hidden" data-reveal-target="maskable">{{secret}}</span>')}`,
    'Content-Type': 'application/json'
  }
})
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(error => console.error('Error:', error));
import requests
import json

url = 'https://app.contentharmony.com/api/v2/content_workflows/1/related_keywords'
auth = ('{{token}}', '{{secret}}')
headers = {'Content-Type': 'application/json'}

response = requests.get(url, auth=auth, headers=headers)
print(response.json())
require 'net/http'
require 'json'

uri = URI('https://app.contentharmony.com/api/v2/content_workflows/1/related_keywords')
request = Net::HTTP::Get.new(uri)
request.basic_auth('{{token}}', '{{secret}}')
request['Content-Type'] = 'application/json'

response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
  http.request(request)
end

puts JSON.parse(response.body)

Response

{
  "object": "list",
  "metadata": {
    "total_count": 2,
    "highlighted_count": 1,
    "links": [
      {
        "object": "link",
        "id": null,
        "show_discussion": true,
        "url": "https://app.contentharmony.com/l/sharediscussion01/topic"
      },
      {
        "object": "link",
        "id": null,
        "show_discussion": false,
        "url": "https://app.contentharmony.com/l/sharereadonly0001/topic"
      }
    ]
  },
  "related_keywords": [
    {
      "object": "related_keyword",
      "id": 1,
      "phrase": "tatooine sunset",
      "position": 0,
      "highlighted": true
    },
    {
      "object": "related_keyword",
      "id": 2,
      "phrase": "tatooine map",
      "position": 1,
      "highlighted": false
    }
  ]
}

List outline headings for a content workflow

Parameters

No parameters.

Returns

Returns outline headings grouped by source for this content workflow. Headings come from two types of sources:

  • AISuggestion - AI-generated heading suggestions for the content
  • Url - Headings extracted from competitor pages that rank for this keyword

The response includes:

  • metadata - Contains total_count, highlighted_count, ai_suggestions_count, competitor_urls_count, and sharing links
  • heading_sources - Array of sources, each containing:
    • source_type - Either "AISuggestion" or "Url"
    • source_id - ID of the source
    • source_url - URL of the competitor page (only for Url sources)
    • total_headings - Number of headings from this source
    • highlighted_headings - Number of highlighted headings from this source
    • headings - Array of heading objects with:
      • heading_text - The heading content
      • heading_type - Type (h1, h2, h3, h4, h5, h6, li)
      • source_position - Order in which heading appears on the source page (0-indexed)
      • highlighted - Whether heading was highlighted by user

Note: AI suggestion sources appear first, followed by URL sources. Headings within each source are ordered by source_position (ascending), matching the order they appear on the original page.

Request

GET /api/v2/content_workflows/1/outline
curl https://app.contentharmony.com/api/v2/content_workflows/1/outline \
  -X GET \
  -u {{token}}:{{secret}} \
  -H 'Content-Type: application/json'
fetch('https://app.contentharmony.com/api/v2/content_workflows/1/outline', {
  method: 'GET',
  headers: {
    'Authorization': `Basic ${btoa('{{token}}:<span class="password-hidden" data-reveal-target="maskable">{{secret}}</span>')}`,
    'Content-Type': 'application/json'
  }
})
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(error => console.error('Error:', error));
import requests
import json

url = 'https://app.contentharmony.com/api/v2/content_workflows/1/outline'
auth = ('{{token}}', '{{secret}}')
headers = {'Content-Type': 'application/json'}

response = requests.get(url, auth=auth, headers=headers)
print(response.json())
require 'net/http'
require 'json'

uri = URI('https://app.contentharmony.com/api/v2/content_workflows/1/outline')
request = Net::HTTP::Get.new(uri)
request.basic_auth('{{token}}', '{{secret}}')
request['Content-Type'] = 'application/json'

response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
  http.request(request)
end

puts JSON.parse(response.body)

Response

{
  "object": "list",
  "metadata": {
    "total_count": 4,
    "highlighted_count": 1,
    "ai_suggestions_count": 1,
    "competitor_urls_count": 1,
    "links": [
      {
        "object": "link",
        "id": null,
        "show_discussion": true,
        "url": "https://app.contentharmony.com/l/sharediscussion01/outline"
      },
      {
        "object": "link",
        "id": null,
        "show_discussion": false,
        "url": "https://app.contentharmony.com/l/sharereadonly0001/outline"
      }
    ]
  },
  "heading_sources": [
    {
      "object": "heading_source",
      "source_type": "AISuggestion",
      "source_id": 1,
      "total_headings": 2,
      "highlighted_headings": 1,
      "headings": [
        {
          "object": "heading",
          "id": 1,
          "heading_text": "Geography and Climate",
          "heading_type": "h2",
          "source_position": 0,
          "highlighted": true
        },
        {
          "object": "heading",
          "id": 2,
          "heading_text": "Notable Inhabitants",
          "heading_type": "h3",
          "source_position": 1,
          "highlighted": false
        }
      ]
    },
    {
      "object": "heading_source",
      "source_type": "Url",
      "source_id": 1,
      "source_url": "https://en.wikipedia.org/wiki/Tatooine",
      "total_headings": 2,
      "highlighted_headings": 0,
      "headings": [
        {
          "object": "heading",
          "id": 3,
          "heading_text": "Tatooine in Star Wars",
          "heading_type": "h1",
          "source_position": 0,
          "highlighted": false
        },
        {
          "object": "heading",
          "id": 4,
          "heading_text": "Major Locations and Settlements",
          "heading_type": "h2",
          "source_position": 1,
          "highlighted": false
        }
      ]
    }
  ]
}

List questions for a content workflow

Parameters

No parameters.

Returns

Returns questions for this content workflow ordered by category and source position. Questions come from multiple source types:

  • AI Suggestion - AI-generated questions about the topic
  • Google PAA - People Also Ask questions from Google search results
  • Quora - Questions from Quora related to the topic
  • Stack Exchange - Questions from Stack Exchange sites
  • Reddit - Questions from Reddit discussions
  • Yahoo! Answers - Questions from Yahoo! Answers (deprecated)

The response includes:

  • metadata - Contains total_count, highlighted_count, by_category breakdown, and sharing links
  • questions - Array of question objects, each containing:
    • question_text - The actual question text
    • category - Category symbol (ai_suggestion, desktop, quora, stack_exchange, reddit, yahoo_answers)
    • category_name - Display name for the category
    • source_position - Order within the source (0-indexed)
    • snippet - Answer snippet or description (optional)
    • title - Page/article title (optional)
    • displayed_link - Display URL (optional)
    • url - Full URL (optional, only for questions with associated URL)
    • highlighted - Whether question was highlighted by user

Note: Questions are ordered by category (Google PAA first, AI Suggestion second, then others), then by source_position within each category.

Request

GET /api/v2/content_workflows/1/questions
curl https://app.contentharmony.com/api/v2/content_workflows/1/questions \
  -X GET \
  -u {{token}}:{{secret}} \
  -H 'Content-Type: application/json'
fetch('https://app.contentharmony.com/api/v2/content_workflows/1/questions', {
  method: 'GET',
  headers: {
    'Authorization': `Basic ${btoa('{{token}}:<span class="password-hidden" data-reveal-target="maskable">{{secret}}</span>')}`,
    'Content-Type': 'application/json'
  }
})
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(error => console.error('Error:', error));
import requests
import json

url = 'https://app.contentharmony.com/api/v2/content_workflows/1/questions'
auth = ('{{token}}', '{{secret}}')
headers = {'Content-Type': 'application/json'}

response = requests.get(url, auth=auth, headers=headers)
print(response.json())
require 'net/http'
require 'json'

uri = URI('https://app.contentharmony.com/api/v2/content_workflows/1/questions')
request = Net::HTTP::Get.new(uri)
request.basic_auth('{{token}}', '{{secret}}')
request['Content-Type'] = 'application/json'

response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
  http.request(request)
end

puts JSON.parse(response.body)

Response

{
  "object": "list",
  "metadata": {
    "total_count": 3,
    "highlighted_count": 1,
    "by_category": {
      "ai_suggestions": 1,
      "google_paa": 2,
      "quora": 0,
      "stack_exchange": 0,
      "reddit": 0,
      "yahoo_answers": 0
    },
    "links": [
      {
        "object": "link",
        "id": null,
        "show_discussion": true,
        "url": "https://app.contentharmony.com/l/sharediscussion01/questions"
      },
      {
        "object": "link",
        "id": null,
        "show_discussion": false,
        "url": "https://app.contentharmony.com/l/sharereadonly0001/questions"
      }
    ]
  },
  "questions": [
    {
      "object": "question",
      "id": 1,
      "question_text": "Where is Tatooine in real life?",
      "category": "ai_suggestion",
      "category_name": "AI Suggestion",
      "source_position": 0,
      "snippet": null,
      "title": null,
      "displayed_link": null,
      "highlighted": true
    },
    {
      "object": "question",
      "id": 2,
      "question_text": "Why is Tatooine included in so many Star Wars plots?",
      "category": "desktop",
      "category_name": "Google PAA",
      "source_position": 0,
      "snippet": "Tatooine filming locations include Tunisia in North Africa...",
      "title": "Tatooine Filming Locations",
      "displayed_link": "en.wikipedia.org/wiki/Tatooine",
      "url": "https://en.wikipedia.org/wiki/Tatooine",
      "highlighted": false
    },
    {
      "object": "question",
      "id": 3,
      "question_text": "What's so special about Tatooine?",
      "category": "desktop",
      "category_name": "Google PAA",
      "source_position": 1,
      "snippet": "It appeared twice in the original trilogy because it was Luke's home planet...",
      "title": "Understanding Tatooine's Importance",
      "displayed_link": "starwars.fandom.com/wiki/Tatooine",
      "url": "https://en.wikipedia.org/wiki/Tatooine",
      "highlighted": false
    }
  ]
}

List competitors for a content workflow

Parameters

No parameters.

Returns

Returns a list of competitor pages analyzed for this content workflow. The response includes:

  • metadata - Contains total_count, highlighted_count, and tab-specific sharing links
  • data - Array of competitors with ranking position, traffic estimates, keyword data, and highlighted status

Note: This endpoint is available for content workflows in any status. The URL will be included in the resources object when retrieving a completed content workflow.

Request

GET /api/v2/content_workflows/1/competitors
curl https://app.contentharmony.com/api/v2/content_workflows/1/competitors \
  -X GET \
  -u {{token}}:{{secret}} \
  -H 'Content-Type: application/json'
fetch('https://app.contentharmony.com/api/v2/content_workflows/1/competitors', {
  method: 'GET',
  headers: {
    'Authorization': `Basic ${btoa('{{token}}:<span class="password-hidden" data-reveal-target="maskable">{{secret}}</span>')}`,
    'Content-Type': 'application/json'
  }
})
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(error => console.error('Error:', error));
import requests
import json

url = 'https://app.contentharmony.com/api/v2/content_workflows/1/competitors'
auth = ('{{token}}', '{{secret}}')
headers = {'Content-Type': 'application/json'}

response = requests.get(url, auth=auth, headers=headers)
print(response.json())
require 'net/http'
require 'json'

uri = URI('https://app.contentharmony.com/api/v2/content_workflows/1/competitors')
request = Net::HTTP::Get.new(uri)
request.basic_auth('{{token}}', '{{secret}}')
request['Content-Type'] = 'application/json'

response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
  http.request(request)
end

puts JSON.parse(response.body)

Response

{
  "object": "list",
  "metadata": {
    "total_count": 2,
    "highlighted_count": 1,
    "links": [
      {
        "object": "link",
        "id": null,
        "show_discussion": true,
        "url": "https://app.contentharmony.com/l/sharediscussion01/competitors"
      },
      {
        "object": "link",
        "id": null,
        "show_discussion": false,
        "url": "https://app.contentharmony.com/l/sharereadonly0001/competitors"
      }
    ]
  },
  "competitors": [
    {
      "object": "competitor",
      "id": 1,
      "position": 1,
      "title": "Tatooine - Wikipedia",
      "description": "Desert planet in the Star Wars galaxy",
      "thumbnail": null,
      "url": "https://en.wikipedia.org/wiki/Tatooine",
      "domain": "en.wikipedia.org",
      "estimated_traffic": 5000,
      "total_keywords": 150,
      "top_keyword": "tatooine",
      "top_keyword_position": 3,
      "top_keyword_volume": 12000,
      "highlighted": false
    },
    {
      "object": "competitor",
      "id": 2,
      "position": 2,
      "title": "Tatooine | Wookieepedia",
      "description": "Complete guide to Tatooine's history and locations",
      "thumbnail": null,
      "url": "https://starwars.fandom.com/wiki/Tatooine",
      "domain": "en.wikipedia.org",
      "estimated_traffic": 3200,
      "total_keywords": 98,
      "top_keyword": "tatooine locations",
      "top_keyword_position": 5,
      "top_keyword_volume": 8500,
      "highlighted": true
    }
  ]
}

Parameters

No parameters.

Returns

Returns external authoritative links that appear across multiple top-ranking pages for this keyword. These are links commonly cited as references or resources by content that ranks well for the topic.

The response includes:

  • metadata - Contains total_count, highlighted_count, and sharing links
  • referenced_links - Array of link objects ordered by total_occurrences_count (descending), each containing:
    • id - Unique identifier
    • url - The referenced URL
    • total_occurrences_count - Number of times this link appears across top-ranking content
    • highlighted - Whether link was highlighted by user

Note: Links are ordered by total_occurrences_count (descending), showing the most commonly referenced sources first.

Request

GET /api/v2/content_workflows/1/referenced_links
curl https://app.contentharmony.com/api/v2/content_workflows/1/referenced_links \
  -X GET \
  -u {{token}}:{{secret}} \
  -H 'Content-Type: application/json'
fetch('https://app.contentharmony.com/api/v2/content_workflows/1/referenced_links', {
  method: 'GET',
  headers: {
    'Authorization': `Basic ${btoa('{{token}}:<span class="password-hidden" data-reveal-target="maskable">{{secret}}</span>')}`,
    'Content-Type': 'application/json'
  }
})
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(error => console.error('Error:', error));
import requests
import json

url = 'https://app.contentharmony.com/api/v2/content_workflows/1/referenced_links'
auth = ('{{token}}', '{{secret}}')
headers = {'Content-Type': 'application/json'}

response = requests.get(url, auth=auth, headers=headers)
print(response.json())
require 'net/http'
require 'json'

uri = URI('https://app.contentharmony.com/api/v2/content_workflows/1/referenced_links')
request = Net::HTTP::Get.new(uri)
request.basic_auth('{{token}}', '{{secret}}')
request['Content-Type'] = 'application/json'

response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
  http.request(request)
end

puts JSON.parse(response.body)

Response

{
  "object": "list",
  "metadata": {
    "total_count": 2,
    "highlighted_count": 1,
    "links": [
      {
        "object": "link",
        "id": null,
        "show_discussion": true,
        "url": "https://app.contentharmony.com/l/sharediscussion01/sources"
      },
      {
        "object": "link",
        "id": null,
        "show_discussion": false,
        "url": "https://app.contentharmony.com/l/sharereadonly0001/sources"
      }
    ]
  },
  "referenced_links": [
    {
      "object": "referenced_link",
      "id": 1,
      "url": "https://starwars.fandom.com/wiki/Tusken_Raiders",
      "total_occurrences_count": 15,
      "highlighted": true
    },
    {
      "object": "referenced_link",
      "id": 2,
      "url": "https://starwars.fandom.com/wiki/Moisture_farming",
      "total_occurrences_count": 8,
      "highlighted": false
    }
  ]
}

Parameters

No parameters.

Returns

Returns internal pages from your own domain that rank for this keyword. This helps identify which existing content on your site is already performing well for the topic.

The response includes:

  • metadata - Contains total_count, highlighted_count, and sharing links
  • internal_links - Array of internal page objects ordered by source_position, each containing:
    • id - Unique identifier
    • url - The page URL
    • title - Page title from search results
    • snippet - Meta description or snippet text
    • displayed_link - Display URL from search results
    • source_position - Ranking position in search results
    • source_name - Source identifier (optional)
    • date - Publication date (optional)
    • thumbnail - Thumbnail image URL (optional)
    • highlighted - Whether page was highlighted by user

Note: Only available if a project domain is configured. Internal links are ordered by source_position (ranking).

Request

GET /api/v2/content_workflows/1/internal_links
curl https://app.contentharmony.com/api/v2/content_workflows/1/internal_links \
  -X GET \
  -u {{token}}:{{secret}} \
  -H 'Content-Type: application/json'
fetch('https://app.contentharmony.com/api/v2/content_workflows/1/internal_links', {
  method: 'GET',
  headers: {
    'Authorization': `Basic ${btoa('{{token}}:<span class="password-hidden" data-reveal-target="maskable">{{secret}}</span>')}`,
    'Content-Type': 'application/json'
  }
})
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(error => console.error('Error:', error));
import requests
import json

url = 'https://app.contentharmony.com/api/v2/content_workflows/1/internal_links'
auth = ('{{token}}', '{{secret}}')
headers = {'Content-Type': 'application/json'}

response = requests.get(url, auth=auth, headers=headers)
print(response.json())
require 'net/http'
require 'json'

uri = URI('https://app.contentharmony.com/api/v2/content_workflows/1/internal_links')
request = Net::HTTP::Get.new(uri)
request.basic_auth('{{token}}', '{{secret}}')
request['Content-Type'] = 'application/json'

response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
  http.request(request)
end

puts JSON.parse(response.body)

Response

{
  "object": "list",
  "metadata": {
    "total_count": 2,
    "highlighted_count": 1,
    "links": [
      {
        "object": "link",
        "id": null,
        "show_discussion": true,
        "url": "https://app.contentharmony.com/l/sharediscussion01/sources"
      },
      {
        "object": "link",
        "id": null,
        "show_discussion": false,
        "url": "https://app.contentharmony.com/l/sharereadonly0001/sources"
      }
    ]
  },
  "internal_links": [
    {
      "object": "source",
      "id": 1,
      "url": "https://mysite.com/tatooine-guide",
      "title": "Complete Guide to Tatooine",
      "snippet": "Everything you need to know about the desert planet...",
      "displayed_link": "mysite.com/tatooine-guide",
      "source_position": 1,
      "source_name": null,
      "date": null,
      "thumbnail": null,
      "highlighted": false
    },
    {
      "object": "source",
      "id": 2,
      "url": "https://mysite.com/outer-rim-planets",
      "title": "Outer Rim Planets: Tatooine",
      "snippet": "Exploring the most iconic desert world in Star Wars...",
      "displayed_link": "mysite.com/outer-rim-planets",
      "source_position": 2,
      "source_name": null,
      "date": null,
      "thumbnail": null,
      "highlighted": true
    }
  ]
}

List government and educational sources for a content workflow

Parameters

No parameters.

Returns

Returns authoritative content from .gov and .edu domains related to this keyword. These sources are often highly trusted and can provide credible references for your content.

The response includes:

  • metadata - Contains total_count, highlighted_count, and sharing links
  • gov_edu_sources - Array of source objects ordered by source_position, each containing:
    • id - Unique identifier
    • url - The source URL
    • title - Page title from search results
    • snippet - Description or excerpt text
    • displayed_link - Display URL from search results
    • source_position - Order in search results
    • source_name - Source identifier (optional)
    • date - Publication date (optional)
    • thumbnail - Thumbnail image URL (optional)
    • highlighted - Whether source was highlighted by user

Note: Sources are ordered by source_position from the search results.

Request

GET /api/v2/content_workflows/1/gov_edu_sources
curl https://app.contentharmony.com/api/v2/content_workflows/1/gov_edu_sources \
  -X GET \
  -u {{token}}:{{secret}} \
  -H 'Content-Type: application/json'
fetch('https://app.contentharmony.com/api/v2/content_workflows/1/gov_edu_sources', {
  method: 'GET',
  headers: {
    'Authorization': `Basic ${btoa('{{token}}:<span class="password-hidden" data-reveal-target="maskable">{{secret}}</span>')}`,
    'Content-Type': 'application/json'
  }
})
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(error => console.error('Error:', error));
import requests
import json

url = 'https://app.contentharmony.com/api/v2/content_workflows/1/gov_edu_sources'
auth = ('{{token}}', '{{secret}}')
headers = {'Content-Type': 'application/json'}

response = requests.get(url, auth=auth, headers=headers)
print(response.json())
require 'net/http'
require 'json'

uri = URI('https://app.contentharmony.com/api/v2/content_workflows/1/gov_edu_sources')
request = Net::HTTP::Get.new(uri)
request.basic_auth('{{token}}', '{{secret}}')
request['Content-Type'] = 'application/json'

response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
  http.request(request)
end

puts JSON.parse(response.body)

Response

{
  "object": "list",
  "metadata": {
    "total_count": 2,
    "highlighted_count": 1,
    "links": [
      {
        "object": "link",
        "id": null,
        "show_discussion": true,
        "url": "https://app.contentharmony.com/l/sharediscussion01/sources"
      },
      {
        "object": "link",
        "id": null,
        "show_discussion": false,
        "url": "https://app.contentharmony.com/l/sharereadonly0001/sources"
      }
    ]
  },
  "gov_edu_sources": [
    {
      "object": "source",
      "id": 1,
      "url": "https://climate.nasa.gov/effects",
      "title": "The Effects of Climate Change - NASA",
      "snippet": "Scientific evidence shows climate change is affecting...",
      "displayed_link": "climate.nasa.gov/effects",
      "source_position": 1,
      "source_name": null,
      "date": null,
      "thumbnail": null,
      "highlighted": true
    },
    {
      "object": "source",
      "id": 2,
      "url": "https://university.edu/climate-research",
      "title": "Climate Change Research - University Study",
      "snippet": "Academic research on climate impacts and projections...",
      "displayed_link": "university.edu/climate-research",
      "source_position": 2,
      "source_name": null,
      "date": null,
      "thumbnail": null,
      "highlighted": false
    }
  ]
}

List news sources for a content workflow

Parameters

No parameters.

Returns

Returns recent news articles and coverage related to this keyword. News sources can help identify trending topics, breaking developments, and timely content opportunities.

The response includes:

  • metadata - Contains total_count, highlighted_count, and sharing links
  • news_sources - Array of news article objects ordered by source_position, each containing:
    • id - Unique identifier
    • url - The article URL
    • title - Article headline
    • snippet - Article excerpt or description
    • displayed_link - Display URL from search results
    • source_position - Order in news results
    • source_name - News outlet name (e.g., "CNN", "BBC News")
    • date - Publication date/time (e.g., "2 hours ago")
    • thumbnail - Article thumbnail image URL (optional)
    • highlighted - Whether article was highlighted by user

Note: News sources are ordered by source_position (relevance/recency) from Google News results.

Request

GET /api/v2/content_workflows/1/news_sources
curl https://app.contentharmony.com/api/v2/content_workflows/1/news_sources \
  -X GET \
  -u {{token}}:{{secret}} \
  -H 'Content-Type: application/json'
fetch('https://app.contentharmony.com/api/v2/content_workflows/1/news_sources', {
  method: 'GET',
  headers: {
    'Authorization': `Basic ${btoa('{{token}}:<span class="password-hidden" data-reveal-target="maskable">{{secret}}</span>')}`,
    'Content-Type': 'application/json'
  }
})
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(error => console.error('Error:', error));
import requests
import json

url = 'https://app.contentharmony.com/api/v2/content_workflows/1/news_sources'
auth = ('{{token}}', '{{secret}}')
headers = {'Content-Type': 'application/json'}

response = requests.get(url, auth=auth, headers=headers)
print(response.json())
require 'net/http'
require 'json'

uri = URI('https://app.contentharmony.com/api/v2/content_workflows/1/news_sources')
request = Net::HTTP::Get.new(uri)
request.basic_auth('{{token}}', '{{secret}}')
request['Content-Type'] = 'application/json'

response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
  http.request(request)
end

puts JSON.parse(response.body)

Response

{
  "object": "list",
  "metadata": {
    "total_count": 2,
    "highlighted_count": 1,
    "links": [
      {
        "object": "link",
        "id": null,
        "show_discussion": true,
        "url": "https://app.contentharmony.com/l/sharediscussion01/sources"
      },
      {
        "object": "link",
        "id": null,
        "show_discussion": false,
        "url": "https://app.contentharmony.com/l/sharereadonly0001/sources"
      }
    ]
  },
  "news_sources": [
    {
      "object": "source",
      "id": 1,
      "url": "https://news.com/ev-breakthrough",
      "title": "Major Breakthrough in Electric Vehicle Technology",
      "snippet": "Scientists announce new battery technology that could triple EV range...",
      "displayed_link": "news.com",
      "source_position": 1,
      "source_name": "Tech News Daily",
      "date": "2 hours ago",
      "thumbnail": "https://news.com/images/ev-thumbnail.jpg",
      "highlighted": false
    },
    {
      "object": "source",
      "id": 2,
      "url": "https://news.com/battery-technology",
      "title": "New Battery Tech Could Transform Electric Vehicles",
      "snippet": "Industry experts say the innovation marks a turning point...",
      "displayed_link": "news.com",
      "source_position": 2,
      "source_name": "Auto Industry Report",
      "date": "5 hours ago",
      "thumbnail": null,
      "highlighted": true
    }
  ]
}

List AI-suggested images for a content workflow

Parameters

No parameters.

Returns

Returns AI-generated suggestions for images that would be effective for this keyword. These are text-only recommendations (title and description) suggesting types of images to create or source.

The response includes:

  • metadata - Contains total_count, highlighted_count, and sharing links
  • suggested_images - Array of AI suggestion objects, each containing:
    • id - Unique identifier
    • title - Brief title of the image suggestion
    • description - Detailed description of the suggested image
    • highlighted - Whether suggestion was highlighted by user

Note: These are text-based suggestions, not actual images. Use them as guidance for creating or sourcing visual content.

Request

GET /api/v2/content_workflows/1/suggested_images
curl https://app.contentharmony.com/api/v2/content_workflows/1/suggested_images \
  -X GET \
  -u {{token}}:{{secret}} \
  -H 'Content-Type: application/json'
fetch('https://app.contentharmony.com/api/v2/content_workflows/1/suggested_images', {
  method: 'GET',
  headers: {
    'Authorization': `Basic ${btoa('{{token}}:<span class="password-hidden" data-reveal-target="maskable">{{secret}}</span>')}`,
    'Content-Type': 'application/json'
  }
})
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(error => console.error('Error:', error));
import requests
import json

url = 'https://app.contentharmony.com/api/v2/content_workflows/1/suggested_images'
auth = ('{{token}}', '{{secret}}')
headers = {'Content-Type': 'application/json'}

response = requests.get(url, auth=auth, headers=headers)
print(response.json())
require 'net/http'
require 'json'

uri = URI('https://app.contentharmony.com/api/v2/content_workflows/1/suggested_images')
request = Net::HTTP::Get.new(uri)
request.basic_auth('{{token}}', '{{secret}}')
request['Content-Type'] = 'application/json'

response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
  http.request(request)
end

puts JSON.parse(response.body)

Response

{
  "object": "list",
  "metadata": {
    "total_count": 2,
    "highlighted_count": 1,
    "links": [
      {
        "object": "link",
        "id": null,
        "show_discussion": true,
        "url": "https://app.contentharmony.com/l/sharediscussion01/images"
      },
      {
        "object": "link",
        "id": null,
        "show_discussion": false,
        "url": "https://app.contentharmony.com/l/sharereadonly0001/images"
      }
    ]
  },
  "suggested_images": [
    {
      "object": "suggested_image",
      "id": 1,
      "title": "Tatooine Desert Landscape",
      "description": "Show the iconic twin suns setting over Tatooine's vast desert dunes to demonstrate the planet's harsh environment and iconic scenery.",
      "highlighted": false
    },
    {
      "object": "suggested_image",
      "id": 2,
      "title": "Mos Eisley Spaceport Aerial View",
      "description": "Display the famous Mos Eisley spaceport from above, highlighting the distinctive architecture and bustling activity of this notorious trading hub.",
      "highlighted": true
    }
  ]
}

List image search results for a content workflow

Parameters

No parameters.

Returns

Returns images from image search results for this keyword. These are actual images that rank in image search for the target keyword.

The response includes:

  • metadata - Contains total_count, highlighted_count, and sharing links
  • google_image_results - Array of image result objects ordered by source_position, each containing:
    • id - Unique identifier
    • url - The source page URL containing the image
    • title - Image title from search results
    • snippet - Description or context
    • displayed_link - Display URL from search results
    • source_position - Ranking position in image results
    • source_name - Source website name (optional)
    • date - Publication date (optional)
    • thumbnail - Thumbnail image URL
    • image - Full-size image URL
    • highlighted - Whether image was highlighted by user

Note: Results are ordered by source_position (ranking in image search results).

Request

GET /api/v2/content_workflows/1/google_image_results
curl https://app.contentharmony.com/api/v2/content_workflows/1/google_image_results \
  -X GET \
  -u {{token}}:{{secret}} \
  -H 'Content-Type: application/json'
fetch('https://app.contentharmony.com/api/v2/content_workflows/1/google_image_results', {
  method: 'GET',
  headers: {
    'Authorization': `Basic ${btoa('{{token}}:<span class="password-hidden" data-reveal-target="maskable">{{secret}}</span>')}`,
    'Content-Type': 'application/json'
  }
})
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(error => console.error('Error:', error));
import requests
import json

url = 'https://app.contentharmony.com/api/v2/content_workflows/1/google_image_results'
auth = ('{{token}}', '{{secret}}')
headers = {'Content-Type': 'application/json'}

response = requests.get(url, auth=auth, headers=headers)
print(response.json())
require 'net/http'
require 'json'

uri = URI('https://app.contentharmony.com/api/v2/content_workflows/1/google_image_results')
request = Net::HTTP::Get.new(uri)
request.basic_auth('{{token}}', '{{secret}}')
request['Content-Type'] = 'application/json'

response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
  http.request(request)
end

puts JSON.parse(response.body)

Response

{
  "object": "list",
  "metadata": {
    "total_count": 2,
    "highlighted_count": 1,
    "links": [
      {
        "object": "link",
        "id": null,
        "show_discussion": true,
        "url": "https://app.contentharmony.com/l/sharediscussion01/images"
      },
      {
        "object": "link",
        "id": null,
        "show_discussion": false,
        "url": "https://app.contentharmony.com/l/sharereadonly0001/images"
      }
    ]
  },
  "google_image_results": [
    {
      "object": "image_result",
      "id": 1,
      "url": "https://en.wikipedia.org/wiki/Tatooine",
      "title": "Tatooine Desert Landscape",
      "snippet": "The harsh twin-sun desert world of Tatooine",
      "displayed_link": "example.com",
      "source_position": 1,
      "source_name": null,
      "date": null,
      "thumbnail": "https://example.com/images/tatooine-thumb.jpg",
      "image": "https://example.com/images/tatooine-full.jpg",
      "highlighted": true
    },
    {
      "object": "image_result",
      "id": 2,
      "url": "https://starwars.fandom.com/wiki/Tatooine/Legends",
      "title": "Twin Suns of Tatooine",
      "snippet": "Iconic binary sunset on the desert planet",
      "displayed_link": "example.com",
      "source_position": 2,
      "source_name": null,
      "date": null,
      "thumbnail": "https://example.com/images/twin-suns-thumb.jpg",
      "image": "https://example.com/images/twin-suns-full.jpg",
      "highlighted": false
    }
  ]
}

List images from competing pages

Parameters

No parameters.

Returns

Returns actual images found on top-ranking competitor pages for this keyword. This shows you what visual content is being used by pages that rank well.

The response includes:

  • metadata - Contains sharing links
  • competitors - Array of competitor objects ordered by ranking position, each containing:
    • position - Ranking position in search results
    • url - The competitor page URL
    • title - Page title from search results
    • images - Array of image URLs found on this page (up to 24 images)
      • Absolute URLs (http/https) are returned as-is
      • Relative URLs are converted to absolute using the page's domain
      • Invalid images (data URIs, SVGs, broken paths) are filtered out

Note: Images are extracted from successfully crawled competitor pages. Empty arrays indicate no images were found or the page could not be crawled.

Request

GET /api/v2/content_workflows/1/competitor_images
curl https://app.contentharmony.com/api/v2/content_workflows/1/competitor_images \
  -X GET \
  -u {{token}}:{{secret}} \
  -H 'Content-Type: application/json'
fetch('https://app.contentharmony.com/api/v2/content_workflows/1/competitor_images', {
  method: 'GET',
  headers: {
    'Authorization': `Basic ${btoa('{{token}}:<span class="password-hidden" data-reveal-target="maskable">{{secret}}</span>')}`,
    'Content-Type': 'application/json'
  }
})
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(error => console.error('Error:', error));
import requests
import json

url = 'https://app.contentharmony.com/api/v2/content_workflows/1/competitor_images'
auth = ('{{token}}', '{{secret}}')
headers = {'Content-Type': 'application/json'}

response = requests.get(url, auth=auth, headers=headers)
print(response.json())
require 'net/http'
require 'json'

uri = URI('https://app.contentharmony.com/api/v2/content_workflows/1/competitor_images')
request = Net::HTTP::Get.new(uri)
request.basic_auth('{{token}}', '{{secret}}')
request['Content-Type'] = 'application/json'

response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
  http.request(request)
end

puts JSON.parse(response.body)

Response

{
  "object": "list",
  "metadata": {
    "links": [
      {
        "object": "link",
        "id": null,
        "show_discussion": true,
        "url": "https://app.contentharmony.com/l/sharediscussion01/images"
      },
      {
        "object": "link",
        "id": null,
        "show_discussion": false,
        "url": "https://app.contentharmony.com/l/sharereadonly0001/images"
      }
    ]
  },
  "competitors": [
    {
      "object": "competitor",
      "position": 1,
      "url": "https://competitor1.com/tatooine-guide",
      "title": "Complete Guide to Tatooine: Everything You Need to Know",
      "images": []
    },
    {
      "object": "competitor",
      "position": 2,
      "url": "https://competitor2.com/desert-planet-living",
      "title": "Living on Desert Planets: A Comprehensive Guide",
      "images": []
    }
  ]
}

List shopping product images for a content workflow

Parameters

No parameters.

Returns

Returns product images from Google Shopping results for this keyword. These show actual products being sold that match the search term.

The response includes:

  • metadata - Contains sharing links
  • shopping_images - Array of shopping product objects (up to 8 results), each containing:
    • thumbnail - Product image thumbnail URL
    • price - Product price as displayed
    • link - Link to product page
    • source - Merchant or store name
    • title - Product title

Note: Shopping images are only available when Google Shopping results exist for the keyword. Returns empty array if no shopping results are found.

Request

GET /api/v2/content_workflows/1/shopping_images
curl https://app.contentharmony.com/api/v2/content_workflows/1/shopping_images \
  -X GET \
  -u {{token}}:{{secret}} \
  -H 'Content-Type: application/json'
fetch('https://app.contentharmony.com/api/v2/content_workflows/1/shopping_images', {
  method: 'GET',
  headers: {
    'Authorization': `Basic ${btoa('{{token}}:<span class="password-hidden" data-reveal-target="maskable">{{secret}}</span>')}`,
    'Content-Type': 'application/json'
  }
})
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(error => console.error('Error:', error));
import requests
import json

url = 'https://app.contentharmony.com/api/v2/content_workflows/1/shopping_images'
auth = ('{{token}}', '{{secret}}')
headers = {'Content-Type': 'application/json'}

response = requests.get(url, auth=auth, headers=headers)
print(response.json())
require 'net/http'
require 'json'

uri = URI('https://app.contentharmony.com/api/v2/content_workflows/1/shopping_images')
request = Net::HTTP::Get.new(uri)
request.basic_auth('{{token}}', '{{secret}}')
request['Content-Type'] = 'application/json'

response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
  http.request(request)
end

puts JSON.parse(response.body)

Response

{
  "object": "list",
  "metadata": {
    "links": [
      {
        "object": "link",
        "id": null,
        "show_discussion": true,
        "url": "https://app.contentharmony.com/l/sharediscussion01/images"
      },
      {
        "object": "link",
        "id": null,
        "show_discussion": false,
        "url": "https://app.contentharmony.com/l/sharereadonly0001/images"
      }
    ]
  },
  "shopping_images": [
    {
      "object": "shopping_image",
      "thumbnail": "https://shopping.example.com/images/tatooine-poster-thumb.jpg",
      "price": "$29.99",
      "link": "https://shopping.example.com/products/tatooine-poster",
      "source": "Star Wars Shop",
      "title": "Tatooine Landscape Poster - 24x36"
    },
    {
      "object": "shopping_image",
      "thumbnail": "https://shopping.example.com/images/tatooine-map-thumb.jpg",
      "price": "$19.99",
      "link": "https://shopping.example.com/products/tatooine-map",
      "source": "Galactic Collectibles",
      "title": "Detailed Map of Tatooine"
    }
  ]
}

List videos for a content workflow

Parameters

No parameters.

Returns

Returns video search results from Google Video Search for this keyword. These are videos that rank for the target keyword.

The response includes:

  • metadata - Contains total_count, highlighted_count, and sharing links
  • videos - Array of video objects ordered by ranking position, each containing:
    • id - Unique identifier for the video result
    • url - Full URL to the video
    • title - Video title from search results
    • snippet - Video description snippet
    • displayed_link - Domain displayed in search results
    • source_position - Ranking position in video search results
    • source_name - Video platform name (e.g., "YouTube", "Vimeo")
    • date - Publication or upload date if available
    • rich_snippet - Additional metadata (duration, views, etc.)
    • highlighted - Boolean indicating if video was highlighted

Note: Videos are only available when Google Video Search results exist for the keyword. Returns empty array if no video results are found.

Request

GET /api/v2/content_workflows/1/videos
curl https://app.contentharmony.com/api/v2/content_workflows/1/videos \
  -X GET \
  -u {{token}}:{{secret}} \
  -H 'Content-Type: application/json'
fetch('https://app.contentharmony.com/api/v2/content_workflows/1/videos', {
  method: 'GET',
  headers: {
    'Authorization': `Basic ${btoa('{{token}}:<span class="password-hidden" data-reveal-target="maskable">{{secret}}</span>')}`,
    'Content-Type': 'application/json'
  }
})
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(error => console.error('Error:', error));
import requests
import json

url = 'https://app.contentharmony.com/api/v2/content_workflows/1/videos'
auth = ('{{token}}', '{{secret}}')
headers = {'Content-Type': 'application/json'}

response = requests.get(url, auth=auth, headers=headers)
print(response.json())
require 'net/http'
require 'json'

uri = URI('https://app.contentharmony.com/api/v2/content_workflows/1/videos')
request = Net::HTTP::Get.new(uri)
request.basic_auth('{{token}}', '{{secret}}')
request['Content-Type'] = 'application/json'

response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
  http.request(request)
end

puts JSON.parse(response.body)

Response

{
  "object": "list",
  "metadata": {
    "total_count": 3,
    "highlighted_count": 1,
    "links": [
      {
        "object": "link",
        "id": null,
        "show_discussion": true,
        "url": "https://app.contentharmony.com/l/sharediscussion01/videos"
      },
      {
        "object": "link",
        "id": null,
        "show_discussion": false,
        "url": "https://app.contentharmony.com/l/sharereadonly0001/videos"
      }
    ]
  },
  "videos": [
    {
      "object": "video",
      "id": 1,
      "url": "https://www.youtube.com/watch?v=abc123",
      "title": "Complete History of Tatooine - YouTube",
      "snippet": "Explore the complete history of Tatooine from its discovery to its role in the Skywalker saga",
      "displayed_link": "youtube.com",
      "source_position": 1,
      "source_name": "YouTube",
      "date": "Jan 15, 2024",
      "rich_snippet": {
        "top": {
          "extensions": [
            "12:15",
            "2.3M views"
          ]
        }
      },
      "highlighted": true
    },
    {
      "object": "video",
      "id": 2,
      "url": "https://www.youtube.com/watch?v=def456",
      "title": "Visiting Tatooine Filming Locations - YouTube",
      "snippet": "Join us as we explore the real-world filming locations in Tunisia used for Tatooine scenes",
      "displayed_link": "youtube.com",
      "source_position": 2,
      "source_name": "YouTube",
      "date": "Feb 1, 2024",
      "rich_snippet": {
        "top": {
          "extensions": [
            "18:42",
            "890K views"
          ]
        }
      },
      "highlighted": false
    },
    {
      "object": "video",
      "id": 3,
      "url": "https://vimeo.com/987654321",
      "title": "Tatooine: A Geographic Analysis",
      "snippet": "Analyzing the geography, climate, and settlements of Star Wars' most iconic desert planet",
      "displayed_link": "vimeo.com",
      "source_position": 3,
      "source_name": "Vimeo",
      "date": "Dec 20, 2023",
      "rich_snippet": {
        "top": {
          "extensions": [
            "15:30",
            "450K views"
          ]
        }
      },
      "highlighted": false
    }
  ]
}

Get content brief for a content workflow

Parameters

No parameters.

Returns

Returns the editable content brief for this content workflow as HTML. The brief contains user-written or AI-generated content structured in a rich text format.

The HTML content is formatted for easy import into Google Docs, Microsoft Word, or other rich text editors.

  • object - Always "brief"
  • keyword_brief_id - ID of the associated content workflow
  • id - Unique identifier for the brief (null if no brief exists)
  • created_at - When the brief was first created (null if no brief exists)
  • updated_at - When the brief was last modified (null if no brief exists)
  • html_content - The brief content converted to HTML (null if no content)
  • has_content - Boolean indicating whether the brief has any content
  • message - Informational message when no brief exists

Note: Not all content workflows have associated content briefs. If no brief has been created yet, the endpoint will return null values with has_content: false.

The HTML content includes proper formatting for headings, paragraphs, lists, tables, code blocks, and other rich text elements. It also may include data from the keyword report if the user highlighted items like competitors, topics, or questions to include in their brief.

Request

GET /api/v2/content_workflows/1/brief
curl https://app.contentharmony.com/api/v2/content_workflows/1/brief \
  -X GET \
  -u {{token}}:{{secret}} \
  -H 'Content-Type: application/json'
fetch('https://app.contentharmony.com/api/v2/content_workflows/1/brief', {
  method: 'GET',
  headers: {
    'Authorization': `Basic ${btoa('{{token}}:<span class="password-hidden" data-reveal-target="maskable">{{secret}}</span>')}`,
    'Content-Type': 'application/json'
  }
})
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(error => console.error('Error:', error));
import requests
import json

url = 'https://app.contentharmony.com/api/v2/content_workflows/1/brief'
auth = ('{{token}}', '{{secret}}')
headers = {'Content-Type': 'application/json'}

response = requests.get(url, auth=auth, headers=headers)
print(response.json())
require 'net/http'
require 'json'

uri = URI('https://app.contentharmony.com/api/v2/content_workflows/1/brief')
request = Net::HTTP::Get.new(uri)
request.basic_auth('{{token}}', '{{secret}}')
request['Content-Type'] = 'application/json'

response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
  http.request(request)
end

puts JSON.parse(response.body)

Response

{
  "object": "brief",
  "keyword_brief_id": 1,
  "id": 1,
  "created_at": "2026-07-16T20:02:47.738Z",
  "updated_at": "2026-07-17T19:02:47.738Z",
  "html_content": "<h1>Complete Guide to Tatooine</h1><p>Tatooine, a remote desert planet in the Outer Rim, orbits twin suns and presents unique challenges for settlers and moisture farmers.</p><h2>Essential Survival Equipment</h2><ul><li>Moisture vaporators for water collection</li><li>Protection from twin suns heat</li><li>Sand filtration systems</li><li>Long-range communication devices</li></ul><h2>Top Moisture Farming Locations</h2><ol><li>Anchorhead region - Best moisture yield</li><li>Bestine Township - Safest from Tusken Raiders</li><li>Mos Eisley outskirts - Good trade access</li></ol>",
  "has_content": true
}

Get content grader for a content workflow

Parameters

No parameters.

Returns

Returns the content grader data for a content workflow, including scoring metrics, draft content, and topic usage analysis. The content grader helps evaluate how well your content matches SEO best practices and competitor patterns.

This endpoint combines content scoring, the draft HTML, and topic analysis in a single response.

  • object - Always "content_grader"
  • keyword_brief_id - ID of the associated content workflow
  • id - Unique identifier for the grader (null if no grader exists)
  • metadata - Contains sharing links pointing to the /grader view
    • links - Array of sharing links with id, show_discussion, and url (points to /grader tab)
  • content_scoring - Object containing all scoring metrics:
    • content_score - Overall score (0-100)
    • content_score_label - Label ("Poor", "Fair", "Good", "Great", "Greatest 💯")
    • content_score_label_color - CSS class for score color
    • current_word_count - Word count in the draft
    • recommended_word_count - Target word count based on competitors
    • current_readability - Flesch-Kincaid readability score
    • current_readability_label - Grade level label (e.g., "8th Grade")
    • recommended_readability - Target readability score
    • recommended_terms_used - Number of recommended topics used in content
    • recommended_terms_total - Total number of recommended topics
  • content_draft - Object containing the draft content:
    • title - Title of the draft
    • html_content - Full HTML content of the draft
    • url - Source URL if content was fetched from a competitor (null otherwise)
    • has_content - Boolean indicating whether draft has content
  • topics - Object containing topic analysis:
    • metadata - Contains total_count and highlighted_count
    • items - Array of topics (same structure as Get topics endpoint), with additional content_grader_uses field showing how many times each topic appears in the draft

Note: Not all content workflows have content graders. If no grader exists, the endpoint returns null values with empty scoring and draft objects.

The content score is calculated based on word count (10% weight) and topic usage (90% weight). A score of 71+ is considered "Great".

The content_grader_uses field in topics shows how many times each topic/keyword appears in the draft content, helping you identify which recommended topics you've covered.

Request

GET /api/v2/content_workflows/1/content_grader
curl https://app.contentharmony.com/api/v2/content_workflows/1/content_grader \
  -X GET \
  -u {{token}}:{{secret}} \
  -H 'Content-Type: application/json'
fetch('https://app.contentharmony.com/api/v2/content_workflows/1/content_grader', {
  method: 'GET',
  headers: {
    'Authorization': `Basic ${btoa('{{token}}:<span class="password-hidden" data-reveal-target="maskable">{{secret}}</span>')}`,
    'Content-Type': 'application/json'
  }
})
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(error => console.error('Error:', error));
import requests
import json

url = 'https://app.contentharmony.com/api/v2/content_workflows/1/content_grader'
auth = ('{{token}}', '{{secret}}')
headers = {'Content-Type': 'application/json'}

response = requests.get(url, auth=auth, headers=headers)
print(response.json())
require 'net/http'
require 'json'

uri = URI('https://app.contentharmony.com/api/v2/content_workflows/1/content_grader')
request = Net::HTTP::Get.new(uri)
request.basic_auth('{{token}}', '{{secret}}')
request['Content-Type'] = 'application/json'

response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
  http.request(request)
end

puts JSON.parse(response.body)

Response

{
  "object": "content_grader",
  "keyword_brief_id": 1,
  "id": 1,
  "created_at": "2026-07-10T20:02:47.744Z",
  "updated_at": "2026-07-17T19:02:47.744Z",
  "metadata": {
    "links": [
      {
        "object": "link",
        "id": 1,
        "show_discussion": true,
        "url": "https://app.contentharmony.com/l/abc123def456/grader"
      },
      {
        "object": "link",
        "id": 2,
        "show_discussion": false,
        "url": "https://app.contentharmony.com/l/xyz789ghi012/grader"
      }
    ]
  },
  "content_scoring": {
    "content_score": 78,
    "content_score_label": "Great",
    "content_score_label_color": "grader-great",
    "current_word_count": 487,
    "recommended_word_count": 650,
    "current_readability": 68,
    "current_readability_label": "8th Grade",
    "recommended_readability": 70,
    "recommended_terms_used": 7,
    "recommended_terms_total": 10
  },
  "content_draft": {
    "title": "Complete Guide to Living on Tatooine",
    "html_content": "<h1>Complete Guide to Living on Tatooine</h1><p>Tatooine, the desert planet orbiting twin suns, presents unique challenges for settlers and moisture farmers. This comprehensive guide covers everything you need to know about surviving and thriving in this harsh environment.</p><h2>Essential Moisture Farming Equipment</h2><p>Moisture farming on Tatooine requires specialized vaporators that extract water from the atmosphere. Quality moisture farming equipment can mean the difference between prosperity and failure in the desert.</p><h2>Key Survival Tips</h2><ul><li>Maintain your vaporator maintenance schedule</li><li>Avoid traveling during the hottest hours</li><li>Watch for Tusken Raider activity</li><li>Keep sandcrawler parts in stock for repairs</li></ul><p>By following these guidelines, you can successfully establish your homestead on Tatooine.</p>",
    "url": "https://example.com/tatooine-settlers-guide",
    "has_content": true
  },
  "topics": {
    "metadata": {
      "total_count": 3,
      "highlighted_count": 1
    },
    "items": [
      {
        "object": "topic",
        "id": 1,
        "phrase": "moisture farming equipment",
        "documents_count": 8,
        "total_occurrences_count": 42,
        "priority": 85,
        "variants": [
          "moisture vaporators",
          "water collection equipment"
        ],
        "highlighted": true,
        "ignored": false,
        "content_grader_uses": 3
      },
      {
        "object": "topic",
        "id": 2,
        "phrase": "vaporator maintenance",
        "documents_count": 6,
        "total_occurrences_count": 28,
        "priority": 65,
        "variants": [
          "vaporator repair",
          "equipment maintenance"
        ],
        "highlighted": false,
        "ignored": false,
        "content_grader_uses": 2
      },
      {
        "object": "topic",
        "id": 3,
        "phrase": "sandcrawler parts",
        "documents_count": 5,
        "total_occurrences_count": 18,
        "priority": 50,
        "variants": [
          "jawa technology"
        ],
        "highlighted": false,
        "ignored": false,
        "content_grader_uses": 0
      }
    ]
  }
}