Refine AIRefine Docs
HTTP API

Public content API

Two read-only HTTPS endpoints return the articles a brand has published to its site with Refine. They are what @getrefine/next calls; use them directly to render articles on any stack (Astro, Nuxt, Rails, a static build).

Not the MCP server

This API only reads published articles and takes a public site key. Analytics, prompts and content generation are reached through the MCP tools with a secret rfn_live_ key.

The site key#

Find it in Settings → Site publishing. There is one per brand; it looks like pk_ followed by 32 hexadecimal characters and is passed as the key query parameter.

It is public by design: it can only read what the brand already published on its own site, so it is safe in server code, build scripts and environment files. An article becomes readable here once publish_to_site (or the Publish button in Refine) has put it on the site; drafts and articles sent only to WordPress or Webflow are not listed.

List articles#

GET /api/public/articles
curl "https://app.refine-app.com/api/public/articles?key=pk_your_site_key"
200
{
  "articles": [
    {
      "slug": "best-crm-for-small-agencies",
      "title": "Best CRM for small agencies in 2026",
      "description": "How to pick a CRM when you run fewer than 20 people…",
      "publishedAt": "2026-10-02T09:14:00.000Z",
      "updatedAt": null
    }
  ]
}

Newest first, up to 500 articles, without bodies. An unknown or malformed key returns { "articles": [] } with a 200 rather than an error, so the endpoint never confirms which keys exist. An empty list from a key you expect to work means the key is wrong or nothing is published yet.

Get one article#

GET /api/public/articles/:slug
curl "https://app.refine-app.com/api/public/articles/best-crm-for-small-agencies?key=pk_your_site_key"
200
{
  "article": {
    "slug": "best-crm-for-small-agencies",
    "title": "Best CRM for small agencies in 2026",
    "description": "How to pick a CRM when you run fewer than 20 people…",
    "html": "<p>…</p><h2>…</h2>…",
    "publishedAt": "2026-10-02T09:14:00.000Z",
    "updatedAt": null
  }
}

Returns 404 with { "error": "Not found" } for an unknown slug or an unknown key.

Article fields

FieldTypeNotes
slugstringURL segment, stable once published.
titlestring"Untitled" when the article has none.
descriptionstring | nullThe meta description.
htmlstringSingle-article endpoint only. Sanitized HTML body; a leading H1 that repeats the title is removed, so render title yourself.
publishedAtISO 8601First publication on the site.
updatedAtISO 8601 | nullCurrently always null.

Caching and CORS#

Every response carries:

headers
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, OPTIONS
Cache-Control: public, s-maxage=60, stale-while-revalidate=300

A newly published article can take up to a minute to appear at the edge, plus your own cache (5 minutes by default with @getrefine/next). Browser calls work, but fetching from your server keeps the article in the HTML that AI crawlers read, which is the point of publishing it.