API Integration Guide
Written By Founder@SEOProAI
Last updated 6 months ago
SEOPro AI API Integration Guide
This guide shows how to programmatically generate and publish blog posts using SEOPro AI's REST API. Unlike webhooks (which are reactive), this API integration allows you to proactively create content for specific dates and manage your blog publishing workflow.
Overview
The SEOPro AI API provides endpoints to:
β Generate blog posts for specific dates (async, returns immediately)
β Poll for blog generation status
β Fetch draft blogs with complete HTML content
β Publish blog drafts to your platform
β Check blog status by date
β Manage scheduled content
β Get blog generation limits and usage
Async Workflow (Recommended)
Blog generation takes 5-10 minutes, so the API uses an asynchronous workflow:
Initiate Generation β POST
/generate-blog-for-dateβ Returns202 ACCEPTEDimmediatelyPoll for Status β GET
/blog-status-by-dateβ Check ifstatusis"draft"(ready)Fetch Draft Content β GET
/blog-drafts/{blog_id}β Get complete HTML contentPublish to Your Site β POST
/blog-drafts/{blog_id}/publishβ Publish to CMS
This pattern lets you:
Start generation without blocking your application
Poll periodically (every 30-60 seconds) until ready
Fetch the complete HTML content when draft is ready
Publish directly to your third-party website
Authentication
All API requests require authentication using API keys. API keys provide secure, programmatic access to the SEOPro AI API without exposing user credentials.
API Key Format
API keys follow this format:
sk_live_1234567_xxxxxxxxxxxxxxxxxxxxx
Using API Keys
Include your API key in the Authorization header of all requests:
Authorization: Bearer sk_live_1234567_xxxxxxxxxxxxxxxxxxxxx
Generating API Keys
Navigate to Settings: Go to Settings > Integrations in your SEOPro AI dashboard
Developer API Tab: Open the "Developer API" card and select "API Keys" tab
Generate New Key: Click "Generate New Key"
Name Your Key: Give it a descriptive name (e.g., "Production API" or "Node.js Integration")
Copy and Save: Copy the full API key immediately - you won't be able to see it again!
β οΈ Important: Store your API key securely. Never commit it to version control or share it publicly.
Base URL
https://api.seoproai.co/api
Finding Your Website ID
Every API request requires your website_id. Here's how to find it:
From the Dashboard UI (Easiest)
Go to Settings > Integrations > Developer API in your SEOPro AI dashboard
Your Website ID is displayed at the top of the API Keys tab
Click the copy button to copy it to your clipboard
Core API Endpoints
1. Generate Blog for Specific Date (Async)
Important: This endpoint returns immediately with 202 ACCEPTED. Blog generation happens in the background and takes 5-10 minutes.
POST /websites/123/generate-blog-for-date
Content-Type: application/json
Authorization: Bearer sk_live_1234567_xxxxxxxxxxxxxxxxxxxxx
{
"scheduled_date": "2024-01-15",
"keywords": ["your target keyword"],
"language_code": "en"
}
Path Parameters:
website_id(required): Your website ID (e.g.,123) - See above on how to get it
Response (Immediate - 202 ACCEPTED):
{
"status": "processing",
"keyword_id": 789,
"scheduled_date": "2024-01-15",
"message": "Blog generation started. Use /blog-status-by-date to check progress."
}
Next Steps:
Poll
/blog-status-by-dateevery 30-60 seconds to check if blog is readyWhen status is
"draft", fetch the content using/blog-drafts/{blog_id}Publish to your site when ready
2. Publish Blog for Specific Date
Publish an existing blog draft for a specific date:
POST /websites/123/blogs/publish-for-date
Content-Type: application/json
Authorization: Bearer sk_live_1234567_xxxxxxxxxxxxxxxxxxxxx
{
"date": "2024-01-15"
}
Path Parameters:
website_id(required): Your website ID (e.g.,123)
Response:
{
"status": "success",
"blog_id": 12345,
"published_url": "https://yoursite.com/blog/post-slug",
"message": "Blog published successfully"
}
3. Check Blog Status by Date (Polling Endpoint)
Use this endpoint to poll for blog generation progress. Check every 30-60 seconds until status is "draft".
GET /websites/123/blog-status-by-date?start_date=2024-01-01&end_date=2024-01-31
Authorization: Bearer sk_live_1234567_xxxxxxxxxxxxxxxxxxxxx
Path Parameters:
website_id(required): Your website ID (e.g.,123)
Query Parameters:
start_date(required): Start date in YYYY-MM-DD formatend_date(required): End date in YYYY-MM-DD format
Response:
{
"date_statuses": {
"2024-01-15": {
"has_blog": true,
"blog_id": 12345,
"status": "draft",
"title": "Generated Blog Title",
"scheduled_date": "2024-01-15"
},
"2024-01-16": {
"has_blog": false
}
}
}
Blog Status Values:
"processing"- Blog is still being generated (keep polling)"draft"- Blog is ready! Fetch content with/blog-drafts/{blog_id}"published"- Blog has been published to your CMS
4. Fetch Draft Blog Content (For Third-Party Publishing)
Once status is "draft", fetch the complete HTML content to publish on your third-party website.
GET /blog-drafts/{blog_id}
Authorization: Bearer sk_live_1234567_xxxxxxxxxxxxxxxxxxxxx
Response:
{
"id": 12345,
"title": "Your Blog Post Title",
"slug": "your-blog-post-title",
"content": "<h1>Your Blog Post Title</h1><p>Full HTML content here...</p>",
"meta_description": "SEO-optimized meta description",
"focus_keyword": "target keyword",
"status": "draft",
"scheduled_date": "2024-01-15T00:00:00Z",
"created_at": "2024-01-15T10:30:00Z",
"featured_image_url": "https://...",
"word_count": 1500
}
Use the content field to publish the HTML to your website. This is the complete, SEO-optimized article.
5. Mark Blog as Published (Required for API Websites)
When SEOPro publishes directly to a connected CMS (WordPress, HubSpot, Webflow, etc.), we automatically capture the live URL, update the blogs table, create the vector embedding, and refresh backlinks.
If you're using the Developer API to publish content to your own CMS, we don't know the live URL yetβyou have to send it to us so we can store it and use it for creating Backlinks for your new blog post.
Follow these two calls after you publish the HTML on your side:
Send us the published URL
PATCH /blog-drafts/{blog_id} Authorization: Bearer sk_live_1234567_xxxxxxxxxxxxxxxxxxxxx Content-Type: application/json { "published_url": "https://yoursite.com/blog/post-slug" }Stores the final URL in the
blogstable.You can clear the URL later by sending an empty string.
Mark the blog as published
POST /blog-drafts/{blog_id}/publish Authorization: Bearer sk_live_1234567_xxxxxxxxxxxxxxxxxxxxxResponse:
{ "status": "success", "blog_id": 12345, "message": "Blog marked as published" }If you prefer to publish-by-date instead of by
blog_id, you can include the URL in the same way and then callPOST /websites/{website_id}/blogs/publish-for-date.
Tip: Always send the URL before calling the publish endpoint. Without it, SEOPro cannot track the post, generate embeddings, or include it in the backlink network.
Note: Generating a Developer API key (or webhook token) automatically switches the selected websiteβs integration to
API, so SEOPro knows to skip CMS publishing. Any new websites you add while an API key exists will default to the sameAPIintegration, though you can override this anytime under Settings β Integrations.
6. Generate Blog (General)
Generate a blog post with custom parameters:
POST /blogs/generate
Content-Type: application/json
Authorization: Bearer sk_live_1234567_xxxxxxxxxxxxxxxxxxxxx
{
"keywords": ["target keyword", "secondary keyword"],
"website_id": 123,
"language_code": "en",
"keyword_id": 456
}
JavaScript/Node.js Integration Examples
Basic Setup with Async Polling
class SEOProAPI {
constructor(apiKey, baseUrl = 'https://api.seoproai.co/api') {
this.apiKey = apiKey;
this.baseUrl = baseUrl;
}
async request(endpoint, options = {}) {
const url = `${this.baseUrl}${endpoint}`;
const config = {
headers: {
'Authorization': `Bearer ${this.apiKey}`,
'Content-Type': 'application/json',
...options.headers
},
...options
};
const response = await fetch(url, config);
if (!response.ok) {
throw new Error(`API Error: ${response.status} ${response.statusText}`);
}
return await response.json();
}
// Generate blog for specific date (async - returns immediately)
async generateBlogForDate(websiteId, date, keywords, languageCode = 'en') {
return await this.request(`/websites/${websiteId}/generate-blog-for-date`, {
method: 'POST',
body: JSON.stringify({
scheduled_date: date,
keywords: keywords,
language_code: languageCode
})
});
}
// Check blog status for date range (for polling)
async getBlogStatusByDate(websiteId, startDate, endDate) {
const params = new URLSearchParams({
start_date: startDate,
end_date: endDate
});
return await this.request(`/websites/${websiteId}/blog-status-by-date?${params}`);
}
// Fetch draft blog content (complete HTML)
async getBlogDraft(blogId) {
return await this.request(`/blog-drafts/${blogId}`);
}
// Mark blog as published (optional)
async markBlogAsPublished(blogId) {
return await this.request(`/blog-drafts/${blogId}/publish`, {
method: 'POST'
});
}
// Poll for blog completion with timeout
async waitForBlogGeneration(websiteId, date, maxWaitMinutes = 15, pollIntervalSeconds = 30) {
const maxAttempts = (maxWaitMinutes * 60) / pollIntervalSeconds;
let attempts = 0;
while (attempts < maxAttempts) {
attempts++;
// Check blog status
const status = await this.getBlogStatusByDate(websiteId, date, date);
const dateStatus = status.date_statuses?.[date];
if (dateStatus?.has_blog && dateStatus?.status === 'draft') {
console.log(`Blog ready after ${attempts * pollIntervalSeconds} seconds`);
return {
ready: true,
blog_id: dateStatus.blog_id,
title: dateStatus.title
};
}
console.log(`Blog still processing... (attempt ${attempts}/${maxAttempts})`);
// Wait before next poll
await new Promise(resolve => setTimeout(resolve, pollIntervalSeconds * 1000));
}
throw new Error(`Blog generation timeout after ${maxWaitMinutes} minutes`);
}
}
Usage Examples
1. Complete Async Workflow - Generate and Fetch Blog
This example shows the recommended async workflow with polling:
const seoProAPI = new SEOProAPI('sk_live_1234567_xxxxxxxxxxxxxxxxxxxxx');
async function generateAndFetchBlog() {
const websiteId = 123;
const targetDate = '2024-01-15'; // YYYY-MM-DD format
try {
// Step 1: Initiate blog generation (returns immediately)
console.log(`Initiating blog generation for ${targetDate}...`);
const initResult = await seoProAPI.generateBlogForDate(
websiteId,
targetDate,
['your target keyword', 'secondary keyword'],
'en'
);
console.log('Generation started:', initResult);
// Response: { status: "processing", keyword_id: 789, scheduled_date: "2024-01-15" }
// Step 2: Poll for completion (waits up to 15 minutes)
console.log('Waiting for blog generation to complete...');
const result = await seoProAPI.waitForBlogGeneration(
websiteId,
targetDate,
15, // max wait 15 minutes
30 // poll every 30 seconds
);
console.log('Blog is ready!', result);
// Result: { ready: true, blog_id: 12345, title: "Your Generated Title" }
// Step 3: Fetch the complete HTML content
console.log(`Fetching blog content (ID: ${result.blog_id})...`);
const blogDraft = await seoProAPI.getBlogDraft(result.blog_id);
console.log('Blog fetched successfully:');
console.log('- Title:', blogDraft.title);
console.log('- Word Count:', blogDraft.word_count);
console.log('- Meta Description:', blogDraft.meta_description);
console.log('- HTML Content Length:', blogDraft.content.length, 'chars');
// Step 4: Publish the HTML to your third-party website
// (Your custom publishing logic here)
await publishToYourWebsite(blogDraft);
// Step 5: Optionally mark as published in SEOPro
await seoProAPI.markBlogAsPublished(result.blog_id);
return blogDraft;
} catch (error) {
console.error('Error:', error.message);
throw error;
}
}
// Helper function to publish to your website
async function publishToYourWebsite(blogDraft) {
// Example: Publish to your custom CMS API
const response = await fetch('https://your-site.com/api/posts', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer your-cms-token'
},
body: JSON.stringify({
title: blogDraft.title,
content: blogDraft.content,
slug: blogDraft.slug,
meta_description: blogDraft.meta_description,
featured_image: blogDraft.featured_image_url
})
});
if (!response.ok) {
throw new Error('Failed to publish to website');
}
console.log('Published to your website successfully!');
}
// Run it
generateAndFetchBlog();
2. Bulk Generate Blogs (Async, No Waiting)
Generate multiple blogs and track them separately (they generate in parallel):
async function bulkGenerateBlogs() {
const websiteId = 123;
const dates = [
{ date: '2024-01-15', keywords: ['monday keyword', 'productivity'] },
{ date: '2024-01-16', keywords: ['tuesday keyword', 'efficiency'] },
{ date: '2024-01-17', keywords: ['wednesday keyword', 'workflow'] },
{ date: '2024-01-18', keywords: ['thursday keyword', 'automation'] },
{ date: '2024-01-19', keywords: ['friday keyword', 'results'] }
];
const results = [];
// Initiate all generations (they run in parallel in the background)
for (const { date, keywords } of dates) {
try {
console.log(`Initiating blog generation for ${date}...`);
const result = await seoProAPI.generateBlogForDate(
websiteId,
date,
keywords
);
results.push({
date: date,
status: 'initiated',
keyword_id: result.keyword_id
});
// Small delay to avoid rate limiting
await new Promise(resolve => setTimeout(resolve, 1000));
} catch (error) {
console.error(`Failed to initiate blog for ${date}:`, error.message);
results.push({
date: date,
status: 'error',
error: error.message
});
}
}
console.log(`Initiated ${results.length} blog generations. They will complete in 5-10 minutes.`);
return results;
}
// Later, check which ones are ready
async function checkBulkBlogStatus(websiteId, startDate, endDate) {
const status = await seoProAPI.getBlogStatusByDate(websiteId, startDate, endDate);
const summary = {
ready: [],
processing: [],
failed: []
};
for (const [date, info] of Object.entries(status.date_statuses)) {
if (info.has_blog) {
if (info.status === 'draft') {
summary.ready.push({ date, blog_id: info.blog_id, title: info.title });
} else if (info.status === 'processing') {
summary.processing.push({ date });
}
} else {
summary.failed.push({ date });
}
}
console.log(`Ready: ${summary.ready.length}, Processing: ${summary.processing.length}, Failed: ${summary.failed.length}`);
return summary;
}
3. Check and Fill Missing Blogs
async function fillMissingBlogs(websiteId, startDate, endDate) {
try {
// Check current blog status
const status = await seoProAPI.getBlogStatusByDate(websiteId, startDate, endDate);
const missingDates = [];
// Find dates without blogs
for (const [date, info] of Object.entries(status.date_statuses)) {
if (!info.has_blog) {
missingDates.push(date);
}
}
console.log(`Found ${missingDates.length} dates without blogs:`, missingDates);
// Generate blogs for missing dates
const results = [];
for (const date of missingDates) {
try {
const result = await seoProAPI.generateBlogForDate(
websiteId,
date,
['auto-generated content', 'blog post']
);
results.push({ date, status: 'generated', blog_id: result.blog_id });
// Wait between requests
await new Promise(resolve => setTimeout(resolve, 3000));
} catch (error) {
results.push({ date, status: 'error', error: error.message });
}
}
return results;
} catch (error) {
console.error('Error filling missing blogs:', error);
throw error;
}
}
// Usage
fillMissingBlogs(123, '2024-01-01', '2024-01-31');
4. Scheduled Publishing Workflow
async function scheduledPublishingWorkflow() {
const websiteId = 123;
const today = new Date().toISOString().split('T')[0];
try {
// Check if today has a blog ready to publish
const status = await seoProAPI.getBlogStatusByDate(websiteId, today, today);
const todayStatus = status.date_statuses[today];
if (todayStatus && todayStatus.has_blog && todayStatus.status === 'draft') {
console.log('Publishing today\'s blog...');
const publishResult = await seoProAPI.publishBlogForDate(websiteId, today);
console.log('Published:', publishResult);
return publishResult;
} else {
console.log('No draft blog available for today');
return { status: 'no_blog_to_publish' };
}
} catch (error) {
console.error('Publishing workflow error:', error);
throw error;
}
}
// Run this daily via cron job or scheduler
scheduledPublishingWorkflow();
Python Integration Example
Complete Async Workflow with Polling
import requests
import time
from datetime import datetime, timedelta
from typing import Dict, Optional
class SEOProAPI:
def __init__(self, api_key: str, base_url: str = "https://api.seoproai.co/api"):
self.api_key = api_key
self.base_url = base_url
self.headers = {
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json"
}
def request(self, endpoint: str, method: str = "GET", data: Optional[Dict] = None):
url = f"{self.base_url}{endpoint}"
response = requests.request(method, url, headers=self.headers, json=data)
response.raise_for_status()
return response.json()
def generate_blog_for_date(self, website_id: int, date: str, keywords: list, language_code: str = "en"):
"""Initiate blog generation (returns immediately)"""
return self.request(
f"/websites/{website_id}/generate-blog-for-date",
method="POST",
data={
"scheduled_date": date,
"keywords": keywords,
"language_code": language_code
}
)
def get_blog_status_by_date(self, website_id: int, start_date: str, end_date: str):
"""Check blog status for polling"""
return self.request(
f"/websites/{website_id}/blog-status-by-date?start_date={start_date}&end_date={end_date}"
)
def get_blog_draft(self, blog_id: int):
"""Fetch complete HTML content of draft blog"""
return self.request(f"/blog-drafts/{blog_id}")
def mark_blog_as_published(self, blog_id: int):
"""Mark blog as published (optional)"""
return self.request(f"/blog-drafts/{blog_id}/publish", method="POST")
def wait_for_blog_generation(
self,
website_id: int,
date: str,
max_wait_minutes: int = 15,
poll_interval_seconds: int = 30
):
"""Poll for blog completion with timeout"""
max_attempts = (max_wait_minutes * 60) // poll_interval_seconds
attempts = 0
while attempts < max_attempts:
attempts += 1
# Check blog status
status = self.get_blog_status_by_date(website_id, date, date)
date_status = status.get("date_statuses", {}).get(date)
if date_status and date_status.get("has_blog") and date_status.get("status") == "draft":
print(f"Blog ready after {attempts * poll_interval_seconds} seconds")
return {
"ready": True,
"blog_id": date_status["blog_id"],
"title": date_status["title"]
}
print(f"Blog still processing... (attempt {attempts}/{max_attempts})")
# Wait before next poll
time.sleep(poll_interval_seconds)
raise TimeoutError(f"Blog generation timeout after {max_wait_minutes} minutes")
# Complete usage example
def generate_and_fetch_blog():
api = SEOProAPI("sk_live_1234567_xxxxxxxxxxxxxxxxxxxxx")
website_id = 123
target_date = "2024-01-15"
try:
# Step 1: Initiate blog generation (returns immediately)
print(f"Initiating blog generation for {target_date}...")
init_result = api.generate_blog_for_date(
website_id,
target_date,
["python", "automation", "async workflow"],
"en"
)
print(f"Generation started: {init_result}")
# Step 2: Poll for completion
print("Waiting for blog generation to complete...")
result = api.wait_for_blog_generation(
website_id,
target_date,
max_wait_minutes=15,
poll_interval_seconds=30
)
print(f"Blog is ready! {result}")
# Step 3: Fetch the complete HTML content
print(f"Fetching blog content (ID: {result['blog_id']})...")
blog_draft = api.get_blog_draft(result["blog_id"])
print("Blog fetched successfully:")
print(f"- Title: {blog_draft['title']}")
print(f"- Word Count: {blog_draft['word_count']}")
print(f"- Meta Description: {blog_draft['meta_description']}")
print(f"- HTML Content Length: {len(blog_draft['content'])} chars")
# Step 4: Publish to your third-party website
# (Your custom publishing logic here)
publish_to_your_website(blog_draft)
# Step 5: Optionally mark as published in SEOPro
api.mark_blog_as_published(result["blog_id"])
return blog_draft
except Exception as e:
print(f"Error: {e}")
raise
def publish_to_your_website(blog_draft: Dict):
"""Example: Publish to your custom CMS"""
response = requests.post(
"https://your-site.com/api/posts",
headers={
"Content-Type": "application/json",
"Authorization": "Bearer your-cms-token"
},
json={
"title": blog_draft["title"],
"content": blog_draft["content"],
"slug": blog_draft["slug"],
"meta_description": blog_draft["meta_description"],
"featured_image": blog_draft.get("featured_image_url")
}
)
response.raise_for_status()
print("Published to your website successfully!")
# Run it
if __name__ == "__main__":
generate_and_fetch_blog()
Rate Limits and Best Practices
Rate Limits
Blog Generation: Limited by your plan (5-60 blogs per month)
API Requests: No strict rate limits (API keys have their own limits - see Authentication section)
Polling: Poll every 30-60 seconds (not more frequently)
Publishing: No limits on publishing existing drafts
Best Practices for Async Workflow
Use Async Polling Pattern: Never wait synchronously for blog generation - always use the polling pattern
Poll Interval: Poll every 30-60 seconds (not more frequently to avoid unnecessary API calls)
Timeout: Set a reasonable timeout (15 minutes recommended)
Check Before Generating: Use
/blog-status-by-dateto check if blog already existsHandle Errors Gracefully: Implement proper error handling and retries
Bulk Operations: When generating multiple blogs, initiate them all first (they process in parallel), then poll for all
Validate Dates: Ensure dates are in YYYY-MM-DD format
Store Blog IDs: Track blog IDs returned by status checks for later retrieval
Test First: Use a test website/API key for development
Recommended Workflow Summary
For Single Blog:
1. POST /generate-blog-for-date β Get keyword_id
2. Loop: GET /blog-status-by-date every 30s β Check if status = "draft"
3. GET /blog-drafts/{blog_id} β Fetch HTML content
4. Publish to your website
5. (Optional) POST /blog-drafts/{blog_id}/publish β Mark as published
For Multiple Blogs:
1. POST /generate-blog-for-date for each date (1 second apart)
2. Wait 5-10 minutes
3. GET /blog-status-by-date with date range β Get all statuses at once
4. For each ready blog: GET /blog-drafts/{blog_id} β Fetch HTML
5. Publish all to your website
Error Handling
Common error responses:
{
"detail": "Monthly blog generation limit reached",
"status_code": 429
}
{
"detail": "Website not found",
"status_code": 404
}
{
"detail": "Blog already exists for this date",
"status_code": 400
}
Integration Patterns
1. Content Calendar Automation
Generate blogs for planned content calendar dates
Automatically publish on scheduled dates
Fill gaps in content calendar
2. Keyword-Driven Content
Generate blogs based on keyword research
Target specific keywords for specific dates
Optimize for seasonal content
3. Bulk Content Creation
Generate multiple blogs for content sprints
Prepare content in advance for busy periods
Create evergreen content libraries
Webhook + API Combined Workflow
You can combine both webhook and API integrations:
API: Generate and schedule blogs programmatically
Webhook: Receive notifications when blogs are published
Your System: Process published content and update your CMS
This gives you full control over both content creation and content delivery.
Quick Start Guide
For Third-Party Website Publishing (Recommended Use Case)
If you want to use SEOPro AI to generate blogs and publish them to your own website (not using SEOPro's built-in CMS integrations):
Setup:
Generate your API key in Settings > Integrations > Developer API
Copy your Website ID - It's displayed at the top of the API Keys tab (see Finding Your Website ID)
Install the code examples above (JavaScript or Python)
Daily Workflow:
# Morning: Generate blog for today
POST /generate-blog-for-date (returns immediately)
# 10 minutes later: Check if ready
GET /blog-status-by-date (poll until status = "draft")
# Fetch HTML content
GET /blog-drafts/{blog_id}
# Publish to your WordPress/custom CMS
[Your publishing logic using the HTML content]
# Mark as published (optional)
POST /blog-drafts/{blog_id}/publish
Complete Setup Steps
Generate Your API Key:
Go to Settings > Integrations > Developer API in your SEOPro AI dashboard
Click "Generate New Key" in the API Keys tab
Give it a descriptive name and copy the key immediately (you won't see it again!)
Store it securely as an environment variable
Copy Your Website ID:
Easiest: Displayed at the top of Settings > Integrations > Developer API (API Keys tab)
Or from dashboard URL:
https://app.seoproai.co/dashboard/websites/{website_id}Or via API:
GET /api/websitesendpointSee full details: Finding Your Website ID
Test with Simple Requests:
Copy one of the complete code examples above (JavaScript or Python)
Replace API key and website_id with your values
Run the
generateAndFetchBlog()exampleVerify you receive the HTML content
Build Your Integration:
Implement the async polling pattern (30-60 second intervals)
Add error handling and timeout logic (15 minutes recommended)
Create your custom publishing function to send HTML to your website
Store API keys securely in environment variables
Monitor and Maintain:
Track your API usage in the dashboard (Settings > Integrations > Developer API > Usage)
Monitor monthly blog generation limits for your plan
Rotate API keys regularly for security
Set up alerts for failed generations
API Key Security Best Practices
Store Securely: Use environment variables or secure secret management
Rotate Regularly: Generate new keys periodically and revoke old ones
Use Test Keys: Create separate keys for development and production
Monitor Usage: Check usage statistics in the dashboard
Revoke Compromised Keys: Delete any keys that may have been exposed
Rate Limits
API keys are subject to the following rate limits:
If you exceed rate limits, you'll receive a 429 Too Many Requests response with a Retry-After header.
Why Use the API?
Perfect for:
Third-party Publishing: Generate SEO blogs and publish to your custom website/CMS
Bulk Generation: Generate multiple blogs efficiently in parallel
Custom Workflows: Integrate blog generation into your existing automation
Agency Use: Generate content for multiple client websites programmatically
Scheduled Publishing: Build your own scheduling system with full control
The API integration gives you powerful programmatic control over your content generation and publishing workflow, perfect for agencies, developers, and businesses with complex content needs.