docs · api
EN
Get started

Authentication

The public API uses API keys. Every request carries the blog's key in the X-API-Key header, and that key defines exactly which content you can read.

How to send the key

Include the X-API-Key header on every request, even simple reads:

curl https://api.draftin.io/public/posts \
  -H "X-API-Key: dra_xxxxxxxxxxxxxxxxxxxxxxxxxxx"

Key scope

An API key is bound to one blog. That means:

  • All responses are automatically filtered to the blog that owns the key — you don't pass a blogId to the public endpoints.
  • The endpoints return published content only (status PUBLISHED). Drafts and scheduled posts are never exposed.
  • To serve several blogs, use one key per blog.

Where to get the key

The API key is generated and managed in the DraftIn panel, under the blog's settings. There you can also rotate it (revoke the current one and generate a new one) if you suspect a leak.

When authentication fails

Requests without the key, or with an invalid key, return 401 Unauthorized. The body follows the standard error envelope:

401 Unauthorized
{
  "success": false,
  "error": {
    "message": "API key required for public routes",
    "code": "UNAUTHORIZED",
    "type": "authentication",
    "statusCode": 401,
    "retryable": false,
    "requestId": "req_5c26469a-1234-4c8b-9e21-370450bbda3a",
    "docs": "https://docs.draftin.io/errors/UNAUTHORIZED"
  }
}

Best practices

  • Keep the key on the server. On Astro/Next sites, make the calls at build time or in server routes; don't embed the key in client JavaScript.
  • Use environment variables. Load the key from process.env and never commit it to Git.
  • One key per environment. Separate development and production keys where possible to limit the impact of a leak.
  • Rotate on suspicion. If the key shows up in a log, commit or public bundle, rotate it immediately in the panel.
  • Cache responses. Published content changes rarely; caching reduces calls and protects against spikes. See Rate limits.

A reusable client

Centralize the key and error handling in a small client — so each call stays lean and consistent:

TypeScript
// draftin.ts — a minimal, reusable client
const BASE = "https://api.draftin.io";

export function createDraftinClient(apiKey: string) {
  async function request<T>(path: string): Promise<T> {
    const res = await fetch(BASE + path, {
      headers: { "X-API-Key": apiKey },
    });
    if (res.status === 401) throw new Error("Invalid or missing API key.");
    if (!res.ok) throw new Error(`DraftIn API: ${res.status}`);
    const { data } = (await res.json()) as { data: T }; // unwrap the envelope
    return data;
  }

  return {
    listPosts: (limit = 10, offset = 0) =>
      request(`/public/posts?limit=${limit}&offset=${offset}`),
    getPostBySlug: (slug: string) =>
      request(`/public/posts/slug/${encodeURIComponent(slug)}`),
    listCategories: () => request("/public/categories"),
    listTags: () => request("/public/tags"),
  };
}

// usage
const draftin = createDraftinClient(process.env.DRAFTIN_API_KEY!);
const posts = await draftin.listPosts(5);