Frase CMS API

Frase CMS post management and bulk import

GitHub OpenAPI

Operations 41

GET /cms/demand Content demand and gaps for a site #
GET /cms/demand/industry Industry content demand for a site's vertical #
POST /cms/media Upload or rehost an image #
POST /cms/posts/validate Dry-run a CMS post create #
POST /cms/posts/{id}/autonomous-publish Request a governed autonomous publish #
GET /cms/posts/{id}/performance Performance for one CMS post #
GET /cms/authors List CMS authors #
POST /cms/authors Create a CMS author #
PATCH /cms/authors/{id} Update a CMS author #
DELETE /cms/authors/{id} Delete a CMS author #
POST /cms/authors/{id}/merge Merge two CMS authors #
GET /cms/categories List a site's CMS categories #
POST /cms/categories Create a CMS category #
PATCH /cms/categories/{id} Update a CMS category #
DELETE /cms/categories/{id} Delete a CMS category #
POST /cms/categories/{id}/merge Merge two CMS categories #
GET /cms/tags List a site's CMS tags #
POST /cms/tags Create a CMS tag #
PATCH /cms/tags/{id} Update a CMS tag #
DELETE /cms/tags/{id} Delete a CMS tag #
POST /cms/tags/{id}/merge Merge two CMS tags #
GET /cms/redirects List a site's redirects #
POST /cms/redirects Create a redirect #
PATCH /cms/redirects/{id} Update a redirect #
DELETE /cms/redirects/{id} Delete a redirect #
GET /cms/redirects/chains Diagnose redirect chains and loops #
POST /cms/redirects/import Bulk-import redirects #
GET /cms/posts/{id}/versions List a post's version history #
GET /cms/posts/{id}/versions/{versionId} Get one post version #
POST /cms/posts/{id}/versions/{versionId}/restore Restore a post to an earlier version #
GET /cms/posts/by-slug Get a CMS post by slug #
GET /cms/posts List CMS posts #
POST /cms/posts Create a CMS post #
GET /cms/posts/{id} Get a CMS post #
PUT /cms/posts/{id} Update a CMS post #
DELETE /cms/posts/{id} Delete a CMS post #
POST /cms/posts/{id}/publish Publish a CMS post #
POST /cms/posts/{id}/unpublish Unpublish a CMS post #
POST /cms/posts/bulk Publish, unpublish, or delete many CMS posts at once #
POST /cms/posts/import Bulk import CMS posts #
GET /cms/posts/import/{jobId} Get import job status #

Work with this as data

Every API here is available over the APIs.io API and to AI agents over MCP.

MCP server

One button, every client — Claude, Cursor, VS Code and the rest.

https://apis.io/mcp

Tools for apis

7 MCP tools reach this
  • find_apisBrowse and filter every API in the catalog.
  • get_api_artifactsOne API's artifacts, grouped by type.
  • get_openapiThe primary OpenAPI for this API.
  • find_similar_apisAPIs that look like this one.
  • apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
  • resolveTurn a domain, URL or GitHub org into the provider it belongs to.
  • find_cohortsEvery scored population of providers in the catalog.
All 92 tools →

Call it yourself

curl for this page
This API
curl "https://apis.io/api/v1/apis/frase-io:frase-io-cms-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"

Calls without X-API-Key need no key. Calls with it need a paid plan — get a key.

Get an API key

Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.

A second provider on the same verified email joins the account you already have.

OpenAPI Specification

frase-io-cms-api-openapi.yml Raw ↑
openapi: 3.2.0
info:
  title: Frase CMS API
  version: 1.0.0
  description: 'The Frase API provides programmatic access to your SEO content platform. Use it to manage content, research, audits, AI visibility tracking, and more. ## Authentication


    All API requests require authentication using an API key. Include your key in the `X-API-KEY` header:


    ```

    X-API-KEY: sk_live_your_api_key_here

    ```


    For legacy compatibility, the `token` header is also supported. ## Rate Limits


    Three independent limits apply, each with its own headers and error code. Headers are returned on `429` responses, not on successful ones. **Per-minute burst** - 60/min on trial and Starter, 120 Professional, 300 Scale, 1,000 Enterprise. Error code `rate_limit_exceeded`. - `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` (epoch seconds)


    **Sustained hourly ceiling** - 2,000/hour trial, 3,000 Starter, 5,000 Professional, 15,000 Scale, 50,000 Enterprise. Error code `hourly_limit_exceeded`. - `X-RateLimit-Hourly-Limit`, `X-RateLimit-Hourly-Remaining`, `X-RateLimit-Hourly-Reset` (epoch seconds)


    **Monthly request budget** - error code `monthly_budget_exceeded`, headers `X-Monthly-Limit`, `X-Monthly-Used`, `X-Monthly-Remaining`. Every `429` carries `Retry-After` in seconds. On the hourly ceiling this can be up to 3,600, so clients should honour it rather than retrying on a short fixed backoff - a retry sooner than `Retry-After` cannot succeed. ## Errors


    The API uses standard HTTP status codes and returns errors in a consistent format:


    ```json

    {

    "error": {

    "type": "authentication_error",

    "code": "invalid_api_key",

    "message": "Invalid API key provided.",

    "doc_url": "https://docs.frase.io/api/errors#invalid_api_key"

    }

    }

    ```


    ## Changelog


    Breaking changes are listed here, newest first. "Breaking" means a field that was always present with a value can now be `null`, or an existing value now means something different — not that a field was removed or renamed. Additive fields are not listed. ### 2026-08-31 — Fact `sources[]` may omit `url`, and minted facts default to `approved`


    - `GET /briefs/{id}/facts`, `PATCH /briefs/{id}/facts/{factId}` → a `sources[]` entry may now carry only `title`, with no `url` or `domain`. Research names a publisher far more often than it captures a link (793 of 801 plan-evidence items in production), and those names were previously discarded rather than returned. Read `url` defensively. - `GET /briefs/{id}/facts` → `approval` on a newly minted fact is now `approved` rather than `pending`. A client polling for `pending` facts to review will see far fewer; rejection is unchanged and remains permanent. Facts the writer asserted in its own output (`provenance: generated`) are still minted `pending`. ### 2026-08-24 — Content Guard''s hasPublishedUrl is now recoverability, not the stored URL


    - `GET /content/{id}/monitoring` → `hasPublishedUrl`: was a judgment on the STORED URL (false for a relative path even when the page was live). It now answers "could Guard watch this article right now" — the stored URL when absolute, or an address the server recovers (CMS inventory; FraseCMS re-derivation from current settings when a live published post exists). `true` no longer implies the stored URL itself is absolute, and a FraseCMS article published before domain setup flips to `true` when setup finishes, with no re-publish. It remains exactly the condition enabling enforces. - `publishedUrlIssue` is now the RESIDUAL after recovery failed, not a raw-column classification — "not_absolute" can also mean the FraseCMS post is not live or FraseCMS is disabled for the site. - `POST /content/{id}/publish` → `url` (frase_cms): now derived from the site''s current CMS settings rather than the adapter''s host-of-the-moment value; the same URL is stored on the content row and sent in the content.published webhook.'
  contact:
    name: Frase Support
    url: https://frase.io
    email: support@frase.io
  license:
    name: Proprietary
    url: https://www.frase.io/company/terms
servers:
- url: https://next.frase.io/api/v1
  description: Production
- url: /api/v1
  description: This host
security:
- ApiKeyAuth: []
- BearerAuth: []
tags:


# --- truncated at 32 KB (154 KB total) ---
# Full source: https://artifacts.apis.io/frase-io/openapi/frase-io-cms-api-openapi.yml