Reference

REST API

The module offers two HTTP APIs.

  • The native Omeka S API resources blog_posts, blog_categories, blog_tags and blog_comments under /api/. These follow the standard Omeka S conventions (JSON-LD, page and per_page, API key authentication with key_identity and key_credential query parameters) and support full create, read, update and delete with role-based permissions. Prefer these for integrations.
  • The legacy JSON endpoints under /api/blog/, which return plain JSON, only expose published posts, and accept comments. They authenticate with the browser session only; Omeka API keys are not accepted on these routes.

If the API Browser module is installed, the four native resources appear in it with their parameters.

Native resources

Permissions

Resource search, read create update delete
blog_posts everyone Editor and above Editor and above Editor and above
blog_categories everyone Editor and above Editor and above Editor and above
blog_tags everyone not available not available not available
blog_comments everyone everyone Editor and above Editor and above

"Everyone" includes anonymous requests, but what a read returns depends on the caller's role:

  • Editor and above (Editor, Site Administrator, Global Administrator, authenticated with an API key) see every post and comment and may filter by any status.
  • Everyone else (anonymous requests, and Researcher or Author keys) gets exactly what the public site shows. blog_posts search is forced to status=published and exclude_future=true, and GET /api/blog_posts/{id} answers 404 for a draft, pending or scheduled post. blog_comments search is forced to status=approved, GET /api/blog_comments/{id} answers 404 for a pending or spam comment, and the o:author_email field is omitted.

Any status or exclude_future value such callers send is ignored rather than rejected.

Authenticate write requests with an API key created under Admin > Users > (user) > Edit > API keys:

/api/blog_posts?key_identity=IDENTITY&key_credential=CREDENTIAL

Posts: /api/blog_posts

GET /api/blog_posts lists posts, newest first, with Omeka-S-Total-Results in the response headers.

Parameter Type Description
status string draft, pending or published. Honoured for Editor and above only; other callers always get published.
exclude_future boolean Omit posts whose published_at is in the future. Always on for callers below Editor. Editors combine it with status=published to get exactly what the public site shows.
site_id integer Only posts assigned to this site.
category_id integer Filter by category ID.
category_slug string Filter by category slug (takes precedence over category_id).
tag_slug string Filter by tag slug.
author_id integer Filter by author user ID.
search string Substring match against title, excerpt and content.
page integer Page number, default 1.
per_page integer Results per page, default 10.

GET /api/blog_posts/{id} returns one post:

{
  "@type": "o:BlogPost",
  "o:id": 1,
  "o:title": "Welcome to the Blog",
  "o:slug": "welcome-to-the-blog",
  "o:excerpt": "<p>The blog module is now installed and ready.</p>",
  "o:content": "<p>Welcome!</p>",
  "o:status": "published",
  "o:author_id": 1,
  "o:author_name": "Jane Editor",
  "o:category_id": 1,
  "o:category_name": "Announcements",
  "o:category_slug": "announcements",
  "o:featured_image_asset_id": null,
  "o:featured_image_url": null,
  "o:published_at": "2026-03-03 00:29:00",
  "o:created_at": "2026-03-03 00:29:39",
  "o:updated_at": "2026-03-04 00:38:56",
  "o:tags": [{ "id": 2, "name": "getting-started", "slug": "getting-started" }]
}

POST /api/blog_posts creates a post. The authenticated user becomes the author.

Body field Required Description
title yes
slug no Generated from the title when omitted.
excerpt, content no HTML strings.
status no draft (default), pending or published.
category_id no Category ID. Unlike the admin form, the API does not require one.
featured_image_asset_id no Omeka asset ID.
featured_image_url no Image URL used by the templates. The API does not derive it from the asset ID; set both if you want the image to display.
published_at no YYYY-MM-DD HH:MM:SS. Defaults to now when status is published.
tags no Array of tag names. Missing tags are created.
site_ids no Array of site IDs the post is visible on. Without it the post is not shown on any site.
curl -X POST "https://example.org/api/blog_posts?key_identity=ID&key_credential=SECRET" \
  -H "Content-Type: application/json" \
  -d '{"title":"Hello","content":"<p>World</p>","status":"published","category_id":1,"tags":["news"],"site_ids":[1]}'

PUT or PATCH /api/blog_posts/{id} updates a post. Both behave as partial updates: only the fields present in the body change. tags replaces the whole tag list, site_ids replaces the site assignments, and sending status: "published" without published_at sets the publish time to now.

DELETE /api/blog_posts/{id} deletes the post and its comments.

Categories: /api/blog_categories

  • GET /api/blog_categories returns all categories ordered by name (no filters).
  • GET /api/blog_categories/{id} returns o:name, o:slug and o:description.
  • POST /api/blog_categories with name (required), optional slug and description.
  • PUT or PATCH /api/blog_categories/{id} with any of name, slug, description.
  • DELETE /api/blog_categories/{id} removes the category; its posts become uncategorised.

Tags: /api/blog_tags

Read-only. GET /api/blog_tags lists all tags ordered by name; GET /api/blog_tags/{id} returns o:name and o:slug. Tags are created through the tags field of a post.

Comments: /api/blog_comments

GET /api/blog_comments accepts status (pending, approved, spam), post_id, page and per_page (default 50). Each comment has o:post_id, o:author_name, o:author_email, o:body, o:status and o:posted_at. Callers below Editor only receive approved comments, whatever status they send, and o:author_email is left out of their responses; GET /api/blog_comments/{id} answers 404 for a pending or spam comment.

POST /api/blog_comments creates a pending comment and is open to anonymous requests. Body: post_id, author_name, author_email, body (all required). The post must be published, otherwise the request fails with a validation error. This resource does not check the Enable comments setting.

PUT or PATCH /api/blog_comments/{id} moderates a comment. The only supported field is status with the value approved or spam; any other field is ignored and any other status value is rejected.

DELETE /api/blog_comments/{id} removes the comment.

Legacy endpoints under /api/blog/

These return plain JSON (not JSON-LD) and are always public for reading.

GET /api/blog/posts

Published posts only, newest first.

Parameter Description
page Page number, default 1.
per_page Default 10, maximum 100.
tag_slug, category_slug, author_id, search Same meaning as for blog_posts.

Scheduled (future-dated) posts are included, and there is no site filter.

{
  "data": [
    {
      "id": 1, "title": "Welcome to the Blog", "slug": "welcome-to-the-blog",
      "excerpt": "...", "content": "...", "status": "published",
      "author_id": 1, "category_id": 1, "tags": ["getting-started"],
      "published_at": "2026-03-03 00:29:00",
      "created_at": "2026-03-03 00:29:39", "updated_at": "2026-03-04 00:38:56"
    }
  ],
  "total": 2, "page": 1, "per_page": 10, "total_pages": 1
}

GET /api/blog/posts/{id}

One published post in the same shape. Unpublished posts return 404.

POST /api/blog/posts

Creates a post for the logged-in user (browser session required; anonymous requests get 401). title and content are required; slug, excerpt, status, category_id, published_at, featured_image_asset_id, featured_image_url and tags (array or comma-separated string) are optional. Site assignments cannot be set here. Responds with 201 and the post. Any logged-in role is accepted by this endpoint; use the native blog_posts resource when you need role enforcement.

POST /api/blog/posts/{id}/comments

Submits a comment for moderation. Body: author_name, author_email, body. Responds with 201 {"id": 12, "status": "pending"}, 403 when comments are disabled, 422 when a field is missing or the post is not published, and 405 for non-POST requests.

curl -X POST https://example.org/api/blog/posts/1/comments \
  -H "Content-Type: application/json" \
  -d '{"author_name":"Alice","author_email":"alice@example.org","body":"Great post!"}'
Log in for Support