WrytzeWrytze Docs
SDK

Usage

Complete guide to every method, parameter, and type in the Wrytze TypeScript SDK.

This page documents every method on the WrytzeClient, the parameters each method accepts, the shape of every response, and how to handle errors. All examples assume you have already installed the SDK and created a client:

import { WrytzeClient } from "@wrytze/sdk";

const wrytze = new WrytzeClient({
  apiKey: process.env.WRYTZE_API_KEY!,
});

Blogs

The wrytze.blogs resource lets you retrieve published blog posts.

List blogs

const { data, pagination } = await wrytze.blogs.list();

Returns a paginated list of published blogs ordered by publish date (newest first). The response does not include the contentHtml field -- use get or getBySlug to fetch full content.

Parameters

All parameters are optional:

const { data, pagination } = await wrytze.blogs.list({
  page: 2,                    // Page number (default: 1)
  limit: 10,                  // Results per page, max 100 (default: 20)
  category: "engineering",    // Filter by category slug
  tag: "nextjs",              // Filter by tag slug
  search: "getting started",  // Search by title (case-insensitive)
  websiteId: "ws_abc123",     // Filter to a specific website
});
ParameterTypeDefaultDescription
pagenumber1Page number
limitnumber20Results per page (max: 100)
categorystring--Filter by category slug
tagstring--Filter by tag slug
searchstring--Search by title (case-insensitive)
websiteIdstring--Scope results to a specific website

Pagination example

async function getAllBlogs() {
  const allBlogs = [];
  let page = 1;

  while (true) {
    const { data, pagination } = await wrytze.blogs.list({ page, limit: 100 });
    allBlogs.push(...data);

    if (page >= pagination.pages) break;
    page++;
  }

  return allBlogs;
}

Get blog by ID

const { data: blog } = await wrytze.blogs.get("cm5abc123def456");

console.log(blog.title);
console.log(blog.contentHtml); // Full HTML content

Returns a single BlogDetail object, which includes the contentHtml field with the full rendered blog content.

Get blog by slug

const { data: blog } = await wrytze.blogs.getBySlug(
  "building-a-modern-blog",
  { websiteId: "ws_abc123" }
);

console.log(blog.title);
console.log(blog.contentHtml);

Looks up a blog by its URL-friendly slug. The websiteId parameter is required because slugs are unique per website, not globally.

Use getBySlug when building dynamic blog pages with URL-based routing (e.g. /blog/[slug] in Next.js). Use get when you already have the blog ID from a previous list request.


Categories

The wrytze.categories resource lets you retrieve content categories.

List categories

const { data: categories } = await wrytze.categories.list();

for (const category of categories) {
  console.log(`${category.name} (${category.slug})`);
  if (category.parentId) {
    console.log(`  Child of: ${category.parentId}`);
  }
}

Returns all categories for your organization. Optionally filter by website:

const { data: categories } = await wrytze.categories.list({
  websiteId: "ws_abc123",
});
ParameterTypeDefaultDescription
websiteIdstring--Scope results to a specific website

Tags

The wrytze.tags resource lets you retrieve content tags.

List tags

const { data: tags } = await wrytze.tags.list();

for (const tag of tags) {
  console.log(`${tag.name} (${tag.color})`);
}

Returns all tags for your organization. Optionally filter by website:

const { data: tags } = await wrytze.tags.list({
  websiteId: "ws_abc123",
});
ParameterTypeDefaultDescription
websiteIdstring--Scope results to a specific website

Error handling

The SDK throws typed errors for all API failures. Import WrytzeError and RateLimitError to handle them:

import { WrytzeClient, WrytzeError, RateLimitError } from "@wrytze/sdk";

const wrytze = new WrytzeClient({ apiKey: process.env.WRYTZE_API_KEY! });

try {
  const { data } = await wrytze.blogs.get("nonexistent_id");
} catch (error) {
  if (error instanceof RateLimitError) {
    console.error(`Rate limited. Retry after ${error.retryAfter} seconds.`);
  } else if (error instanceof WrytzeError) {
    console.error(`API error (${error.status}): ${error.message}`);
  } else {
    throw error; // Re-throw unexpected errors
  }
}

WrytzeError

Thrown for any non-2xx API response (except 429).

PropertyTypeDescription
messagestringHuman-readable error description from the API
statusnumberHTTP status code (e.g. 401, 404, 500)

RateLimitError

Extends WrytzeError. Thrown when the API returns HTTP 429 (rate limit exceeded). The Wrytze API allows 100 requests per minute per API key.

PropertyTypeDescription
messagestringAlways "Rate limit exceeded"
statusnumberAlways 429
retryAfternumberSeconds to wait before retrying

Retry with backoff

Here is a helper that automatically retries on rate limit errors:

import { RateLimitError } from "@wrytze/sdk";

async function withRetry<T>(fn: () => Promise<T>, maxRetries = 3): Promise<T> {
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
    try {
      return await fn();
    } catch (error) {
      if (error instanceof RateLimitError && attempt < maxRetries) {
        const delay = error.retryAfter * 1000;
        console.log(`Rate limited. Retrying in ${error.retryAfter}s...`);
        await new Promise((resolve) => setTimeout(resolve, delay));
        continue;
      }
      throw error;
    }
  }
  throw new Error("Unreachable");
}

// Usage
const { data } = await withRetry(() => wrytze.blogs.list({ limit: 50 }));

TypeScript types

The SDK exports all types used in requests and responses. Import them directly for use in your application code:

import type {
  Blog,
  BlogDetail,
  Category,
  Tag,
  Pagination,
  ListResponse,
  SingleResponse,
  ListBlogsParams,
  ListResourceParams,
  GetBySlugParams,
  WrytzeClientConfig,
} from "@wrytze/sdk";

Blog

Returned in list responses. Does not include HTML content.

interface Blog {
  id: string;
  title: string;
  slug: string;
  excerpt: string | null;
  metaTitle: string | null;
  metaDescription: string | null;
  featuredImageUrl: string | null;
  featuredImageAlt: string | null;
  wordCount: number;
  readingTimeMinutes: number;
  publishedAt: string;           // ISO 8601 timestamp
  websiteId: string;
  categories: { name: string; slug: string }[];
  tags: { name: string; slug: string }[];
}

BlogDetail

Returned by get and getBySlug. Extends Blog with the full HTML content.

interface BlogDetail extends Omit<Blog, "categories" | "tags"> {
  contentHtml: string;           // Full rendered HTML
  categories: { id: string; name: string; slug: string }[];
  tags: { id: string; name: string; slug: string }[];
}

Category

interface Category {
  id: string;
  name: string;
  slug: string;
  description: string;
  parentId: string | null;       // null for top-level categories
  sortOrder: number;
  websiteId: string;
}

Tag

interface Tag {
  id: string;
  name: string;
  slug: string;
  color: string;                 // Hex color code
  websiteId: string;
}

Pagination

Included in every list response.

interface Pagination {
  page: number;                  // Current page number
  limit: number;                 // Results per page
  total: number;                 // Total matching results
  pages: number;                 // Total number of pages
}

Response envelopes

// Wraps list responses (blogs.list, categories.list, tags.list)
interface ListResponse<T> {
  data: T[];
  pagination: Pagination;
}

// Wraps single-resource responses (blogs.get, blogs.getBySlug)
interface SingleResponse<T> {
  data: T;
}

On this page