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:
- Create an API key — Go to Settings → API Keys and generate a new key. Copy it immediately — it's shown only once.
- Make your first request
- Explore — Browse the endpoint reference below.
curl -H "Authorization: Bearer pb_live_YOUR_KEY" \
https://praisedby.com/api/v1/spacesAuthentication
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_KEYBase URL
All versioned API endpoints are relative to:
https://praisedby.com/api/v1Public 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.
| Endpoint | Limit | Window |
|---|---|---|
GET /spaces | 100 | 60s |
GET /spaces/:id/testimonials | 100 | 60s |
POST /spaces/:id/testimonials | 30 | 60s |
GET /testimonials/:id | 200 | 60s |
PATCH /testimonials/:id | 60 | 60s |
DELETE /testimonials/:id | 30 | 60s |
GET /spaces/:id/analytics | 60 | 60s |
POST /api/testimonials (public) | 5 | 60s per IP |
GET /widget/:spaceId | 200 | 60s |
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)" }
]
}| Code | Meaning |
|---|---|
400 | Invalid request body or query parameters |
401 | Missing or invalid API key |
403 | Feature not available on your plan |
404 | Resource not found or not owned by you |
429 | Rate limit exceeded — wait and retry |
500 | Internal 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
}
}?status=approved to filter testimonials before pagination to reduce payload size.Spaces
/api/v1/spacesRetrieve all spaces belonging to your account.
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/spacesTestimonials
/api/v1/spaces/:spaceId/testimonialsList testimonials for a space. Supports filtering by status, tag, and pagination.
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
status | string | No | "pending", "approved", or "rejected" |
tag | string | No | Tag ID to filter by |
page | integer | No | Page number (default: 1) |
per_page | integer | No | Results 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"/api/v1/spaces/:spaceId/testimonialsCreate a new testimonial. The source is automatically set to "api".
Request Body (JSON)
| Parameter | Type | Required | Description |
|---|---|---|---|
customer_name | string | Yes | 1-100 characters |
customer_email | string | Yes | Valid email address |
customer_company | string | No | Max 100 characters |
customer_role | string | No | Max 100 characters |
rating | integer | Yes | 1 to 5 |
content | string | Yes | 10-2000 characters |
status | string | No | "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"
}
}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/api/v1/testimonials/:idRetrieve full details for a single testimonial, including enhanced content and video fields.
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
id | string | Yes | Testimonial 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/api/v1/testimonials/:idUpdate a testimonial's status or star flag. The reviewed_at timestamp is set automatically on status changes.
Request Body (JSON)
| Parameter | Type | Required | Description |
|---|---|---|---|
status | string | No | "pending", "approved", or "rejected" |
is_starred | boolean | No | Star 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/api/v1/testimonials/:idPermanently delete a testimonial. The space's testimonial_count is automatically decremented.
Response
200 OK{ "success": true }curl -X DELETE \
-H "Authorization: Bearer pb_live_YOUR_KEY" \
https://praisedby.com/api/v1/testimonials/TESTIMONIAL_ID/api/testimonialsPublic endpoint for end-users to submit testimonials via your collection form. No authentication required. Rate limited by IP address.
Request Body (JSON)
| Parameter | Type | Required | Description |
|---|---|---|---|
spaceId | string | Yes | The space to submit the testimonial to |
customer_name | string | Yes | 1-100 characters |
customer_email | string | Yes | Valid email address |
customer_company | string | No | Company name |
customer_role | string | No | Job title / role |
rating | integer | Yes | 1 to 5 |
content | string | Yes | 10-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/api/spaces/:spaceId/exportExport all testimonials for a space as a CSV file. Useful for backups, reporting, or importing into other tools.
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
spaceId | string | Yes | The 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/exportAnalytics
/api/v1/spaces/:spaceId/analyticsWidget impression, click, and conversion analytics. Starter plan gets basic analytics; Pro plan gets full analytics with all time periods.
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
period | string | No | "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
}
]
}
}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.
/api/widget/:spaceIdFetch approved testimonials and widget configuration for rendering the embed widget. Returns testimonials, space settings, and theme configuration.
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/api/widget/:spaceId/schemaReturns JSON-LD structured data (Schema.org) for your testimonials. Embed this in your page's <head> to enable Google rich snippets with star ratings.
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/api/widget/:spaceId/eventTrack widget impressions, clicks, and conversion events. Used internally by the widget SDK to power analytics.
Request Body (JSON)
| Parameter | Type | Required | Description |
|---|---|---|---|
type | string | Yes | "impression", "click", or "conversion" |
testimonialId | string | No | ID of the specific testimonial interacted with |
metadata | object | No | Additional 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/eventWebhooks
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.
| Event | Description |
|---|---|
new_testimonial | Fired when a new testimonial is submitted (from form, API, or import) |
status_change | Fired 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_idSignature 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");/api/webhooksList all configured webhooks for your account.
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"
}
]
}/api/webhooksCreate a new webhook endpoint.
Request Body (JSON)
| Parameter | Type | Required | Description |
|---|---|---|---|
url | string | Yes | HTTPS URL to receive webhook deliveries |
events | string[] | Yes | Array 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"
}
}/api/webhooks/:idDelete a webhook endpoint. No more events will be delivered to this URL.
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:
| Endpoint | Purpose |
|---|---|
POST /api/zapier/subscribe | Register a Zapier webhook URL for new testimonials |
DELETE /api/zapier/unsubscribe | Remove a Zapier webhook subscription |
GET /api/zapier/sample | Returns sample testimonial data for Zapier's field mapping |
Make.com Integration
Use the PraisedBy API with Make.com's HTTP module to build custom automation scenarios:
- Create a new scenario in Make.com with the HTTP module
- Set the URL to
https://praisedby.com/api/v1/spaces/YOUR_SPACE_ID/testimonials - Add an Authorization header with your API key
- Use Make.com's Webhooks module to receive
new_testimonialevents in real-time
Teams
Manage your team members and invitations. Team endpoints use session authentication (must be logged in).
/api/teamGet your team details including all members and their roles.
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"
}
}/api/teamCreate a new team. You become the owner automatically.
Request Body (JSON)
| Parameter | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Team name (1-100 characters) |
Response
201 Created{
"data": {
"id": "team_n3m4l5k6j7h8",
"name": "Acme Inc.",
"created_at": "2025-03-15T14:30:00Z"
}
}/api/team/inviteSend an email invitation to join your team. The invitee receives a link to accept.
Request Body (JSON)
| Parameter | Type | Required | Description |
|---|---|---|---|
email | string | Yes | Email address to invite |
role | string | Yes | "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"
}
}/api/team/invite/acceptAccept a team invitation using the token from the invitation email.
Request Body (JSON)
| Parameter | Type | Required | Description |
|---|---|---|---|
token | string | Yes | Invitation token from the email link |
Response
200 OK{
"data": {
"team_id": "team_a1b2c3d4e5f6",
"role": "editor",
"joined_at": "2025-03-15T14:30:00Z"
}
}/api/team/members/:memberIdUpdate a team member's role. Only team owners and admins can change roles.
Request Body (JSON)
| Parameter | Type | Required | Description |
|---|---|---|---|
role | string | Yes | "admin", "editor", or "viewer" |
Response
200 OK{
"data": {
"id": "mem_n3m4l5k6j7h8",
"role": "admin",
"updated_at": "2025-03-15T14:30:00Z"
}
}/api/team/members/:memberIdRemove a member from the team. Only owners and admins can remove members. Owners cannot be removed.
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-embedReact / 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