API Documentation
Generate SEO blog content from your own system with the /v1 API. Get a key in your workspace → API keys.
Authentication
Send your key as a Bearer token. Keep it secret — it inherits your plan's usage limits.
Authorization: Bearer sk_live_xxxxxxxxxxxxxxxxxxxxBring your own AI key (unlimited)
By default, generations use our AI and count against your plan. Connect your own provider key — Anthropic, OpenAI, Google or DeepSeek — in your workspace → Settings, and every API call bills to your provider account: unlimited and unmetered by us. Your key is stored encrypted and used only for your own requests — never shared or logged.
Generate a post
POST /v1/generate runs the full pipeline (angle → outline → draft → critic) and returns finished HTML. One request = one document against your plan.
Body fields
keywordorsubject— required (topic to write about)template— one of:roundup,buying_guide,howto,comparison,product,linkedin,newsletterbrand_voice,tone,audience— optionalmodel—claude(default),gpt,gemini,deepseek
curl
curl -X POST https://your-domain.com/v1/generate \
-H "Authorization: Bearer sk_live_..." \
-H "Content-Type: application/json" \
-d '{"keyword":"new AI tools for customer support","template":"howto"}'JavaScript
const res = await fetch('https://your-domain.com/v1/generate', {
method: 'POST',
headers: {
Authorization: 'Bearer sk_live_...',
'Content-Type': 'application/json',
},
body: JSON.stringify({
keyword: 'new AI tools for customer support',
template: 'howto',
}),
})
const post = await res.json()
// { id, title, html, meta: { title, description }, critic, word_count }PHP (Laravel)
$res = Http::withToken('sk_live_...')
->post('https://your-domain.com/v1/generate', [
'keyword' => 'new AI tools for customer support',
'template' => 'howto',
]);
$post = $res->json();
// insert into your CMS:
BlogPost::create([
'title' => $post['title'],
'body' => $post['html'],
]);Response
{
"id": 123,
"title": "New AI Tools For Customer Support",
"html": "<h2>...</h2><p>...</p>",
"meta": { "title": "...", "description": "..." },
"critic": { "score": 8.4, "verdict": "...", "issues": [] },
"word_count": 1180
}Other endpoints
GET /v1/content/{id}Fetch a generated document by id.
GET /v1/usageYour current plan usage: { plan, used, limit, remaining, byok }.
Errors
| Status | Code | Meaning |
|---|---|---|
| 401 | unauthorized | Missing/invalid API key |
| 402 | limit_reached | Plan limit hit — upgrade or use BYOK |
| 403 | forbidden | Key lacks the required scope |
| 400 | bad_request | Missing keyword/subject |
| 500 | generation_failed | Server error during generation |
Rate limits & usage
Each key is rate-limited (default 60 requests/minute). Exceeding it returns 429 with a Retry-After header — back off and retry. Check your remaining plan quota anytime with GET /v1/usage. Always send requests over HTTPS and keep your key server-side (never in client code).
Templates (your own content styles)
A template shapes the angle, structure and tone of everything you generate. Use a built-in one by slug (e.g. howto, roundup), or create your own — either by hand or by letting AI design it from a plain-English description. Pass a template's id as template when you generate or schedule content.
# List built-in + your custom templates
curl https://your-domain.com/v1/templates \
-H "Authorization: Bearer sk_live_..."
# Let AI design a template and save it in one call
curl -X POST https://your-domain.com/v1/templates/generate \
-H "Authorization: Bearer sk_live_..." \
-H "Content-Type: application/json" \
-d '{
"description": "a daily AI-news brief for small-business owners",
"audience": "non-technical founders",
"save": true
}'
# → { "id": "42", "name": "AI News Brief", "content_type": "blog_post", ... }
# Then generate using that template id
curl -X POST https://your-domain.com/v1/generate \
-H "Authorization: Bearer sk_live_..." \
-H "Content-Type: application/json" \
-d '{ "keyword": "new AI coding tools", "template": "42" }'Also: POST /v1/templates (create manually), GET /v1/templates/{id}, PATCH /v1/templates/{id}, DELETE /v1/templates/{id}. Set "save": false on /v1/templates/generate to preview a draft without storing it. Requires the templates:write scope.
Schedules (automatic content)
Create a schedule to generate content on a timer — it rotates through your keyword list and (optionally) pushes each finished post to your webhook. Manage schedules in your workspace → Automations or via the API.
curl -X POST https://your-domain.com/v1/schedules \
-H "Authorization: Bearer sk_live_..." \
-H "Content-Type: application/json" \
-d '{
"name": "AI news daily",
"template": "howto",
"frequency": "daily",
"keywords": ["new AI coding tools", "AI in customer support"],
"delivery": "webhook"
}'Also: GET /v1/schedules, PATCH /v1/schedules/{id}, DELETE /v1/schedules/{id}.
Webhooks
Add endpoints in your workspace → Automations. When a scheduled post is ready we POST it to your URL, signed with an HMAC so you can verify it's from us:
POST (your endpoint)
X-Signature: sha256=<hmac-of-raw-body>
X-Webhook-Event: content.completed
{
"event": "content.completed",
"data": {
"id": 42,
"title": "New AI Coding Tools",
"html": "<h2>...</h2><p>...</p>",
"meta": { "title": "...", "description": "..." },
"word_count": 1180,
"schedule": { "id": 3, "name": "AI news daily" }
},
"timestamp": 1721000000000
}Verify the signature (PHP / Laravel)
$sig = 'sha256=' . hash_hmac('sha256', $request->getContent(), $endpointSecret);
if (!hash_equals($sig, $request->header('X-Signature'))) abort(401);
$post = $request->json('data');
BlogPost::create(['title' => $post['title'], 'body' => $post['html']]);