Skip to content
ActiveGeo

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_xxxxxxxxxxxxxxxxxxxx

Bring 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

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/usage

Your current plan usage: { plan, used, limit, remaining, byok }.

Errors

StatusCodeMeaning
401unauthorizedMissing/invalid API key
402limit_reachedPlan limit hit — upgrade or use BYOK
403forbiddenKey lacks the required scope
400bad_requestMissing keyword/subject
500generation_failedServer 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']]);

Ready to build?

Create a key and make your first request in minutes.

Get your API key
Content API — Generate SEO Articles Programmatically