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
});| Parameter | Type | Default | Description |
|---|---|---|---|
page | number | 1 | Page number |
limit | number | 20 | Results per page (max: 100) |
category | string | -- | Filter by category slug |
tag | string | -- | Filter by tag slug |
search | string | -- | Search by title (case-insensitive) |
websiteId | string | -- | 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 contentReturns 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",
});| Parameter | Type | Default | Description |
|---|---|---|---|
websiteId | string | -- | 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",
});| Parameter | Type | Default | Description |
|---|---|---|---|
websiteId | string | -- | 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).
| Property | Type | Description |
|---|---|---|
message | string | Human-readable error description from the API |
status | number | HTTP 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.
| Property | Type | Description |
|---|---|---|
message | string | Always "Rate limit exceeded" |
status | number | Always 429 |
retryAfter | number | Seconds 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;
}