ULTRVA

API

Manage your blogs and posts programmatically.

Overview

The ULTRV API is a REST API that lets you create and manage blogs, publish posts, and update settings — all with a simple API key. Built for developers, scripts, and mobile apps.

Base URL: https://app.ultrv.com/api/v1

Interactive API docs (Swagger UI) →

Authentication

Every request requires an API key sent as a Bearer token. You can find your API key in your account settings.

Authorization: Bearer ultrv_your_api_key_here

Keep your API key secret. If compromised, regenerate it from your settings page.

Quick Start

List your blogs

curl -H "Authorization: Bearer $API_KEY" \
  https://app.ultrv.com/api/v1/blogs

Create a post

curl -X POST \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"title":"My Post","slug":"my-post","content":"<p>Hello world</p>","status":"draft"}' \
  https://app.ultrv.com/api/v1/blogs/my-blog/posts

Create a post with image upload

curl -X POST \
  -H "Authorization: Bearer $API_KEY" \
  -F "title=My Post" \
  -F "slug=my-post" \
  -F "content=<p>Hello world</p>" \
  -F "status=draft" \
  -F "featured_image_file=@/path/to/image.jpg" \
  https://app.ultrv.com/api/v1/blogs/my-blog/posts

Publish a post

curl -X POST \
  -H "Authorization: Bearer $API_KEY" \
  https://app.ultrv.com/api/v1/blogs/my-blog/posts/42/publish

Endpoints

Blogs

Method Path Description
GET /blogs List all your blogs
POST /blogs Create a new blog
GET /blogs/{slug} Get a blog
PUT /blogs/{slug} Update a blog's settings
DELETE /blogs/{slug} Delete a blog

Posts

Method Path Description
GET /blogs/{slug}/posts List posts (filter with ?status=published)
POST /blogs/{slug}/posts Create a post
GET /blogs/{slug}/posts/{id} Get a post
PUT /blogs/{slug}/posts/{id} Update a post
DELETE /blogs/{slug}/posts/{id} Delete a post
POST /blogs/{slug}/posts/{id}/publish Publish a post
POST /blogs/{slug}/posts/{id}/unpublish Unpublish a post (back to draft)

All paths are relative to /api/v1. Blogs are identified by slug, posts by numeric ID.

Post Fields

Fields accepted when creating or updating a post.

Field Type Required Description
title string Yes Post title (max 255 characters)
slug string Yes URL slug. Lowercase alphanumeric with hyphens (e.g. my-first-post). Must be unique within the blog.
content string No Post body as HTML. Sanitized on the server.
excerpt string No Short summary shown in post listings (max 1000 characters)
status string No draft or published. You can also use the /publish and /unpublish endpoints.
tags string[] No Array of tag names. Tags are created automatically if they don't exist. Replaces all existing tags on update.
featured_image string No URL for the featured image (max 255 characters). Cannot be used with featured_image_file.
featured_image_file file No Image upload via multipart form data. JPEG, PNG, GIF, or WebP, max 10 MB. Cannot be used with featured_image.

Example: create a post with tags and excerpt

curl -X POST \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Getting Started with ULTRV",
    "slug": "getting-started",
    "excerpt": "A quick guide to setting up your blog.",
    "content": "<p>Welcome to ULTRV...</p>",
    "tags": ["tutorial", "getting-started"],
    "status": "draft"
  }' \
  https://app.ultrv.com/api/v1/blogs/my-blog/posts

Example response

{
  "data": {
    "id": 1,
    "title": "Getting Started with ULTRV",
    "slug": "getting-started",
    "excerpt": "A quick guide to setting up your blog.",
    "content": "<p>Welcome to ULTRV...</p>",
    "featured_image": null,
    "status": "draft",
    "published_at": null,
    "tags": [
      { "id": 1, "name": "tutorial", "slug": "tutorial" },
      { "id": 2, "name": "getting-started", "slug": "getting-started" }
    ],
    "created_at": "2026-02-26T12:00:00+00:00",
    "updated_at": "2026-02-26T12:00:00+00:00"
  }
}

Blog Fields

Fields accepted when creating or updating a blog.

Field Type Required Description
name string Yes Blog display name (max 255 characters)
slug string Yes URL slug. Lowercase alphanumeric with hyphens. Must be unique.
author_name string Yes Author name displayed on the blog (max 255 characters)
description string No Blog description (max 1000 characters)
discoverable boolean No Whether the blog appears in the explore directory
custom_domain string No Custom domain for the blog (update only)
theme string No Blog theme identifier (update only)
meta_title string No Custom SEO title (max 255 characters, update only)
meta_description string No Custom SEO description (max 1000 characters, update only)
og_image string No Social sharing image URL (update only)
language string No Language code, e.g. en or en-US (update only)

Example response

{
  "data": {
    "id": 1,
    "name": "My Blog",
    "slug": "my-blog",
    "description": "A blog about things.",
    "author_name": "Jane Doe",
    "custom_domain": null,
    "discoverable": true,
    "theme": "default",
    "meta_title": null,
    "meta_description": null,
    "og_image": null,
    "language": "en",
    "canonical_url": "https://my-blog.ultrv.com",
    "created_at": "2026-01-15T08:00:00+00:00",
    "updated_at": "2026-02-20T14:30:00+00:00"
  }
}

Image Uploads

You can upload featured images when creating or updating posts using the featured_image_file field with multipart form data.

Supported formats

  • JPEG, PNG, GIF, WebP
  • Maximum file size: 10 MB

Two ways to set featured images

1. Upload a file — Use featured_image_file with multipart/form-data:

curl -X POST \
  -H "Authorization: Bearer $API_KEY" \
  -F "title=My Post" \
  -F "featured_image_file=@/path/to/image.jpg" \
  https://app.ultrv.com/api/v1/blogs/my-blog/posts

2. Reference an existing URL — Use featured_image with JSON:

curl -X POST \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"title":"My Post","featured_image":"https://example.com/image.jpg"}' \
  https://app.ultrv.com/api/v1/blogs/my-blog/posts

Note: You cannot use both featured_image and featured_image_file in the same request.

Rate Limits

The API is rate-limited to 60 requests per minute per API key. Rate limit headers are included in every response:

X-RateLimit-Limit: 60
X-RateLimit-Remaining: 59
Retry-After: 30  # (only when rate limited)

Errors

The API uses standard HTTP status codes. Error responses include a message field and optionally an errors object for validation failures:

{
  "message": "The slug has already been taken.",
  "errors": {
    "slug": ["The slug has already been taken."]
  }
}
Status Meaning
401Missing or invalid API key
403Not authorized to access this resource
404Resource not found
422Validation error
429Rate limit exceeded

Pagination

List endpoints return paginated results (20 items per page). Use ?page=2 to navigate. Response metadata includes:

{
  "data": [...],
  "links": {
    "first": "...?page=1",
    "last": "...?page=5",
    "prev": null,
    "next": "...?page=2"
  },
  "meta": {
    "current_page": 1,
    "last_page": 5,
    "per_page": 20,
    "total": 100
  }
}