API Documentation
The AutoBlog API lets you programmatically fetch AI-generated blog posts and newsletters (and hero images when present on a row). It is a RESTful JSON API served by the api-gateway Supabase Edge Function.
Available on SEO Engine ($49/mo) and Programmatic Pro ($149/mo) plans. See pricing.
Quick Start
Get your first API response in under 60 seconds.
Get your API key
Sign up at autoblogs.dev and open API Keys in the dashboard. Create a full key for server-side calls to content endpoints, or an embed key (analytics only, with allowed origins) for the browser tracking script. See API keys & scopes.
Make your first request
Use your API key in the Authorization header against the gateway URL to fetch your latest generated content.
Publish
Parse the JSON response and publish the HTML body, image, and metadata to your CMS or website.
// Minimal integration example (gateway = Supabase Edge Function URL)
const API_BASE = process.env.AUTOBLOG_API_BASE ?? 'https://YOUR_REF.supabase.co/functions/v1/api-gateway';
const response = await fetch(`${API_BASE}/v1/content/latest`, {
headers: { 'Authorization': `Bearer ${process.env.AUTOBLOG_API_KEY}` }
});
const { data } = await response.json();
// data.body_html, data.meta_title, data.slug
// data.id - UUID (use in URLs and analytics)
// data.image_url - Hero image URL when set
Authentication
Authenticate with either:
- API key:
Authorization: Bearer <api_key>. Create and revoke keys under API Keys in the dashboard. - Dashboard session: Supabase JWT in
Authorization: Bearer <jwt>(token starts witheyJ), or the same token inx-supabase-session. Used by the logged-in app; not for public embeds.
Authorization: Bearer YOUR_API_KEY
Content-Type: application/json
API keys & scopes
Each key has one or more scopes that limit what it can call. If scopes are not set on a key, the API defaults to allowing both content_read and analytics_ingest.
| Scope | Allows |
|---|---|
content_read |
GET /v1/content, GET /v1/content/:id (UUID), GET /v1/content/latest, GET /v1/content/by-slug, GET /v1/analytics/summary, GET/POST /v1/projects, and related content routes. Required for normal server integrations. |
analytics_ingest |
POST /v1/analytics/events only. Used by embed keys for the tracking script. |
Full key: Typically includes content_read (and may include analytics where your plan allows). Use only on servers, CI, or trusted tools.
Embed key: Scoped to analytics_ingest. Safe in the browser when you set allowed origins so only your domains can send events. The API checks the Origin or Referer header against that list.
Base URL
The REST API is implemented by the api-gateway Supabase Edge Function. Replace YOUR_REF with your Supabase project reference (same subdomain you use for the dashboard client).
https://YOUR_REF.supabase.co/functions/v1/api-gateway
Endpoints are prefixed with /v1. Example:
GET https://YOUR_REF.supabase.co/functions/v1/api-gateway/v1/content/latest
In server code, set AUTOBLOG_API_BASE to the gateway URL (no trailing slash), then call ${AUTOBLOG_API_BASE}/v1/....
All requests and responses use JSON. Set Content-Type: application/json for POST bodies.
The same function also implements POST /v1/auth/signup and POST /v1/auth/login for the in-app auth flow (email/password). Third-party integrations normally use API keys instead.
Scheduled publishing
Recurring schedules and publish times are configured in the dashboard per content source. The API does not create schedules; it returns content that is already published (or other statuses if you ask for them).
Use GET /v1/content with status=published (the default) and limit / offset to page through results. For automation patterns, see Integrations.
Rate Limits
The gateway does not send X-RateLimit-* headers. Plan-based usage (posts per month, etc.) is enforced in the product and billing layer.
For POST /v1/content/generate calls made with an API key, the gateway compares your plan to a per-minute ceiling before running generation: 10/min on SEO Engine (seo_engine) and 30/min on Pro (pro). Other routes are not individually throttled inside this function.
Error Handling
Errors return JSON with "status": "error", a message string, and sometimes a code string (for example "PAYMENT_REQUIRED"), not the HTTP status duplicated as a number.
{
"status": "error",
"message": "Authentication required"
}
| HTTP | Meaning | Typical cause |
|---|---|---|
400 | Bad Request | Missing query/body fields, invalid JSON, or invalid date range. |
401 | Unauthorized | No valid API key or session token. |
402 | Payment required | Account not active (e.g. subscription not completed); analytics/content may return PAYMENT_REQUIRED. |
403 | Forbidden | Wrong plan for API/analytics, missing scope on the key, or origin not allowed (analytics). |
404 | Not Found | Unknown route or no matching content. |
429 | Too many requests | Generation rate limit exceeded for your plan. |
500 | Server error | Upstream or configuration issue. Retry or contact support. |
Returns the most recently generated content for the authenticated project. This is the primary endpoint most integrations will use.
Query Parameters
type string optional
Filter by content type. Values: blog_post, newsletter. Defaults to blog_post.
status string optional
Same as list: published (default), or all for any status.
Example: JavaScript (Node.js / Next.js)
const AUTOBLOG_API_KEY = process.env.AUTOBLOG_API_KEY;
const API_BASE = process.env.AUTOBLOG_API_BASE ?? 'https://YOUR_REF.supabase.co/functions/v1/api-gateway';
async function getLatestPost(type = 'blog_post') {
const response = await fetch(
`${API_BASE}/v1/content/latest?type=${type}`,
{
headers: {
'Authorization': `Bearer ${AUTOBLOG_API_KEY}`,
'Content-Type': 'application/json'
}
}
);
if (!response.ok) {
throw new Error(`AutoBlog API error: ${response.status}`);
}
const json = await response.json();
return json.data;
}
// Usage
const post = await getLatestPost();
console.log(post.meta_title); // "The Complete Guide to..."
console.log(post.slug); // "plumbing-maintenance-guide"
console.log(post.body_html); // "<h1>The Complete...</h1>..."
console.log(post.image_url); // hero image URL or null
console.log(post.newsletter_html); // newsletter HTML or null
Example: Python
import os
import requests
API_KEY = os.environ["AUTOBLOG_API_KEY"]
API_BASE = os.environ.get("AUTOBLOG_API_BASE", "https://YOUR_REF.supabase.co/functions/v1/api-gateway")
def get_latest_post(content_type="blog_post"):
response = requests.get(
f"{API_BASE}/v1/content/latest",
headers={"Authorization": f"Bearer {API_KEY}"},
params={"type": content_type}
)
response.raise_for_status()
return response.json()["data"]
post = get_latest_post()
print(post["meta_title"])
print(post["body_html"][:200])
Example: cURL
curl -s "https://YOUR_REF.supabase.co/functions/v1/api-gateway/v1/content/latest?type=blog_post" \
-H "Authorization: Bearer YOUR_API_KEY" | jq .
Response (200 OK)
See Response Schema for full field documentation.
{
"status": "success",
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"content_type": "blog_post",
"status": "published",
"created_at": "2026-04-06T10:00:00.000Z",
"project_id": "660e8400-e29b-41d4-a716-446655440001",
"meta_title": "The Complete Guide to Home Plumbing Maintenance",
"meta_description": "Prevent burst pipes and expensive leaks with our comprehensive checklist.",
"slug": "home-plumbing-maintenance-guide",
"body_html": "<h1>...</h1><p>...</p>",
"body_text": "Plain text excerpt...",
"newsletter_html": null,
"image_url": "https://…",
"image_alt_text": "Copper pipes and pressure gauge",
"keywords": ["plumbing"],
"word_count": 1420,
"reading_time": "6 min"
}
}
Returns a list of generated content for the authenticated account, ordered by created_at (newest first). Use limit and offset to page through results.
Query Parameters
type string optional
Filter: blog_post or newsletter (alias: content_type).
project_id string optional
Filter by project (Pro plan).
status string optional
Filter by row status. Defaults to published. Use all to include drafts and scheduled items.
limit integer optional
Max items (default 20, max 100).
offset integer optional
Skip this many rows (for pagination).
Blog page integration
Point your website’s /blog route at this API (server-side, with your API key). List posts with status=published (default), then link each card to a detail view that loads by slug or id.
// Example: blog index (Node / Edge)
const API_BASE = process.env.AUTOBLOG_API_BASE ?? 'https://YOUR_REF.supabase.co/functions/v1/api-gateway';
const res = await fetch(
`${API_BASE}/v1/content?type=blog_post&status=published&limit=20`,
{ headers: { Authorization: `Bearer ${API_KEY}` } }
);
const { data } = await res.json();
// data[].slug, data[].meta_title, data[].body_html, data[].created_at
Single post by slug: GET /v1/content/by-slug?slug=my-post&status=published (SEO Engine / Pro).
Response (200 OK)
The response is a JSON array of content rows (same shape as GET /content/:id). There is no total count in the payload; increase offset until you receive fewer than limit items.
{
"status": "success",
"data": [
{ /* same shape as single content object */ },
{ /* ... */ }
]
}
Analytics embed script
Track views, time on page, and outbound clicks from your live site. Browser traffic uses an embed API key and the optional script below; server-side tools and MCP use GET /v1/analytics/summary with a full key.
POST /v1/analytics/events
With an API key, the key must include the analytics_ingest scope, and you must configure at least one allowed origin; the request’s Origin or Referer must match. Session tokens (JWT from the dashboard) authenticate as you without an API key record, so the allowed-origin list is not enforced for that path. Browser embeds should always use an embed key.
Events are stored for your user; unknown content_id values or invalid event_type values are skipped (partial insert).
Send at most 50 events per request body. Successful responses include how many rows were inserted (events targeting valid content may be fewer than the array length).
Request body
{
"events": [
{
"content_id": "uuid",
"event_type": "view" | "heartbeat" | "link_click",
"session_id": "opaque-id",
"payload": { "dwell_ms": 15000, "href": "https://..." },
"client_ts": "2026-01-01T12:00:00.000Z"
}
]
}
Response (200 OK)
{ "status": "success", "data": { "inserted": 3 } }
GET /v1/analytics/summary
Requires a full API key with the content_read scope (embed keys cannot call this route). Available on SEO Engine and Pro when analytics is enabled for your account.
Query parameters
from string optional
Start of range (ISO date or datetime). Defaults to 30 days before to.
to string optional
End of range (ISO date or datetime). Defaults to now.
Aggregates use server receive time with from inclusive and to exclusive. Only posts with at least one event in the window appear.
Response (200 OK)
{
"status": "success",
"data": [
{
"content_id": "uuid",
"meta_title": "Example post",
"slug": "example-post",
"views": 42,
"total_dwell_ms": 125000,
"clicks": 5
}
]
}
Embed script (copy-paste)
Host /js/autoblog-track.js from this site or copy the file to your CDN. On each post detail page, add:
<script src="https://autoblogs.dev/js/autoblog-track.js"
data-gateway="https://YOUR_REF.supabase.co/functions/v1/api-gateway"
data-site-key="ab_live_…"
data-content-id="POST_UUID_FROM_DASHBOARD"
async></script>
No-code builders
- Wix: Settings → Custom Code → Add code to All pages or the blog post template → paste the
<script>(use the post’s content UUID indata-content-id; you may use Velo to inject it dynamically). - Framer: Site Settings → Custom Code, or a Code component on your blog article template; pass the CMS item’s id into
data-content-id. - Lovable: Add the script in your root layout or article page; map route params to
data-content-id.
Analytics may require cookie or consent banners depending on your region. Consult your legal advisor. The embed sends first-party events to AutoBlog only.
Returns a single content item by primary key. The path segment must be a UUID (the id column on content).
Path Parameters
id uuid required
Example: 550e8400-e29b-41d4-a716-446655440000. Returned in list and fetch responses as data.id.
Response
Same shape as the single content object in GET /content/latest.
Runs a synchronous Gemini call and inserts a new draft row. Requires an active subscription (profile.status === active). With an API key, your plan must be SEO Engine or Pro; the gateway also enforces per-minute generation caps (10/min vs 30/min).
The implementation in api-gateway is intentionally minimal: it sends your topic (and optional keywords) to the model and persists the parsed JSON. It does not substitute template variables in topic, and it does not accept word_count, image_style, or generate_image in the request body. Those fields are ignored if sent.
Request body
type string required
blog_post or newsletter (must satisfy the database check constraint).
topic string required
Topic line passed into the model prompt.
keywords string[] optional
Joined into the prompt for the model.
project_id uuid optional
If set, voice sliders/sample are taken from this project; otherwise optional voice fields on the body can supply voice_mode, voice_instructions, formality, energy, personality for the prompt builder.
Example request
const API_BASE = process.env.AUTOBLOG_API_BASE ?? 'https://YOUR_REF.supabase.co/functions/v1/api-gateway';
const response = await fetch(`${API_BASE}/v1/content/generate`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
type: 'blog_post',
topic: 'Winter plumbing checklist for homeowners',
keywords: ['frozen pipes', 'maintenance']
})
});
const { data } = await response.json();
// data is the inserted content row (draft) or parsed model output on save failure
Response (201 Created)
Returns { "status": "success", "data": <content row> } where data matches the database shape (blog posts expect meta_title, body_html, body_text from the model). Richer generation (images, scheduling, full SEO pipeline) uses other app flows such as the generate-content function, not this minimal route.
Generate distribution variants from an existing content item. This endpoint is an Edge Function and is invoked with supabase.functions.invoke(), not through api-gateway.
Requires an authenticated dashboard session JWT. Plan gating applies by format: Pulse supports summary output; SEO Engine and Pro support all formats.
Request body
content_id uuid required
The source content row to repurpose.
formats string[] required
Requested output formats. Supported values include twitter_thread, linkedin_post, newsletter_teaser, and summary.
Example invoke
const { data, error } = await supabase.functions.invoke('repurpose-content', {
body: {
content_id: '550e8400-e29b-41d4-a716-446655440000',
formats: ['twitter_thread', 'linkedin_post', 'summary']
}
});
Response shape
{
"status": "success",
"data": {
"content_id": "uuid",
"variants": {
"twitter_thread": "...",
"linkedin_post": "...",
"newsletter_teaser": "...",
"summary": "..."
}
}
}
Generate plain-English analytics insights for a selected date range. This endpoint is an Edge Function called via supabase.functions.invoke(), not through api-gateway.
Requires an authenticated dashboard session JWT and an active plan that includes AI insights.
Request body
from string optional
Start of range (ISO date or datetime).
to string optional
End of range (ISO date or datetime).
Example invoke
const { data, error } = await supabase.functions.invoke('generate-insights', {
body: {
from: '2026-03-01T00:00:00.000Z',
to: '2026-04-01T00:00:00.000Z'
}
});
Response shape
{
"status": "success",
"data": {
"insights": [
{
"type": "opportunity",
"title": "Publishing window is outperforming baseline",
"detail": "..."
}
]
}
}
Response Schema
Content endpoints return rows from the public.content table (JSON field names match column names). There is no nested image object. Hero image fields are flat.
| Field | Type | Description |
|---|---|---|
id | uuid | Primary key. Use in GET /v1/content/:id, analytics content_id, and embed data-content-id. |
content_type | string | blog_post or newsletter. |
status | string | draft, published, scheduled, or archived. |
created_at | string | ISO 8601 timestamp. |
updated_at | string | ISO 8601 timestamp. |
project_id | uuid|null | Optional project association. |
meta_title | string|null | Page title / SEO title. |
meta_description | string|null | SEO description. |
slug | string|null | URL slug when set. |
body_html | string|null | Article HTML for blog posts. |
body_text | string|null | Plain text body. |
subject_line | string|null | Newsletter subject (when applicable). |
newsletter_html | string|null | Newsletter HTML (when applicable). |
image_url | string|null | Hero image URL when present. |
image_alt_text | string|null | Alt text for the hero image. |
image_style | string|null | Style enum when set (see Image Styles). |
keywords | string[] | Keyword list stored on the row. |
word_count | integer|null | Word count when populated. |
reading_time | string|null | Estimated reading time when populated. |
tone | string|null | Tone or voice metadata (may reflect sliders/sample mode). |
Content Types
| Type | Value | Description |
|---|---|---|
| Blog Post | blog_post | SEO-optimized article with structured HTML, meta tags, and slug. Designed for website publishing. |
| Newsletter | newsletter | Email-safe HTML with inline styles, subject line, and preview text. Compatible with all major ESPs. |
On-demand generation via POST /v1/content/generate sets type to a single value per request (blog_post or newsletter); there is no combined both mode in the gateway.
Image Styles
Allowed values for image_style on stored content (for example when set by the dashboard or full generate-content pipeline). The minimal api-gateway POST /v1/content/generate route does not currently persist image_style or generate images.
| Value | Description |
|---|---|
photorealistic | High-quality, realistic photography style. Best for most business and editorial use cases. |
illustration | Clean, modern vector illustration. Good for tech and SaaS blogs. |
minimal | Simple, low-detail compositions with lots of whitespace. Good for clean brands. |
abstract | Artistic, non-representational visuals. Good for creative and design-focused content. |
editorial | Magazine-style editorial photography. Good for lifestyle and fashion content. |
infographic | Data-visualization style with charts and diagrams. Best for technical or data-driven posts. |
Webhooks
This codebase does not expose a public “outbound” webhook that POSTs to your URL on every new post. Stripe and other billing webhooks are internal to the platform.
For inbound idea capture, projects can POST to https://YOUR_REF.supabase.co/functions/v1/idea-webhook with header x-webhook-secret matching the project’s configured secret (rate-limited per secret).
To detect new published content from your stack, poll GET /v1/content on a schedule or integrate from the dashboard workflows you already use.
MCP server (Cursor & Claude Desktop)
This project ships a Model Context Protocol server so assistants can list posts, read analytics summaries, and show embed instructions. Code lives under packages/autoblog-mcp (run npm install in that folder).
Requirements: Node.js 18+, SEO Engine or Pro, and a full API key with content_read and analytics_ingest (see package README). Set:
export AUTO_BLOG_BASE_URL="https://YOUR_REF.supabase.co/functions/v1/api-gateway"
export AUTO_BLOG_API_KEY="ab_live_…"
node /path/to/packages/autoblog-mcp/index.js
Tools exposed: list_blog_posts, get_analytics_summary, get_embed_instructions. In Cursor or Claude Desktop, add an MCP server entry that runs node with the absolute path to index.js and the environment variables above.
Integration: Next.js
Fetch your latest blog post at build time or on each request using Next.js server components or API routes.
Server Component (App Router)
// app/blog/latest/page.tsx
async function getPost() {
const API_BASE = process.env.AUTOBLOG_API_BASE ?? 'https://YOUR_REF.supabase.co/functions/v1/api-gateway';
const res = await fetch(`${API_BASE}/v1/content/latest`, {
headers: { 'Authorization': `Bearer ${process.env.AUTOBLOG_API_KEY}` },
next: { revalidate: 3600 } // revalidate every hour
});
const json = await res.json();
return json.data;
}
export default async function LatestBlogPage() {
const post = await getPost();
return (
<article>
<head>
<title>{post.meta_title}</title>
<meta name="description" content={post.meta_description} />
</head>
{post.image_url && (
<img src={post.image_url} alt={post.image_alt_text || ''} width={1200} height={630} />
)}
<div dangerouslySetInnerHTML={{ __html: post.body_html }} />
</article>
);
}
Integration: WordPress
Use a cron job or WP-CLI command to fetch content and create WordPress posts automatically.
// functions.php - Add to your theme or as a custom plugin
function autoblog_fetch_latest() {
$response = wp_remote_get('https://YOUR_REF.supabase.co/functions/v1/api-gateway/v1/content/latest', [
'headers' => [
'Authorization' => 'Bearer ' . AUTOBLOG_API_KEY,
]
]);
$body = json_decode(wp_remote_retrieve_body($response), true);
$post_data = $body['data'];
// Create a new WordPress post
wp_insert_post([
'post_title' => $post_data['meta_title'],
'post_content' => $post_data['body_html'],
'post_name' => $post_data['slug'],
'post_status' => 'draft',
'post_type' => 'post',
]);
}
// Run weekly via WP-Cron
add_action('autoblog_weekly_fetch', 'autoblog_fetch_latest');
if (!wp_next_scheduled('autoblog_weekly_fetch')) {
wp_schedule_event(time(), 'weekly', 'autoblog_weekly_fetch');
}
Integration: Generic REST (Any Stack)
AutoBlog works with any language or framework that can make HTTP requests. Here is the minimal pattern:
# 1. Set your API key as an environment variable
export AUTOBLOG_API_KEY="your_key_here"
export AUTOBLOG_API_BASE="https://YOUR_REF.supabase.co/functions/v1/api-gateway"
# 2. Fetch the latest blog post
curl -s "${AUTOBLOG_API_BASE}/v1/content/latest" \
-H "Authorization: Bearer $AUTOBLOG_API_KEY" \
-o latest_post.json
# 3. Extract the HTML body
cat latest_post.json | jq -r '.data.body_html' > post.html
# 4. Download the image
curl -s $(cat latest_post.json | jq -r '.data.image_url // empty') -o hero.jpg