API Documentation

Everything you need to integrate BlogSaas into your application.

Authentication

All API requests require an API key passed via the X-API-Key header. Create API keys from the dashboard.

curl -H "X-API-Key: YOUR_API_KEY" \
  https://api-blogsaas.figoodle.com/api/v1/posts

Base URL

https://api-blogsaas.figoodle.com

Blog API

Fetch and display blog posts on your site.

GET/api/v1/posts

Returns a paginated list of published blog posts. Pinned posts appear first, then ordered by publish date (newest first).

Query Parameters

ParamTypeDefaultDescription
pageinteger1Page number
pageSizeinteger9Posts per page
categorystringFilter by category name

Example Request

curl -H "X-API-Key: YOUR_API_KEY" \
  "https://api-blogsaas.figoodle.com/api/v1/posts?page=1&pageSize=9&category=Tech"

Response

{
  "items": [
    {
      "id": "uuid",
      "title": "Post Title",
      "slug": "post-title",
      "excerpt": "Short summary...",
      "cover_image_url": "https://api-host/uploads/image.webp",
      "category": "Tech",
      "author_name": "John",
      "is_pinned": false,
      "published_at": "2026-03-14T00:00:00Z",
      "cta_url": "https://...",
      "cta_text": "Try It Free",
      "tags": ["tag1", "tag2"]
    }
  ],
  "total_count": 42,
  "page": 1,
  "page_size": 9
}
GET/api/v1/posts/:slug

Returns a single published blog post by its slug, including full markdown content. Each request increments the view count.

URL Parameters

ParamTypeDescription
slugstringThe post's URL slug (from list endpoint)

Example Request

curl -H "X-API-Key: YOUR_API_KEY" \
  "https://api-blogsaas.figoodle.com/api/v1/posts/my-blog-post-slug"

Response

{
  "id": "uuid",
  "title": "Post Title",
  "slug": "post-title",
  "content": "Full markdown content...",
  "excerpt": "Short summary...",
  "cover_image_url": "https://api-host/uploads/image.webp",
  "meta_title": "SEO Title",
  "meta_description": "SEO description...",
  "keywords": "seo, keywords",
  "category": "Tech",
  "author_name": "John",
  "is_pinned": false,
  "cta_url": "https://...",
  "cta_text": "Try It Free",
  "published_at": "2026-03-14T00:00:00Z",
  "view_count": 123,
  "tags": ["tag1", "tag2"],
  "image_urls": ["https://api-host/uploads/img1.webp"]
}

Pagination

Use page and pageSize query parameters. The response includes total_count so you can calculate total pages:

const totalPages = Math.ceil(data.total_count / data.page_size);

Response Fields Reference

FieldTypeDescription
idstring (uuid)Unique post identifier
titlestringPost title
slugstringURL-safe slug
contentstringFull markdown content (single post only)
excerptstring?Short summary (max 500 chars)
cover_image_urlstring?Cover image URL
meta_titlestring?SEO title (single post only)
meta_descriptionstring?SEO description (single post only)
keywordsstring?Comma-separated SEO keywords (single post only)
categorystring?Post category
author_namestring?Author display name
is_pinnedbooleanWhether post is pinned to top
cta_urlstring?Call-to-action link URL
cta_textstring?Call-to-action button text
published_atstring (ISO 8601)Publish date
view_countintegerTotal views (single post only)
tagsstring[]Array of tag strings
image_urlsstring[]Embedded image URLs (single post only)

Rendering Blog Content

Blog content is returned as Markdown. To render it properly on your site, you need a markdown library and typography CSS.

1. Install Dependencies

# React / Next.js / Vite
npm install marked

# For Tailwind CSS typography (proper spacing, fonts, lists)
npm install @tailwindcss/typography

2. Configure Tailwind

// Tailwind v4 (globals.css)
@import "tailwindcss";
@plugin "@tailwindcss/typography";

// Tailwind v3 (tailwind.config.js)
import typography from '@tailwindcss/typography'
export default {
  plugins: [typography],
}

3. Render Content

import { marked } from 'marked';

marked.setOptions({ breaks: true, gfm: true });

// In your component:
<div
  className="prose prose-lg max-w-none"
  dangerouslySetInnerHTML={{
    __html: marked.parse(post.content)
  }}
/>

That's it! The prose class from Tailwind Typography handles all heading sizes, list spacing, paragraph margins, image styling, link colors, and code blocks automatically.

Subscribers API

Manage your subscriber list programmatically.

POST/api/v1/subscribers

Add a subscriber. If the email already exists and was unsubscribed, it reactivates them.

Request Body

{
  "email": "user@example.com",
  "name": "Jane Doe",
  "source": "api",
  "tags": "customers,trial"
}

Example

curl -X POST -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"email":"user@example.com","name":"Jane"}' \
  https://api-blogsaas.figoodle.com/api/v1/subscribers
POST/api/v1/subscribers/batch

Import up to 1000 subscribers at once. Returns counts of created, updated, and skipped. Existing subscribers are updated (name, metadata, tags). Unsubscribed users are skipped.

Request Body

{
  "subscribers": [
    { "email": "a@example.com", "name": "Alice", "tags": "Active-user" },
    { "email": "b@example.com", "name": "Bob", "tags": "Active-user,Premium" }
  ]
}

Response

{ "created": 2, "updated": 0, "skipped": 0, "errors": [] }

Groups & Tags

The tags field is a comma-separated string of group names (e.g. "Active-user,Premium"). Groups are auto-created if they don't exist yet — no need to create groups manually before importing. Tags set on a subscriber replace any previously assigned tags (not merged). When sending newsletters, you can target subscribers by group name.

GET/api/v1/subscribers

List subscribers with optional filtering by status, tag, or search term.

Query Parameters

ParamDescription
statusFilter: active, unsubscribed, deleted
tagFilter by tag name
searchSearch by email or name
page, pageSizePagination (default 1, 50)
PUT/api/v1/subscribers/:id|DELETE/api/v1/subscribers/:id

Update a subscriber's name, metadata, or tags. DELETE soft-deletes the subscriber.

PUT Body

{ "name": "Updated Name", "tags": "paid,enterprise" }

Newsletters API

Create, send, and manage newsletters via API.

POST/api/v1/newsletters

Create a newsletter draft. Use content_json for block-based content.

Request Body

{
  "subject": "March Product Update",
  "preview_text": "New features this month",
  "content_json": "[{\"type\":\"text\",\"content\":\"Hello!\"}]",
  "target_tags": "customers",
  "show_logo": true,
  "show_footer": true
}
GET/api/v1/newsletters

List newsletters. Filter by status (draft, scheduled, queued, sending, sent, failed).

POST/api/v1/newsletters/:id/send

Queue a draft newsletter for immediate sending. Requires Resend API key configured in site settings.

Example

curl -X POST -H "X-API-Key: YOUR_API_KEY" \
  https://api-blogsaas.figoodle.com/api/v1/newsletters/NEWSLETTER_ID/send

Response

{ "message": "Newsletter queued for sending" }
POST/api/v1/newsletters/:id/schedule

Schedule a draft newsletter for a future date.

Request Body

{ "scheduled_at": "2026-04-01T09:00:00Z" }
GET/api/v1/newsletters/:id/report

Get send report: total recipients, sent count, failed count, and rendered HTML.

Response

{
  "id": "uuid",
  "subject": "March Product Update",
  "status": "sent",
  "total_recipients": 142,
  "sent_count": 140,
  "failed_count": 2,
  "sent_at": "2026-03-15T10:00:00Z"
}

Embed Widget

Drop-in blog widget for any website.

Drop this snippet into any HTML page to embed your blog with zero setup. The widget handles pagination, theming, and post rendering automatically.

<div id="blog"></div>
<script src="https://api-blogsaas.figoodle.com/embed.js"></script>
<script>
  BlogSaas.init({
    apiKey: 'YOUR_API_KEY',
    container: '#blog',
    theme: 'auto',       // 'light' | 'dark' | 'auto'
    layout: 'grid',      // 'grid' | 'list'
    postsPerPage: 9,
  });
</script>

Error Responses

StatusMeaning
401Missing or invalid API key
404Resource not found
409Conflict (e.g. editing a sent newsletter)
500Server error
{ "error": "API key required" }