SayWall
Documentation

REST API Reference

Use the SayWall REST API to fetch your approved testimonials and display them in custom UIs, mobile apps, or any platform.

Updated March 27, 2026

Overview

The SayWall REST API lets you fetch your approved testimonials programmatically. Build custom testimonial displays, integrate with mobile apps, or pipe data into your own systems.

Requirements:

  • Pro plan (API access is a Pro feature)
  • API key (create one in Dashboard > Developer > API)

Base URL:

https://saywall.io/api/v1

Authentication

All API requests require a Bearer token in the Authorization header.

Creating an API Key

  1. Go to Dashboard > Developer > API
  2. Click Create and give your key a name
  3. Copy the key immediately — it won't be shown again

Making Authenticated Requests

curl -H "Authorization: Bearer sk_live_your_key_here" \
  https://saywall.io/api/v1/testimonials

Security best practices:

  • Never expose your API key in client-side JavaScript
  • Store keys in environment variables (SAYWALL_API_KEY)
  • Use a server-side proxy for browser-based apps
  • Rotate keys periodically

Endpoints

List Testimonials

GET /api/v1/testimonials

Returns a paginated list of approved testimonials for your project.

Query Parameters

ParameterTypeDefaultDescription
pageinteger1Page number
limitinteger20Items per page (1-100)
sortstringnewestSort order: newest, oldest, rating
ratingintegerExact rating filter (1-5)
rating_minintegerMinimum rating (1-5)
rating_maxintegerMaximum rating (1-5)
sourcestringComma-separated sources: manual, twitter, google, trustpilot, public_form, g2
media_typestringComma-separated types: text, video, image
tagstringComma-separated tag names
tag_modestringanyTag matching: any (has any tag), all (has all tags), exclude (excludes tags)
searchstringText search in testimonial content (max 200 chars)

Example Request

curl -H "Authorization: Bearer sk_live_..." \
  "https://saywall.io/api/v1/testimonials?tag=saas,b2b&sort=rating&limit=10"

Example Response

{
  "data": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "content": "SayWall made it incredibly easy to collect and display our customer testimonials.",
      "author": {
        "name": "Jane Smith",
        "role": "Head of Marketing",
        "handle": null,
        "avatar_url": "https://example.com/avatar.jpg"
      },
      "rating": 5,
      "source": "manual",
      "source_url": null,
      "media_type": "text",
      "media_url": null,
      "tags": ["saas", "b2b"],
      "posted_at": "2026-03-15T10:30:00Z",
      "created_at": "2026-03-15T10:30:00Z",
      "video": null
    }
  ],
  "pagination": {
    "page": 1,
    "limit": 10,
    "total": 42,
    "total_pages": 5,
    "has_next": true,
    "has_prev": false
  }
}

Get Single Testimonial

GET /api/v1/testimonials/:id

Returns a single testimonial by ID.

Example Request

curl -H "Authorization: Bearer sk_live_..." \
  https://saywall.io/api/v1/testimonials/550e8400-e29b-41d4-a716-446655440000

Example Response

{
  "data": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "content": "SayWall made it incredibly easy to collect and display our customer testimonials.",
    "author": {
      "name": "Jane Smith",
      "role": "Head of Marketing",
      "handle": null,
      "avatar_url": "https://example.com/avatar.jpg"
    },
    "rating": 5,
    "source": "manual",
    "source_url": null,
    "media_type": "text",
    "media_url": null,
    "tags": ["saas", "b2b"],
    "posted_at": "2026-03-15T10:30:00Z",
    "created_at": "2026-03-15T10:30:00Z",
    "video": null
  }
}

Filtering Examples

By rating

# Testimonials with exactly 5 stars
?rating=5
 
# Testimonials with 4 or 5 stars
?rating_min=4
 
# Testimonials with 1-3 stars
?rating_max=3

By tag

# Has any of these tags
?tag=saas,enterprise
 
# Has ALL of these tags
?tag=saas,enterprise&tag_mode=all
 
# Exclude testimonials with these tags
?tag=internal&tag_mode=exclude

By source

# Only Google and Trustpilot reviews
?source=google,trustpilot
 
# Only manually added testimonials
?source=manual

By media type

# Only video testimonials
?media_type=video
 
# Text and image testimonials
?media_type=text,image
# Search testimonial content
?search=amazing product

Combined filters

# 5-star Google reviews tagged "enterprise", newest first
?rating=5&source=google&tag=enterprise&sort=newest&limit=5

Pagination

All list responses include a pagination object:

{
  "pagination": {
    "page": 2,
    "limit": 20,
    "total": 85,
    "total_pages": 5,
    "has_next": true,
    "has_prev": true
  }
}

Iterate through pages:

let page = 1;
let hasNext = true;
 
while (hasNext) {
  const res = await fetch(
    `https://saywall.io/api/v1/testimonials?page=${page}&limit=100`,
    { headers: { Authorization: `Bearer ${API_KEY}` } }
  );
  const { data, pagination } = await res.json();
 
  // Process data...
 
  hasNext = pagination.has_next;
  page++;
}

Rate Limiting

API requests are limited to 60 requests per minute per API key.

Every response includes rate limit headers:

HeaderDescription
X-RateLimit-LimitMaximum requests per window
X-RateLimit-RemainingRequests remaining in current window
X-RateLimit-ResetUnix timestamp when the window resets

When the limit is exceeded, the API returns 429 Too Many Requests with a Retry-After header:

{
  "error": {
    "code": "rate_limited",
    "message": "Rate limit exceeded. Try again in 45 seconds."
  }
}

Error Codes

StatusCodeDescription
400bad_requestInvalid query parameters
401unauthorizedMissing or invalid API key
403forbiddenAPI access requires Pro plan
404not_foundTestimonial not found
429rate_limitedToo many requests
500internal_errorServer error

All error responses follow this format:

{
  "error": {
    "code": "error_code",
    "message": "Human-readable description"
  }
}

Code Examples

curl

# List all testimonials
curl -H "Authorization: Bearer $SAYWALL_API_KEY" \
  https://saywall.io/api/v1/testimonials
 
# Get 5-star testimonials tagged "featured"
curl -H "Authorization: Bearer $SAYWALL_API_KEY" \
  "https://saywall.io/api/v1/testimonials?rating=5&tag=featured&limit=5"

JavaScript / TypeScript

const API_KEY = process.env.SAYWALL_API_KEY;
 
async function getTestimonials(params?: Record<string, string>) {
  const url = new URL("https://saywall.io/api/v1/testimonials");
  if (params) {
    Object.entries(params).forEach(([key, value]) =>
      url.searchParams.set(key, value)
    );
  }
 
  const res = await fetch(url.toString(), {
    headers: { Authorization: `Bearer ${API_KEY}` },
  });
 
  if (!res.ok) {
    const { error } = await res.json();
    throw new Error(error.message);
  }
 
  return res.json();
}
 
// Usage
const { data, pagination } = await getTestimonials({
  tag: "featured",
  sort: "rating",
  limit: "10",
});

Python

import requests
import os
 
API_KEY = os.environ["SAYWALL_API_KEY"]
BASE_URL = "https://saywall.io/api/v1"
 
def get_testimonials(**params):
    response = requests.get(
        f"{BASE_URL}/testimonials",
        headers={"Authorization": f"Bearer {API_KEY}"},
        params=params,
    )
    response.raise_for_status()
    return response.json()
 
# Usage
result = get_testimonials(tag="featured", sort="rating", limit=10)
for testimonial in result["data"]:
    print(f"{testimonial['author']['name']}: {testimonial['content'][:100]}")

React (Server Component)

// app/testimonials/page.tsx
async function getTestimonials() {
  const res = await fetch(
    "https://saywall.io/api/v1/testimonials?tag=featured&sort=newest",
    {
      headers: {
        Authorization: `Bearer ${process.env.SAYWALL_API_KEY}`,
      },
      next: { revalidate: 3600 }, // Cache for 1 hour
    }
  );
  return res.json();
}
 
export default async function TestimonialsPage() {
  const { data: testimonials } = await getTestimonials();
 
  return (
    <section>
      <h2>What our customers say</h2>
      <div className="grid grid-cols-3 gap-4">
        {testimonials.map((t) => (
          <div key={t.id} className="p-4 border rounded-lg">
            <p>{t.content}</p>
            <footer>
{t.author.name}
              {t.author.role && `, ${t.author.role}`}
            </footer>
          </div>
        ))}
      </div>
    </section>
  );
}

Video Testimonials

Video testimonials include additional fields in the video object:

{
  "media_type": "video",
  "media_url": "https://...",
  "video": {
    "playback_id": "abc123",
    "thumbnail_url": "https://image.mux.com/abc123/thumbnail.jpg",
    "duration": 45.2,
    "width": 1920,
    "height": 1080
  }
}

Use the playback_id with Mux Player for HLS streaming, or use media_url for direct access.


Response Object Reference

Testimonial

FieldTypeDescription
idstringUnique identifier (UUID)
contentstringTestimonial text
author.namestring | nullAuthor display name
author.rolestring | nullAuthor job title
author.handlestring | nullSocial media handle
author.avatar_urlstring | nullProfile picture URL
ratingnumber | nullStar rating (1-5)
sourcestringSource type
source_urlstring | nullLink to original source
media_typestringtext, video, or image
media_urlstring | nullMedia file URL
tagsstring[]Tag names
posted_atstringISO 8601 date when originally posted
created_atstringISO 8601 date when added to SayWall
videoobject | nullVideo metadata (only for video testimonials)

Start collecting testimonials for free

Set up in minutes. No credit card required.

Get started free

Need help?

Have a question or need assistance? Reach us at hello@saywall.io