API

Two things people genuinely want an API for: reading a wall as JSON so you can render it in your own framework, and submitting testimonials from your own form while keeping our moderation, storage and embeds.

Access

The API is included with Pro, Team and Agency. It is gated on the space owner's plan, not on a key — endpoints are addressed by the wall's uuid or the space/form slug, exactly like the embeds, so there is no token to rotate or leak into client-side code. On a Free plan the endpoints answer 402 and point you at the free HTML and script embeds, which are unlimited and never count-capped.

See plans

Read a wall as JSON

The wall uuid is in your dashboard next to the embed snippet. The response is CORS-open, so you can call it straight from a browser app.

GET https://testimonials.free/api/testimonials/wall/<uuid>/
{
  "wall": {"id": "3f9a…", "name": "Homepage wall", "layout": "grid"},
  "space": {"name": "Acme", "slug": "acme"},
  "count": 12,
  "testimonials": [
    {
      "id": "8c21…",
      "kind": "text",
      "body": "Cut our onboarding time in half.",
      "rating": 5,
      "author": {"name": "Dana Reyes", "title": "Head of Ops",
                 "company": "Northwind", "url": "https://…",
                 "avatar": "https://…"},
      "video": null,
      "featured": true,
      "tags": ["onboarding"],
      "published_at": "2026-07-14T09:22:41+00:00"
    }
  ]
}

React

const WALL = "https://testimonials.free/api/testimonials/wall/3f9a…/";

export function Wall() {
  const [items, setItems] = useState([]);
  useEffect(() => {
    fetch(WALL).then(r => r.json()).then(d => setItems(d.testimonials));
  }, []);
  return items.map(t => (
    <figure key={t.id}>
      <blockquote>{t.body}</blockquote>
      <figcaption>{t.author.name} — {t.author.company}</figcaption>
    </figure>
  ));
}

Submit from your own form

Keep your own design; we do the moderation, storage, snapshots and embeds. The submission lands exactly where the hosted collection page's would — pending, or approved if the form auto-approves.

POST https://testimonials.free/api/testimonials/submit/
Content-Type: application/json

{
  "form": "acme/website",        // "<space-slug>/<form-slug>"
  "author_name": "Dana Reyes",
  "body": "Cut our onboarding time in half.",
  "rating": 5,
  "author_title": "Head of Ops",
  "author_company": "Northwind",
  "author_email": "dana@example.com",
  "consent": true                // required when the form requires consent
}
curl -s https://testimonials.free/api/testimonials/submit/ \
  -H "Content-Type: application/json" \
  -d '{"form":"acme/website","author_name":"Dana Reyes",
       "body":"Cut our onboarding time in half.","rating":5,"consent":true}'

# 201 {"id": "8c21…", "status": "pending"}

Python

import requests

requests.post("https://testimonials.free/api/testimonials/submit/", json={
    "form": "acme/website",
    "author_name": "Dana Reyes",
    "body": "Cut our onboarding time in half.",
    "rating": 5,
    "consent": True,
})

wall = requests.get(
    "https://testimonials.free/api/testimonials/wall/3f9a…/").json()
print(wall["count"], "testimonials")

Errors

402 — the space owner is on Free; the response carries the free embed or collection URL to use instead. 400 — missing author_name, a body under 10 characters, a malformed form reference, or missing consent when the form requires it. 404 — unknown wall uuid, space, or inactive form.

You may not need it

The HTML and script embeds are free on every plan, unlimited, and render from a pre-built snapshot — no fetch happens when a visitor loads your page, so they are faster than anything you could build on this API. Reach for the API when you need the data inside your own components or your own submission form, not to render a standard wall.