Home/API Referencev1

API Reference

Programmatic access to PraisedBy. Manage spaces, testimonials, analytics, widgets, teams, and webhooks through a simple REST API.

Quick Start

Get up and running in under a minute:

  1. Create an API key — Go to Settings → API Keys and generate a new key. Copy it immediately — it's shown only once.
  2. Make your first request
  3. Explore — Browse the endpoint reference below.
curl -H "Authorization: Bearer pb_live_YOUR_KEY" \
  https://praisedby.com/api/v1/spaces

Authentication

Every authenticated request must include a Bearer token in the Authorization header. API keys are prefixed with pb_live_ and are available on the Pro plan.

Authorization: Bearer pb_live_YOUR_API_KEY
Important: Keys are shown only once at creation time. If you lose a key, revoke it and create a new one. API access requires the Pro plan ($49/month).

Base URL

All versioned API endpoints are relative to:

https://praisedby.com/api/v1

Public endpoints (widget, submit) use https://praisedby.com/api without the version prefix.

Rate Limiting

Rate limits are applied per API key using a sliding window. Every response includes X-RateLimit-Remaining and X-RateLimit-Limit headers.

EndpointLimitWindow
GET /spaces10060s
GET /spaces/:id/testimonials10060s
POST /spaces/:id/testimonials3060s
GET /testimonials/:id20060s
PATCH /testimonials/:id6060s
DELETE /testimonials/:id3060s
GET /spaces/:id/analytics6060s
POST /api/testimonials (public)560s per IP
GET /widget/:spaceId20060s

When rate-limited, you'll receive a 429 Too Many Requests response with a Retry-After header indicating how many seconds to wait.

Error Handling

All errors return a JSON body with an error field. Validation errors (400) also include an issues array with per-field details.

Response

400 Bad Request
{
  "error": "Validation failed",
  "issues": [
    { "path": ["rating"], "message": "Required" },
    { "path": ["content"], "message": "String must contain at least 10 character(s)" }
  ]
}
CodeMeaning
400Invalid request body or query parameters
401Missing or invalid API key
403Feature not available on your plan
404Resource not found or not owned by you
429Rate limit exceeded — wait and retry
500Internal server error — contact support

Pagination

List endpoints support cursor-based pagination via page and per_page query parameters. Default page size is 50, maximum is 100.

Response

200 OK
{
  "data": [ ... ],
  "pagination": {
    "page": 1,
    "per_page": 50,
    "total": 142,
    "total_pages": 3
  }
}
Tip: Use ?status=approved to filter testimonials before pagination to reduce payload size.

Spaces

GET/api/v1/spaces

Retrieve all spaces belonging to your account.

API Key100 req/minPro

Response

200 OK
{
  "data": [
    {
      "id": "spc_a1b2c3d4e5f6",
      "name": "My Product",
      "slug": "my-product",
      "testimonial_count": 42,
      "is_active": true,
      "created_at": "2025-01-15T10:30:00Z"
    }
  ]
}
curl -H "Authorization: Bearer pb_live_YOUR_KEY" \
  https://praisedby.com/api/v1/spaces

Testimonials

GET/api/v1/spaces/:spaceId/testimonials

List testimonials for a space. Supports filtering by status, tag, and pagination.

API Key100 req/minPro

Query Parameters

ParameterTypeRequiredDescription
statusstringNo"pending", "approved", or "rejected"
tagstringNoTag ID to filter by
pageintegerNoPage number (default: 1)
per_pageintegerNoResults per page (default: 50, max: 100)

Response

200 OK
{
  "data": [
    {
      "id": "tst_x9y8z7w6v5u4",
      "customer_name": "Jane Smith",
      "customer_email": "jane@company.com",
      "customer_company": "Acme Inc.",
      "customer_role": "CEO",
      "rating": 5,
      "content": "Absolutely love this product...",
      "status": "approved",
      "is_starred": true,
      "source": "form",
      "submitted_at": "2025-02-01T12:00:00Z",
      "created_at": "2025-02-01T12:00:00Z"
    }
  ],
  "pagination": {
    "page": 1,
    "per_page": 50,
    "total": 142,
    "total_pages": 3
  }
}
curl -H "Authorization: Bearer pb_live_YOUR_KEY" \
  "https://praisedby.com/api/v1/spaces/SPACE_ID/testimonials?status=approved&per_page=10"
POST/api/v1/spaces/:spaceId/testimonials

Create a new testimonial. The source is automatically set to "api".

API Key30 req/minPro

Request Body (JSON)

ParameterTypeRequiredDescription
customer_namestringYes1-100 characters
customer_emailstringYesValid email address
customer_companystringNoMax 100 characters
customer_rolestringNoMax 100 characters
ratingintegerYes1 to 5
contentstringYes10-2000 characters
statusstringNo"pending" (default), "approved", or "rejected"

Response

201 Created
{
  "data": {
    "id": "tst_n3m4l5k6j7h8",
    "customer_name": "Jane Smith",
    "customer_email": "jane@company.com",
    "rating": 5,
    "content": "This product transformed our workflow. Highly recommended!",
    "status": "approved",
    "source": "api",
    "created_at": "2025-02-01T12:00:00Z"
  }
}
Note: Returns 403 if you've hit your plan's testimonial limit (Free: 10, Starter: 100, Pro: unlimited).
curl -X POST \
  -H "Authorization: Bearer pb_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "customer_name": "Jane Smith",
    "customer_email": "jane@company.com",
    "rating": 5,
    "content": "This product transformed our workflow. Highly recommended!",
    "status": "approved"
  }' \
  https://praisedby.com/api/v1/spaces/SPACE_ID/testimonials
GET/api/v1/testimonials/:id

Retrieve full details for a single testimonial, including enhanced content and video fields.

API Key200 req/minPro

Path Parameters

ParameterTypeRequiredDescription
idstringYesTestimonial ID

Response

200 OK
{
  "data": {
    "id": "tst_x9y8z7w6v5u4",
    "space_id": "spc_a1b2c3d4e5f6",
    "customer_name": "Jane Smith",
    "customer_email": "jane@company.com",
    "customer_company": "Acme Inc.",
    "customer_role": "CEO",
    "rating": 5,
    "content": "Original testimonial content...",
    "enhanced_content": "AI-enhanced version of the testimonial...",
    "testimonial_type": "text",
    "video_url": null,
    "status": "approved",
    "is_starred": true,
    "source": "form",
    "submitted_at": "2025-02-01T12:00:00Z",
    "reviewed_at": "2025-02-02T09:00:00Z",
    "created_at": "2025-02-01T12:00:00Z",
    "updated_at": "2025-02-02T09:00:00Z"
  }
}
curl -H "Authorization: Bearer pb_live_YOUR_KEY" \
  https://praisedby.com/api/v1/testimonials/TESTIMONIAL_ID
PATCH/api/v1/testimonials/:id

Update a testimonial's status or star flag. The reviewed_at timestamp is set automatically on status changes.

API Key60 req/minPro

Request Body (JSON)

ParameterTypeRequiredDescription
statusstringNo"pending", "approved", or "rejected"
is_starredbooleanNoStar or unstar the testimonial

Response

200 OK
{
  "data": {
    "id": "tst_x9y8z7w6v5u4",
    "status": "approved",
    "is_starred": true,
    "reviewed_at": "2025-02-02T09:00:00Z",
    "updated_at": "2025-02-02T09:00:00Z"
  }
}
curl -X PATCH \
  -H "Authorization: Bearer pb_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "status": "approved", "is_starred": true }' \
  https://praisedby.com/api/v1/testimonials/TESTIMONIAL_ID
DELETE/api/v1/testimonials/:id

Permanently delete a testimonial. The space's testimonial_count is automatically decremented.

API Key30 req/minPro

Response

200 OK
{ "success": true }
curl -X DELETE \
  -H "Authorization: Bearer pb_live_YOUR_KEY" \
  https://praisedby.com/api/v1/testimonials/TESTIMONIAL_ID
POST/api/testimonials

Public endpoint for end-users to submit testimonials via your collection form. No authentication required. Rate limited by IP address.

Public (no auth)5 req/min per IP

Request Body (JSON)

ParameterTypeRequiredDescription
spaceIdstringYesThe space to submit the testimonial to
customer_namestringYes1-100 characters
customer_emailstringYesValid email address
customer_companystringNoCompany name
customer_rolestringNoJob title / role
ratingintegerYes1 to 5
contentstringYes10-2000 characters

Response

201 Created
{
  "data": {
    "id": "tst_p1q2r3s4t5u6",
    "status": "pending",
    "source": "form",
    "created_at": "2025-03-15T14:30:00Z"
  }
}
curl -X POST \
  -H "Content-Type: application/json" \
  -d '{
    "spaceId": "SPACE_ID",
    "customer_name": "Alex Johnson",
    "customer_email": "alex@example.com",
    "rating": 5,
    "content": "This product has been a game-changer for our team!"
  }' \
  https://praisedby.com/api/testimonials
GET/api/spaces/:spaceId/export

Export all testimonials for a space as a CSV file. Useful for backups, reporting, or importing into other tools.

Session (logged in)10 req/minStarter+

Path Parameters

ParameterTypeRequiredDescription
spaceIdstringYesThe space to export from

Returns a CSV file with Content-Type: text/csv and a Content-Disposition header for download.

# Requires session cookie (use from browser or with cookie jar)
curl -b cookies.txt \
  -o testimonials.csv \
  https://praisedby.com/api/spaces/SPACE_ID/export

Analytics

GET/api/v1/spaces/:spaceId/analytics

Widget impression, click, and conversion analytics. Starter plan gets basic analytics; Pro plan gets full analytics with all time periods.

API Key60 req/minStarter+

Query Parameters

ParameterTypeRequiredDescription
periodstringNo"7d" (default), "30d", or "90d"

Response

200 OK
{
  "data": {
    "period": "30d",
    "start": "2025-01-01",
    "end": "2025-01-31",
    "summary": {
      "total_impressions": 12450,
      "total_clicks": 342,
      "total_conversions": 28,
      "total_unique_visitors": 8210
    },
    "daily": [
      {
        "date": "2025-01-01",
        "impressions": 410,
        "clicks": 12,
        "conversions": 1,
        "unique_visitors": 280
      }
    ]
  }
}
Note: Free plans receive 403. Starter plans get basic analytics. Pro plans get full analytics with all time periods.
curl -H "Authorization: Bearer pb_live_YOUR_KEY" \
  "https://praisedby.com/api/v1/spaces/SPACE_ID/analytics?period=30d"

Widget

Public endpoints used by the embeddable widget. No authentication required.

GET/api/widget/:spaceId

Fetch approved testimonials and widget configuration for rendering the embed widget. Returns testimonials, space settings, and theme configuration.

Public (no auth)200 req/min

Response

200 OK
{
  "space": {
    "id": "spc_a1b2c3d4e5f6",
    "name": "My Product",
    "settings": {
      "theme": "light",
      "layout": "carousel",
      "show_ratings": true,
      "show_date": true
    }
  },
  "testimonials": [
    {
      "id": "tst_x9y8z7w6v5u4",
      "customer_name": "Jane Smith",
      "customer_company": "Acme Inc.",
      "rating": 5,
      "content": "Absolutely love this product...",
      "customer_avatar": "https://..."
    }
  ]
}
curl https://praisedby.com/api/widget/SPACE_ID
GET/api/widget/:spaceId/schema

Returns JSON-LD structured data (Schema.org) for your testimonials. Embed this in your page's <head> to enable Google rich snippets with star ratings.

Public (no auth)200 req/min

Response

200 OK
{
  "@context": "https://schema.org",
  "@type": "Product",
  "name": "My Product",
  "aggregateRating": {
    "@type": "AggregateRating",
    "ratingValue": "4.8",
    "reviewCount": "42"
  },
  "review": [
    {
      "@type": "Review",
      "author": { "@type": "Person", "name": "Jane Smith" },
      "reviewRating": {
        "@type": "Rating",
        "ratingValue": "5"
      },
      "reviewBody": "Absolutely love this product..."
    }
  ]
}
curl https://praisedby.com/api/widget/SPACE_ID/schema
POST/api/widget/:spaceId/event

Track widget impressions, clicks, and conversion events. Used internally by the widget SDK to power analytics.

Public (no auth)200 req/min

Request Body (JSON)

ParameterTypeRequiredDescription
typestringYes"impression", "click", or "conversion"
testimonialIdstringNoID of the specific testimonial interacted with
metadataobjectNoAdditional event data (referrer, page URL, etc.)

Response

200 OK
{ "success": true }
curl -X POST \
  -H "Content-Type: application/json" \
  -d '{ "type": "impression" }' \
  https://praisedby.com/api/widget/SPACE_ID/event

Webhooks

Receive real-time notifications when events occur in your account. Configure webhooks in Settings → Webhooks.

Webhook Events

Webhooks deliver a POST request to your URL with an HMAC-SHA256 signature.

EventDescription
new_testimonialFired when a new testimonial is submitted (from form, API, or import)
status_changeFired when a testimonial's status changes (approved, rejected, pending)

Payload Format

{
  "event": "new_testimonial",
  "data": {
    "id": "tst_x9y8z7w6v5u4",
    "space_id": "spc_a1b2c3d4e5f6",
    "customer_name": "Jane Smith",
    "rating": 5,
    "content": "Great product!",
    "status": "pending",
    "created_at": "2025-02-01T12:00:00Z"
  },
  "timestamp": "2025-02-01T12:00:01Z"
}

Headers included with every webhook delivery:

Content-Type: application/json
X-PraisedBy-Signature: sha256=a1b2c3d4e5f6...
X-PraisedBy-Event: new_testimonial
X-PraisedBy-Delivery: dlv_unique_id

Signature Verification

Always verify the X-PraisedBy-Signature header using HMAC-SHA256 with your webhook secret (prefixed whsec_) before acting on any payload.

import crypto from "node:crypto";

function verifyWebhookSignature(body, signature, secret) {
  const expected = crypto
    .createHmac("sha256", secret)
    .update(body)
    .digest("hex");
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(`sha256=${expected}`)
  );
}

// In your webhook handler:
const isValid = verifyWebhookSignature(
  rawBody,
  req.headers["x-praisedby-signature"],
  process.env.WEBHOOK_SECRET // whsec_...
);
if (!isValid) return res.status(401).send("Invalid signature");
GET/api/webhooks

List all configured webhooks for your account.

Session (logged in)60 req/minPro

Response

200 OK
{
  "data": [
    {
      "id": "whk_a1b2c3d4e5f6",
      "url": "https://example.com/webhook",
      "events": ["new_testimonial", "status_change"],
      "is_active": true,
      "created_at": "2025-01-15T10:30:00Z"
    }
  ]
}
POST/api/webhooks

Create a new webhook endpoint.

Session (logged in)10 req/minPro

Request Body (JSON)

ParameterTypeRequiredDescription
urlstringYesHTTPS URL to receive webhook deliveries
eventsstring[]YesArray of event types: "new_testimonial", "status_change"

Response

201 Created
{
  "data": {
    "id": "whk_n3m4l5k6j7h8",
    "url": "https://example.com/webhook",
    "events": ["new_testimonial"],
    "secret": "whsec_a1b2c3d4e5f6...",
    "is_active": true,
    "created_at": "2025-03-15T14:30:00Z"
  }
}
Important: The webhook secret is shown only once in the creation response. Store it securely.
DELETE/api/webhooks/:id

Delete a webhook endpoint. No more events will be delivered to this URL.

Session (logged in)10 req/minPro

Response

200 OK
{ "success": true }

Integrations

Zapier Integration

Connect PraisedBy to 5,000+ apps via Zapier. The integration uses REST hooks with subscribe/unsubscribe endpoints:

EndpointPurpose
POST /api/zapier/subscribeRegister a Zapier webhook URL for new testimonials
DELETE /api/zapier/unsubscribeRemove a Zapier webhook subscription
GET /api/zapier/sampleReturns sample testimonial data for Zapier's field mapping
Tip: Search for "PraisedBy" in the Zapier app directory to set up triggers and actions without writing any code.

Make.com Integration

Use the PraisedBy API with Make.com's HTTP module to build custom automation scenarios:

  1. Create a new scenario in Make.com with the HTTP module
  2. Set the URL to https://praisedby.com/api/v1/spaces/YOUR_SPACE_ID/testimonials
  3. Add an Authorization header with your API key
  4. Use Make.com's Webhooks module to receive new_testimonial events in real-time

Teams

Manage your team members and invitations. Team endpoints use session authentication (must be logged in).

GET/api/team

Get your team details including all members and their roles.

Session (logged in)60 req/min

Response

200 OK
{
  "data": {
    "id": "team_a1b2c3d4e5f6",
    "name": "Acme Inc.",
    "members": [
      {
        "id": "mem_x9y8z7w6v5u4",
        "user_id": "usr_p1q2r3s4t5u6",
        "email": "owner@acme.com",
        "name": "John Doe",
        "role": "owner",
        "joined_at": "2025-01-01T00:00:00Z"
      },
      {
        "id": "mem_n3m4l5k6j7h8",
        "user_id": "usr_g7h8i9j0k1l2",
        "email": "editor@acme.com",
        "name": "Jane Smith",
        "role": "editor",
        "joined_at": "2025-01-15T10:30:00Z"
      }
    ],
    "created_at": "2025-01-01T00:00:00Z"
  }
}
POST/api/team

Create a new team. You become the owner automatically.

Session (logged in)10 req/minPro

Request Body (JSON)

ParameterTypeRequiredDescription
namestringYesTeam name (1-100 characters)

Response

201 Created
{
  "data": {
    "id": "team_n3m4l5k6j7h8",
    "name": "Acme Inc.",
    "created_at": "2025-03-15T14:30:00Z"
  }
}
POST/api/team/invite

Send an email invitation to join your team. The invitee receives a link to accept.

Session (logged in)10 req/minPro

Request Body (JSON)

ParameterTypeRequiredDescription
emailstringYesEmail address to invite
rolestringYes"admin", "editor", or "viewer"

Response

200 OK
{
  "data": {
    "invitation_id": "inv_a1b2c3d4e5f6",
    "email": "newmember@company.com",
    "role": "editor",
    "expires_at": "2025-03-22T14:30:00Z"
  }
}
POST/api/team/invite/accept

Accept a team invitation using the token from the invitation email.

Session (logged in)10 req/min

Request Body (JSON)

ParameterTypeRequiredDescription
tokenstringYesInvitation token from the email link

Response

200 OK
{
  "data": {
    "team_id": "team_a1b2c3d4e5f6",
    "role": "editor",
    "joined_at": "2025-03-15T14:30:00Z"
  }
}
PATCH/api/team/members/:memberId

Update a team member's role. Only team owners and admins can change roles.

Session (logged in)30 req/minPro

Request Body (JSON)

ParameterTypeRequiredDescription
rolestringYes"admin", "editor", or "viewer"

Response

200 OK
{
  "data": {
    "id": "mem_n3m4l5k6j7h8",
    "role": "admin",
    "updated_at": "2025-03-15T14:30:00Z"
  }
}
DELETE/api/team/members/:memberId

Remove a member from the team. Only owners and admins can remove members. Owners cannot be removed.

Session (logged in)10 req/minPro

Response

200 OK
{ "success": true }

Widget SDK

Embed testimonial widgets in your website using the praisedby-embed npm package or the popup script.

Installation

npm install praisedby-embed

React / Next.js

import { PraisedByWidget } from "praisedby-embed";

export default function Testimonials() {
  return <PraisedByWidget spaceId="your-space-id" />;
}

The widget automatically fetches testimonials and renders them using your space's configured theme and layout. Widget appearance is managed from your dashboard under Spaces > Widget — no code updates needed.

Popup Widget

Add a floating testimonial popup to any page with a single script tag:

<script
  src="https://praisedby.com/popup-widget.js"
  data-space-id="your-space-id"
  defer
></script>

The popup displays a rotating testimonial in the bottom corner of the page, encouraging social proof without disrupting the user experience.

Vanilla JavaScript

import { initPraisedByWidget } from "praisedby-embed";

const cleanup = initPraisedByWidget(
  document.getElementById("testimonials"),
  { spaceId: "your-space-id" }
);

// To remove the widget later:
cleanup();

Need help? Contact support