The LotsBlog REST API: Publish From Any Agent or Script

SIsivaguru·
The LotsBlog REST API: Publish From Any Agent or Script

The LotsBlog REST API lets any script, cloud agent or automation tool create, update, schedule and publish posts on your LotsBlog blog with one API key. Create the key at lotstech.com/api, send it in an X-API-Key header to https://api.lots.blog/api/v1/lotsblog, and you get the same 37 operations my AI agent uses through MCP.

If your agent supports remote MCP (Claude, ChatGPT, Codex, Cursor), connect the MCP server instead; it signs in with OAuth and needs no key. This guide is for everything else: CI jobs, cron scripts, n8n, Make or Zapier, and agents you build yourself. Every example below is copy-ready, checked against the LotsBlog API docs on 18 October 2026.

REST or MCP: which one to use

Both reach the same blog with the same permissions. The difference is who makes the call.

You're working fromUseHow it signs in
Claude Code, Claude, ChatGPT, Codex, Cursor or another agent with remote MCPMCP at https://api.lots.blog/mcpOAuth in your browser, no key (Claude Code guide)
A cloud agent that can only make HTTP callsRESTAPI key
A GitHub Action, CI job or cron scriptRESTAPI key stored as a secret
n8n, Make, Zapier or another workflow toolREST, through its HTTP stepAPI key in a header
An agent you build with your own framework (your tools are HTTP functions)REST, with docs.md as the tool referenceAPI key
Your own app or CMS integrationRESTAPI key

The REST API isn't a cut-down version. The docs list 37 endpoints, matching the 37 tools in LotsBlog's published tool list: blogs, posts, topics, images, domains and /blog subdirectory hosting, analytics, team members, funding status and the optional quality check. If you've read the list of blogging platforms with MCP support, you know several gate their API or MCP to higher plans. LotsBlog includes both on every plan and on pay as you go.

The basics

ItemValue
Base URLhttps://api.lots.blog/api/v1/lotsblog
AuthX-API-Key: YOUR_API_KEY, or Authorization: Bearer YOUR_API_KEY
Default rate limit100 requests a minute per key
Lower limits on some writescreate, update, publish, schedule: 30 a minute; trash and restore: 20; upload from URL and quality check: 10
Rate-limit headersX-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset on every response
FormatJSON in, JSON out: {"success": true, "data": {...}, "meta": {...}}
Full referencehttps://api.lots.blog/docs.md (Markdown, written so an agent can read it)

Step 1: Create an API key

  1. Open lotstech.com/api and click Manage API keys.
  2. Sign in with your Lots account.
  3. Choose Lots.Blog, name the key (for example "ci-publisher") and create it.
  4. Copy the key now. It's shown once.

One account holds the keys for every Lots app, and you can revoke a key from the same page. A key acts as you inside LotsBlog, so you need to have signed in to LotsBlog once before you create it. Keys can also be created at https://api.lots.blog/dashboard.

Never paste the key into code or a committed file. Put it in an environment variable or your CI's secret store. The examples below read it from one:

export LOTSBLOG_API_KEY="paste-your-key-here"   # better: load it from your secret manager
export LB="https://api.lots.blog/api/v1/lotsblog"

Step 2: Find your blog and topics

curl -s -H "X-API-Key: $LOTSBLOG_API_KEY" "$LB/blogs"

The response lists every blog you own or belong to, with your role and its public URL. Save the blog's id:

export BLOG_ID="your-blog-uuid"
curl -s -H "X-API-Key: $LOTSBLOG_API_KEY" "$LB/blogs/$BLOG_ID/topics"

Topics come back with numeric IDs; you pass those as topic_ids. GET $LB/blogs/$BLOG_ID also returns the blog's private blog_guide, so an agent working over REST can read your voice and rules before it writes.

Step 3: Create a draft with Markdown, a search title and FAQ schema

Put the post in a JSON file so the Markdown and schema stay readable:

{
  "post_type": "article",
  "status": "draft",
  "title": "How We Back Up Postgres to S3 Every Night",
  "slug": "postgres-backup-to-s3",
  "content": "Our nightly backup runs `pg_dump`, compresses the file and copies it to S3.\n\n## The script\n\n...\n\n## Common questions\n\n### How long do you keep backups?\n\nThirty days, then S3 lifecycle rules delete them.",
  "description": "The nightly Postgres backup we run, with the script.",
  "meta_title": "Postgres Backup to S3: Our Nightly Script",
  "meta_description": "The nightly pg_dump to S3 backup we run, with the full script, retention rules and how we test a restore.",
  "meta_keywords": ["postgres backup", "pg_dump s3"],
  "topic_ids": [12],
  "structured_data": {
    "@context": "https://schema.org",
    "@type": "FAQPage",
    "mainEntity": [
      {
        "@type": "Question",
        "name": "How long do you keep backups?",
        "acceptedAnswer": {
          "@type": "Answer",
          "text": "Thirty days, then S3 lifecycle rules delete them."
        }
      }
    ]
  }
}

Then send it:

curl -s -X POST "$LB/blogs/$BLOG_ID/posts" \
  -H "X-API-Key: $LOTSBLOG_API_KEY" \
  -H "Content-Type: application/json" \
  --data @post.json

What each field does:

  • status defaults to draft. A draft is private; the response includes a preview_url that asks the reader to sign in with an account on the blog.
  • meta_title becomes the page <title>, og:title and twitter:title exactly as written. The visible headline stays title. Keep it to about 60 characters.
  • meta_description is the search snippet. Keep it under 160 characters.
  • structured_data takes supplemental JSON-LD such as FAQPage. LotsBlog already generates Article and BreadcrumbList schema, so don't send those. The FAQ must match questions visible on the page; I explain why in Article, Breadcrumb and FAQ schema for blog posts.

Save the post id from the response:

export POST_ID="the-new-post-uuid"

Step 4: Upload a cover image

A local image goes up in three calls, so the bytes never pass through the API as base64:

FILE=cover.webp
SIZE=$(wc -c < "$FILE" | tr -d ' ')

# 1. Ask for an upload URL (valid 15 minutes)
curl -s -X POST "$LB/blogs/$BLOG_ID/images/upload" \
  -H "X-API-Key: $LOTSBLOG_API_KEY" \
  -H "Content-Type: application/json" \
  -d "{\"filename\":\"$FILE\",\"file_size\":$SIZE,\"mime_type\":\"image/webp\"}"

The response carries upload_url, the headers to send (just Content-Type), a file_key and the future public_url.

# 2. PUT the bytes to upload_url with only the returned Content-Type header
curl -s -X PUT -H "Content-Type: image/webp" --data-binary @"$FILE" "$UPLOAD_URL"

# 3. Confirm it: LotsBlog checks the size and the file's real format
curl -s -X POST "$LB/blogs/$BLOG_ID/images/complete" \
  -H "X-API-Key: $LOTSBLOG_API_KEY" \
  -H "Content-Type: application/json" \
  -d "{\"file_key\":\"$FILE_KEY\",\"file_size\":$SIZE}"

The last call returns the public CDN url. JPEG, PNG, WebP and GIF are accepted, up to 25 MB. If the image already sits at a public URL, skip all three and call POST $LB/blogs/$BLOG_ID/images with image_url. Uploaded images are public even on a private blog.

Step 5: Update the post

PATCH changes only the fields you send. Everything else stays as it was:

curl -s -X PATCH "$LB/blogs/$BLOG_ID/posts/$POST_ID" \
  -H "X-API-Key: $LOTSBLOG_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"featured_image":"https://media.lots.blog/YOUR_IMAGE.webp","meta_description":"A sharper snippet under 160 characters."}'

Send "topic_ids": [] to clear topics, or leave the field out to keep them. Update the same post_id instead of creating a second post; slugs are unique within a blog, so a retry with the same slug fails rather than duplicating.

Step 6: List posts

curl -s -H "X-API-Key: $LOTSBLOG_API_KEY" \
  "$LB/blogs/$BLOG_ID/posts?status=draft&limit=20"

Posts come back newest first. Filter by status (draft, scheduled, published, archived, trashed), topic_id, and page with limit (up to 100) and offset. To check one post exactly as saved, GET $LB/blogs/$BLOG_ID/posts/$POST_ID. Read a post back before retrying a write you aren't sure went through.

Step 7: Schedule or publish

Writing a draft never publishes it. Going live is always its own call:

# Schedule: ISO 8601 with a timezone, at least 5 minutes ahead
curl -s -X POST "$LB/blogs/$BLOG_ID/posts/$POST_ID/schedule" \
  -H "X-API-Key: $LOTSBLOG_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"scheduled_for":"2026-10-20T09:00:00+05:30"}'

# Publish now
curl -s -X POST "$LB/blogs/$BLOG_ID/posts/$POST_ID/publish" \
  -H "X-API-Key: $LOTSBLOG_API_KEY"

LotsBlog checks for scheduled posts every 15 minutes, so a post goes live on the first check after its time. A scheduled response isn't proof it's live; read the post back after the time passes. To pull an article back, POST .../posts/$POST_ID/unpublish returns it to a private draft and clears any schedule. DELETE .../posts/$POST_ID moves a post to Trash, out of the site, sitemap and RSS, and POST .../posts/$POST_ID/restore brings it back with its previous status.

The same flow in Python

For a script or an agent framework, the whole draft flow is a few lines:

import os, requests

LB = "https://api.lots.blog/api/v1/lotsblog"
H = {"X-API-Key": os.environ["LOTSBLOG_API_KEY"]}
blog_id = os.environ["BLOG_ID"]

post = requests.post(f"{LB}/blogs/{blog_id}/posts", headers=H, json={
    "post_type": "article",
    "title": "Release notes: October",
    "content": open("release-notes.md").read(),
    "meta_description": "What shipped in October and how to use it.",
}).json()

if not post.get("success"):
    raise SystemExit(post["error"])
print(post["data"])  # includes the post id and preview_url; it stays a draft

Add the schedule call only when a person has approved the draft, or when the whole pipeline (a release, a changelog) is the approval.

No-code tools: n8n, Make and Zapier

Any step that sends an HTTP request with a custom header works. I checked each vendor's own docs on 18 October 2026:

  • n8n: the HTTP Request node supports every method you need (GET, POST, PATCH, PUT, DELETE) and Header auth, so put X-API-Key in a Header Auth credential rather than in the node.
  • Make: the HTTP app's "Make a request" module has an API key authentication type; Make recommends storing the key there instead of typing it into headers.
  • Zapier: Webhooks by Zapier is available on every plan, Free included. Use POST or PUT for creating drafts, and Custom Request for PATCH and DELETE.

A pattern that works well: a form, a spreadsheet row or a finished document triggers the workflow, the workflow POSTs a draft, and a person publishes from the preview link.

Errors you'll see

CodeHTTPWhat it meansFix
AUTHENTICATION_REQUIRED401Missing or invalid keyCheck the header name and that the key wasn't revoked
VALIDATION_ERROR400A field is wrong: a meta description over 160 characters, a slug already used, a schedule under 5 minutes aheadRead error.message; it names the field
RATE_LIMIT_EXCEEDED429Over the limit for that endpointWait for X-RateLimit-Reset, then retry
ENDPOINT_NOT_FOUND404Wrong pathCopy the path from docs.md
(from storage)403 on the image PUTExtra headers on the uploadSend only the Content-Type that images/upload returned

Host it on your own domain

Everything you publish over REST goes to the blog's address: a lots.blog subdomain, a custom domain like blog.example.com, or a path on your own site. The same API connects the domain (POST $LB/blogs/$BLOG_ID/domain, with path: "/blog" for a subdirectory) and checks it (POST .../domain/check). The full setup is in How to host your blog at yourdomain.com/blog.

What it costs

The REST API and the MCP server are included on every plan and on pay as you go. Pay as you go is $9 per blog a month from credits. Plans are $24 a month for 3 blogs, $69 for 10 and $159 for 30, or 10 months' price for a year. Posts, page views and authors are unlimited, and Pro and Business also include LotsNotes Pro. New accounts get 10,000 free credits, valid 90 days, no card. The optional quality check (posts/:post_id/quality-check) uses credits only when you call it. Full details on the pricing page.

Start from your agent

Give this to any coding agent and let it build the integration with you:

Read https://api.lots.blog/docs.md and help me integrate LotsBlog's REST API into my application. Read my API key from the LOTSBLOG_API_KEY environment variable, save posts as drafts, and never publish or schedule without asking me.

How I checked this guide

I checked every request shape above against docs.md and the published tool list on 18 October 2026. Then I ran the same operations through the LotsBlog MCP server, which calls the same tools as the REST endpoints, on a throwaway private draft: create with Markdown, meta_title, meta_description and FAQPage structured_data; a cover upload in three calls; an update that changed two fields and kept the rest; a filtered list; and a move to Trash. Every field came back as sent.

Common questions

Do I need an API key to use LotsBlog from an AI agent?

Not if your agent supports remote MCP. Claude Code, Claude, ChatGPT, Codex and Cursor connect to https://api.lots.blog/mcp and sign in with OAuth. You need a key only for the REST API: scripts, CI, workflow tools and agents that make plain HTTP calls.

Where do I create a LotsBlog API key?

At lotstech.com/api: click Manage API keys, sign in, choose Lots.Blog and create the key. It's shown once, so copy it into a secret store straight away. You can revoke it from the same page.

What are the LotsBlog API rate limits?

100 requests a minute per key by default. Some writes are lower: creating, updating, publishing and scheduling posts allow 30 a minute, and uploading an image from a URL allows 10. Every response carries rate-limit headers.

Can the REST API do everything the MCP server does?

Yes. The docs list 37 endpoints, one for each tool in LotsBlog's published tool list, from posts and images to domains, team members and analytics.

Will a script publish my posts by accident?

No. New posts are private drafts unless you set a status or call publish or schedule. Keep publishing as a separate, deliberate step, and store the key as a secret, because a key acts as you.

Is the REST API on every LotsBlog plan?

Yes. Every plan ($24, $69 or $159 a month) and pay as you go ($9 per blog a month) includes both the REST API and the MCP server.

LotsBlog details checked on 18 October 2026 from api.lots.blog/docs.md, lotstech.com/api, lots.blog/skill.md and lots.blog/pricing. n8n, Make and Zapier details checked on 18 October 2026 from n8n's HTTP Request node docs, Make's HTTP app docs and Zapier's webhooks help article.