blogr.ai

Docs

REST API

Pull your generated articles and the inner pages from your topical map into any stack: a read-only JSON API, authenticated with your integration's access token.

How it works

The REST API is a pull-based integration: your site fetches articles from blogr.ai whenever it builds or renders — a natural fit for static site generators and custom stacks. With it connected, clicking Publish marks the article published and makes it available to fetch; nothing is pushed to your servers.

The API is read-only. There are no endpoints that create, change, or delete anything.

Let your AI write the sync

You don't have to write the puller yourself. Copy this prompt into your AI coding assistant (Cursor, Claude Code, Copilot, ChatGPT) and it writes the code that pulls your articles into your project, in your project's own language and style. The full API contract below is baked in.

I use blogr.ai to write and publish articles for my website. blogr.ai has a read-only REST API that returns my articles as JSON: blog posts, and the site pages it designs from its topical map (pricing, comparisons, services, locations). Build the code that pulls them into this project. Match the language, framework, and conventions this project already uses.

The API:
- GET https://blogr.ai/api/v1/articles
- Every request needs my access token in an "Authorization: Bearer" header. Read it from an environment variable or secret store, never hardcode it. I will copy the token from the REST API card in blogr.ai.
- Query parameters: "status" ("published" or "draft"), "type" ("post" by default, "page" for site pages, "all" for both), "per_page" (1 to 100, default 25), and "page".
- The response is standard Laravel pagination JSON: the articles sit under "data", with paging info under "links" and "meta". Follow "links.next" until it is null to read every page.
- The limit is 60 requests per minute; a 429 response means slow down and retry later.
- A 401 response means the token is missing or wrong.

Each article under "data":

{
  "id": 123,
  "title": "Article title",
  "type": "post or page",
  "slug": "article-title",
  "path": "null for a post; the site path for a page, e.g. /compare/acme-alternatives/",
  "status": "published",
  "published": true,
  "published_at": "2026-01-01T00:00:00+00:00",
  "published_url": "https://my-site.com/blog/article-title, or null",
  "content_markdown": "# Markdown body...",
  "content_html": "<h1>HTML body...</h1>",
  "seo": {
    "title": "SEO title, or null",
    "meta_description": "Meta description, or null"
  },
  "og_image_url": "https://... social image, or null",
  "og_image_alt": "Image alt text, or null",
  "target_keyword": "keyword, or null",
  "categories": ["Category"],
  "tags": ["tag"],
  "created_at": "2026-01-01T00:00:00+00:00",
  "updated_at": "2026-01-01T00:00:00+00:00"
}

What the code should do:
1. Fetch my published content ("status=published&type=all") page by page.
2. Create or update each item on my site. Match on "id" or "slug" so a re-run updates existing items instead of duplicating them.
3. A "type" of "post" is a blog post: publish it in the blog as the site already does. A "type" of "page" is a site page: serve it at "path" exactly, outside the blog (for example "/pricing/" at /pricing, "/compare/acme-alternatives/" at /compare/acme-alternatives), never list it among blog posts, and never overwrite a hand-built route that already exists at that path.
4. Render "content_html" as-is, or run "content_markdown" through this project's Markdown pipeline. Use the "seo" fields for the page title and meta description tags.
5. Make it runnable on demand, and on a schedule if this project has one.

When you are done, tell me where I paste my access token and how I trigger the first sync.

Connect the API

  1. In your dashboard, open Setup → Publishing and choose REST API.
  2. Copy the generated access token — it authenticates every request as a bearer token.
  3. Save. A website publishes to one destination, so this replaces any other integration on the website.

Authentication

Send the access token with every request. Each website has its own token, and the token identifies the website — there are no ids to pass:

curl https://blogr.ai/api/v1/articles \
  -H "Authorization: Bearer <your website's access token>" \
  -H "Accept: application/json"

A missing or wrong token gets a 401. Running several websites? Connect the REST API on each one and give every site's consumer its own token.

List your articles

GET /api/v1/articles

Returns the token's website's generated articles — published pieces and waiting drafts — newest first, paginated. Titles still in planning or mid-generation never appear.

Query parameter Meaning
statuspublished — only live articles; draft — only articles not (yet) published. Omit for both.
typepost — blog posts (the default); page — the inner pages your topical map designs; all — both. See Blog posts and inner pages below.
per_pageArticles per page, 1–100. Defaults to 25.
pageThe page number; pagination links also appear in the response meta.

Each article looks like this:

{
  "id": 812,
  "title": "How to Choose a Laravel Hosting Provider",
  "type": "post",
  "slug": "how-to-choose-a-laravel-hosting-provider",
  "path": null,
  "status": "published",
  "published": true,
  "published_at": "2026-07-08T09:30:00+00:00",
  "published_url": "https://example.com/blog/how-to-choose-a-laravel-hosting-provider",
  "content_markdown": "# How to Choose a Laravel Hosting Provider\n\nPicking a host…",
  "content_html": "<h1>How to Choose a Laravel Hosting Provider</h1>…",
  "seo": {
    "title": "How to Choose a Laravel Hosting Provider (2026 Guide)",
    "meta_description": "Compare Laravel hosting providers on speed, pricing, and deploys."
  },
  "og_image_url": "https://blogr.ai/storage/articles/laravel-hosting-9f2c41ab.png",
  "og_image_alt": "How to Choose a Laravel Hosting Provider (2026 Guide) — laravel hosting",
  "target_keyword": "laravel hosting",
  "categories": ["Hosting", "Laravel"],
  "tags": ["laravel hosting"],
  "created_at": "2026-07-06T14:02:11+00:00",
  "updated_at": "2026-07-08T09:30:00+00:00"
}

status is always published or draft: an article is either live or it isn't. published_at and published_url are null on drafts.

Blog posts and inner pages

The API returns two kinds of content. Check type on every item before you place it.

Blog posts (type "post") belong in your blog. Inner pages (type "page") are the site-level pages your topical map designs — pricing, comparisons, services, locations — and belong on the site itself, at the path in the path field, outside the blog.

By default the API lists posts only, so a consumer built before pages existed keeps working unchanged. Add type=page to list pages, or type=all to sync both in one pass:

GET /api/v1/articles?status=published&type=all

A page looks like a post, with type and path set:

{
  "id": 913,
  "title": "Acme Alternatives: 7 CRMs Compared",
  "type": "page",
  "slug": "acme-alternatives",
  "path": "/compare/acme-alternatives/",
  "status": "published",
  "published": true,
  "content_html": "<h1>Acme Alternatives</h1>…",
  "seo": { "title": "…", "meta_description": "…" },
  "categories": [],
  "tags": []
}
Blog post Inner page
typepostpage
pathnullThe site path, slashes on both ends — /pricing/, /compare/acme-alternatives/
slugFrom the SEO settings, or derived from the titleThe last segment of path
Where it goesYour blog, e.g. /blog/{slug}Exactly at path, never listed among posts. Leave a hand-built route at that path alone.
Listed byThe default, or type=posttype=page, or type=all for both

Rate limits

Requests are limited to 60 per minute per account, across all your websites and tokens. Exceeding the limit returns a 429 with a Retry-After header; the X-RateLimit-Remaining header on every response shows what's left in the current window.

Errors

Status Meaning
401Missing or invalid access token.
422An invalid query parameter (e.g. an unknown status value).
429Rate limit exceeded — wait for the Retry-After seconds and try again.

Good to know

  • Your token is stored encrypted and never shown again after saving — save a new value anytime to rotate it, and update your consumer.
  • Prefer a push instead? The Webhooks integration delivers the same article data to your endpoint the moment it publishes.