API Documentation

Everything you need to fetch your blog content via the Blotd API.

Getting Started

  1. Sign up - Create a free account at blotd.com/login
  2. Get your API key - Go to Dashboard > API Keys and copy your key
  3. Make your first request - See the example below

Authentication

All API requests require a Bearer token in the Authorization header:

Header
Authorization: Bearer blotd_xxxxxxxxxxxx

Keep your API key safe

Never expose your API key in client-side code, public repositories, or browser requests. Store it as an environment variable (e.g. BLOTD_API_KEY) and only use it in server-side code such as API routes, server components, or build scripts. If you believe a key has been compromised, revoke it immediately from your dashboard and create a new one.

API Key Scoping

Each API key only returns articles assigned to it. This lets you manage content for multiple websites or clients from a single Blotd account.

How it works

  1. Create separate API keys - Go to Dashboard > API Keys and create one key per website or client (e.g. "Marketing Site", "Partner Portal", "Mobile App").
  2. Assign articles to keys - When creating or editing an article, use the "API Key Access" panel in the sidebar to select which keys can access that article.
  3. Fetch with the right key - Each key only returns its assigned articles. An article assigned to "Marketing Site" won't appear in API responses for "Partner Portal".

Default behavior

If you leave all keys unchecked when creating an article, that article will be accessible by all of your API keys. This preserves backward compatibility and is the default for existing articles.

Example: Multi-site setup
// Marketing site — uses its own key, only gets marketing articles
const marketing = await fetch(
  "https://api.blotd.com/v1/articles",
  {
    headers: {
      Authorization: `Bearer ${process.env.MARKETING_BLOTD_KEY}`,
    },
  }
);

// Partner portal — different key, different articles
const partner = await fetch(
  "https://api.blotd.com/v1/articles",
  {
    headers: {
      Authorization: `Bearer ${process.env.PARTNER_BLOTD_KEY}`,
    },
  }
);

Base URL

https://api.blotd.com/v1

Endpoints

GET/articles

List all published articles, paginated.

Query Parameters

ParamTypeDefaultDescription
pagenumber1Page number
limitnumber10Items per page (max 50)
sortstringnewestnewest or oldest
tagstring-Filter by tag

Example

const res = await fetch(
  "https://api.blotd.com/v1/articles?page=1&limit=10",
  {
    headers: {
      Authorization: "Bearer blotd_xxxxxxxxxxxx",
    },
  }
);
const { data, pagination } = await res.json();
GET/articles/:slug

Get a single article by its slug.

const res = await fetch(
  "https://api.blotd.com/v1/articles/my-first-post",
  {
    headers: {
      Authorization: "Bearer blotd_xxxxxxxxxxxx",
    },
  }
);
const { data } = await res.json();
GET/articles/tag/:tag

Filter articles by tag. Supports the same pagination params as /articles.

GET/articles/search?q=query

Search articles by title, content, or tags.

Response Format

All responses follow this shape:

{
  "success": true,
  "data": { ... },
  "pagination": {
    "page": 1,
    "limit": 10,
    "total": 42,
    "totalPages": 5
  }
}

Rate Limits

PlanMonthly LimitExceeded
Free1,000 requests429 Too Many Requests
Pro25,000 requests429 Too Many Requests

Code Examples

Next.js (App Router)

app/blog/page.tsx
async function getArticles() {
  const res = await fetch(
    "https://api.blotd.com/v1/articles",
    {
      headers: {
        Authorization: `Bearer ${process.env.BLOTD_API_KEY}`,
      },
      next: { revalidate: 60 },
    }
  );
  return res.json();
}

export default async function BlogPage() {
  const { data } = await getArticles();

  return (
    <div>
      {data.map((post) => (
        <article key={post.id}>
          <h2>{post.title}</h2>
          <p>{post.readingTime}</p>
          <div dangerouslySetInnerHTML={{ __html: post.content }} />
        </article>
      ))}
    </div>
  );
}

Astro

src/pages/blog.astro
---
const res = await fetch(
  "https://api.blotd.com/v1/articles",
  {
    headers: {
      Authorization: `Bearer ${import.meta.env.BLOTD_API_KEY}`,
    },
  }
);
const { data: posts } = await res.json();
---

{posts.map((post) => (
  <article>
    <h2>{post.title}</h2>
    <Fragment set:html={post.content} />
  </article>
))}

Plain JavaScript

fetch("https://api.blotd.com/v1/articles", {
  headers: {
    Authorization: "Bearer blotd_xxxxxxxxxxxx",
  },
})
  .then((res) => res.json())
  .then(({ data }) => {
    data.forEach((post) => {
      console.log(post.title, post.slug);
    });
  });

Best Practices

Cache responses server-side

Blog content changes infrequently. Cache API responses so one request serves thousands of visitors. In Next.js, use revalidate for time-based caching:

await fetch(url, { next: { revalidate: 60 } })

Use on-demand revalidation for zero waste

Even better than time-based caching: tag your fetches and only invalidate when content actually changes. Your pages stay fully cached until you publish, update, or delete an article.

// In your page (cache indefinitely)
await fetch(url, { next: { tags: ["blog"] } })

// In your publish/update handler (bust the cache)
import { revalidateTag } from "next/cache"
revalidateTag("blog")

Fetch only what you need

Use ?limit=5 if your homepage only shows 5 posts. Filter by tag with ?tag=tutorials for specific sections. Smaller responses mean faster loads and fewer wasted API calls.

Handle errors gracefully

Always handle 401, 404, and 429 responses. Show fallback UI when the API is unreachable, a friendly message for missing articles, and a retry prompt when rate-limited. Never let an API error crash your page.

Error Codes

CodeMeaning
200Success
400Bad request (missing params)
401Unauthorized (missing or invalid API key)
404Article not found
429Rate limit exceeded
500Server error