Public Endpoints

Postnomic provides unauthenticated public endpoints for accessing published blog content. These endpoints are served by the PublicBlogsController, PublicPostsController, and PublicAnalyticsController ...

Overview

Postnomic provides unauthenticated public endpoints for accessing published blog content. These endpoints are served by the PublicBlogsController, PublicPostsController, and PublicAnalyticsController and do not require JWT or API Key authentication.

Public endpoints are ideal for:

  • Building custom frontends that display blog content
  • Static site generators that fetch content at build time
  • Third-party integrations that need read-only access

The API is mounted at the root of its host — there is no /api path prefix. Public routes begin with /public/.

Blog Information

Get Blog Info

Retrieve public information about a blog by its slug.

curl -X GET https://api.postnomic.com/public/blogs/my-dev-blog

Response:

{
  "name": "My Dev Blog",
  "slug": "my-dev-blog",
  "description": "A blog about software development",
  "defaultLayout": "Default",
  "showBranding": true
}

Get Tags, Categories, and Authors

curl -X GET https://api.postnomic.com/public/blogs/my-dev-blog/tags
curl -X GET https://api.postnomic.com/public/blogs/my-dev-blog/categories
curl -X GET https://api.postnomic.com/public/blogs/my-dev-blog/authors
curl -X GET https://api.postnomic.com/public/blogs/my-dev-blog/authors/jane-developer

Tags and categories are returned as objects with name, slug, and postCount:

[
  { "name": "Docker", "slug": "docker", "postCount": 7 },
  { "name": "DevOps", "slug": "devops", "postCount": 12 }
]

The tag list is capped at the blog's configured sidebar tag limit (most-used first, then sorted alphabetically). Pass ?limit=0 for the full list.

Posts

List Published Posts

Retrieve a paginated list of published posts for a blog.

curl -X GET "https://api.postnomic.com/public/blogs/my-dev-blog/posts?page=1&pageSize=10"

Response:

{
  "items": [
    {
      "slug": "getting-started-with-docker",
      "title": "Getting Started with Docker",
      "excerpt": "Learn the fundamentals of containerization...",
      "thumbnailImageUrl": "/media/blob/blog-media/my-dev-blog/docker-thumb.jpg",
      "authorName": "Jane Developer",
      "authorSlug": "jane-developer",
      "publishedAt": "2026-03-01T10:00:00Z",
      "commentCount": 4,
      "language": "en",
      "availableLanguages": ["en", "de"],
      "tags": [
        { "name": "Docker", "slug": "docker", "postCount": 7 }
      ],
      "categories": [
        { "name": "Tutorials", "slug": "tutorials", "postCount": 21 }
      ]
    }
  ],
  "page": 1,
  "pageSize": 10,
  "totalCount": 42,
  "totalPages": 5
}

Get Post by Slug

Retrieve the full content of a specific published post. Approved comments are embedded in this response — there is no separate public comments endpoint.

curl -X GET https://api.postnomic.com/public/blogs/my-dev-blog/posts/getting-started-with-docker

Response:

{
  "slug": "getting-started-with-docker",
  "title": "Getting Started with Docker",
  "content": "<h2>Introduction</h2><p>Docker is a platform for...</p>",
  "excerpt": "Learn the fundamentals of containerization...",
  "canonicalUrl": "https://example.com/blog/post/getting-started-with-docker",
  "coverImageUrl": "/media/blob/blog-media/my-dev-blog/docker-cover.jpg",
  "authorName": "Jane Developer",
  "authorSlug": "jane-developer",
  "publishedAt": "2026-03-01T10:00:00Z",
  "language": "en",
  "availableLanguages": ["en", "de"],
  "commentsEnabled": true,
  "commentRequireModeration": true,
  "commentRequireFirstname": true,
  "commentRequireLastname": false,
  "commentRequireEmail": true,
  "commentRequirePhone": false,
  "commentRequireSubject": false,
  "tags": [
    { "name": "Docker", "slug": "docker", "postCount": 7 }
  ],
  "categories": [
    { "name": "Tutorials", "slug": "tutorials", "postCount": 21 }
  ],
  "comments": [
    {
      "publicId": "d4e5f6a7-b8c9-0123-def4-56789012345a",
      "subject": "Great intro",
      "body": "This cleared up a lot for me, thanks.",
      "authorName": "Sam Reader",
      "createdAt": "2026-03-02T08:15:00Z",
      "replies": []
    }
  ]
}

Comments are nested: each comment carries its own replies collection.

Query Parameters

The post list accepts the following query parameters:

Parameter Type Default Notes
page int 1 Page number.
pageSize int 5 Values below 1 fall back to 5; values above 50 are capped at 50.
tag string Filter by tag slug.
category string Filter by category slug.
author string Filter by the author's full name ("Firstname Lastname"), matched exactly. Note this is the name, not the authorSlug returned on each post.
search string Substring match against post title, excerpt, and content.
lang string Requested language (ISO-639-1).
# Filter by category
curl -X GET "https://api.postnomic.com/public/blogs/my-dev-blog/posts?category=tutorials"

# Filter by tag
curl -X GET "https://api.postnomic.com/public/blogs/my-dev-blog/posts?tag=docker"

# Filter by author (full name)
curl -X GET "https://api.postnomic.com/public/blogs/my-dev-blog/posts?author=Jane%20Developer"

# Search
curl -X GET "https://api.postnomic.com/public/blogs/my-dev-blog/posts?search=containers"

# Request German content
curl -X GET "https://api.postnomic.com/public/blogs/my-dev-blog/posts?lang=de"

lang is also accepted on the post detail endpoint. When no lang is given, the Accept-Language header is used; if neither resolves to an available translation, the blog's default language is served. A missing translation falls back to the default language rather than returning 404. The language field on the response reports the language actually served.

curl -X GET "https://api.postnomic.com/public/blogs/my-dev-blog/posts/top-commented?count=3"
curl -X GET "https://api.postnomic.com/public/blogs/my-dev-blog/posts/most-read?count=3"

Both return a list of { "slug", "title", "count" } objects.

Comments

Submit a Comment

curl -X POST https://api.postnomic.com/public/blogs/my-dev-blog/posts/getting-started-with-docker/comments \
  -H "Content-Type: application/json" \
  -d '{ "authorFirstname": "Sam", "authorEmail": "sam@example.com", "body": "Nice write-up." }'

Which fields are mandatory depends on the blog's comment settings, which the post detail response exposes as the commentRequire* flags. If the blog requires moderation, the comment is created as Pending and is not returned by public reads until it is approved.

Analytics

The public analytics endpoints are write-only. They record visitor activity; they do not read view counts back.

# Record a page view
curl -X POST https://api.postnomic.com/public/blogs/my-dev-blog/analytics/pageview \
  -H "Content-Type: application/json" \
  -d '{ "sessionId": "5f2c...", "postSlug": "getting-started-with-docker" }'

A PATCH on the same path updates the recorded read duration for a session. View counts are read through the authenticated analytics endpoints in the dashboard.

Response Format

All public endpoints return JSON responses with consistent field naming:

  • Posts, blogs, tags, and categories are addressed by slug — public responses do not expose publicId (comments are the exception; a comment's publicId is needed to reply to it)
  • Dates are formatted in ISO 8601 UTC (e.g., 2026-03-01T10:00:00Z)
  • Image URLs are relative paths through the blob proxy (e.g., /media/blob/blog-media/...), which you resolve against the API base address
  • Pagination uses page, pageSize, totalCount, and totalPages fields alongside items

Published Content Only

Public endpoints return only posts in the Published status. Drafts, scheduled, unpublished, and archived posts are not reachable through them. Posts cross-posted to a blog are included alongside the blog's own posts.

Was this article helpful?

Thank you for your feedback!