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 plansRead 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.