ScreenshotNeo

BlogUse cases

Testimonial Screenshot API

Generate customer testimonial graphics with an API, compare Pika and Orshot, and build a reliable rendering workflow with code examples.

By the ScreenshotNeo team1 October 20269 min read

Short answer: a testimonial screenshot API turns structured review data into a ready-to-share image. Send testimonial text, rating, author details, optional photo, typography settings, and output width; the service renders a PNG, JPEG, WebP, PDF, or another supported format. Pika documents a dedicated Testimonial Screenshot API, while Orshot provides a matching testimonial-screenshot template that can be rendered through its REST API or SDKs.

This guide explains the data model, integration choices, a complete do-it-yourself renderer, production checks, and how to automate batches for websites, social posts, email campaigns, and product dashboards.

What a testimonial screenshot API does

The API separates content from presentation. Your application stores a testimonial as structured fields, then submits those fields to a template renderer. The response is an image or document that can be saved, displayed, or sent to another workflow.

Input Purpose Typical validation
testimonialText The customer quotation Require text; normalize whitespace; enforce a maximum length for the selected layout
rating Star or score display Use the provider’s accepted range; Pika documents 0 as the value that hides the rating
authorPhoto Avatar image Use a stable HTTPS URL or base64 data; verify dimensions and content type
authorName Reviewer identity Trim whitespace and apply a length limit
authorDescription Role, company, or short attribution Keep it short enough to avoid wrapping into the quote area
testimonialFontSize Quote typography Choose from a small approved range so long quotes remain readable
authorMetaFontSize Author and description typography Keep smaller than the quote size, but above your accessibility minimum
width Output width Use a fixed set of widths for consistent cards and social formats

Choosing an API provider

Pika

Pika documents a dedicated testimonial screenshot API for custom text, author photo, author description, rating, and width. Its documented fields include testimonialText, testimonialFontSize, rating, authorPhoto, authorName, authorDescription, authorMetaFontSize, and width. Pika lists Node, Ruby, Python, and PHP SDKs, Zapier integration, emoji support, and binary, base64, or direct-link responses. Pika describes generation as taking a couple of seconds; treat that as a vendor claim and measure your own workload before setting an SLA.

Orshot

Orshot documents REST API and SDK rendering with a testimonial-screenshot template. Its template page lists PNG, JPG, JPEG, WebP, PDF, and MP4 outputs. It also lists Zapier, Make, n8n, webhooks, dynamic URLs, signed URLs, an MCP Server, and an Orshot CLI. Use the same core testimonial fields, then select the output format and delivery method supported by your account.

Decision What to check
API surface REST endpoint, SDK languages, authentication, and whether the response is binary, base64, or a hosted link
Template control Quote length, rating visibility, avatar handling, font sizes, width, and any provider-specific layout options
Automation Webhooks, queues, Zapier/Make/n8n connectors, retries, and idempotency behavior
Formats PNG for lossless UI cards, JPEG for smaller photographic assets, WebP for modern web delivery, PDF or MP4 when your channel requires it
Pricing and quotas Confirm current limits directly with the provider; the researched pages do not establish comparable prices or quotas

Define a stable testimonial payload

Keep your internal record provider-neutral, then map it to the provider’s field names. This lets you change templates without rewriting your review database.

{
  "id": "review_1042",
  "text": "The reporting workflow saves our team hours every week.",
  "rating": 5,
  "author": {
    "name": "Jordan Lee",
    "description": "Operations lead, Acme",
    "photoUrl": "https://cdn.example.com/avatars/jordan.jpg"
  },
  "design": {
    "quoteFontSize": 42,
    "authorFontSize": 20,
    "width": 1200
  }
}

Before rendering, validate required fields, reject unsupported image URLs, strip control characters, and decide how to handle missing ratings or photos. Store the provider request, response status, output URL or object key, and a content hash so retries do not create duplicate assets.

Build it yourself with HTML and Playwright

A local renderer is useful when you need total layout control or want to prototype before selecting a hosted API. The following Node.js script writes an HTML card, opens it in Chromium, and captures a PNG.

npm init -y
npm install playwright
npx playwright install chromium
// render-testimonial.mjs
import { chromium } from 'playwright';
import { writeFile } from 'node:fs/promises';

const testimonial = {
  text: 'The reporting workflow saves our team hours every week.',
  rating: 5,
  authorName: 'Jordan Lee',
  authorDescription: 'Operations lead, Acme',
  authorPhoto: 'https://i.pravatar.cc/160?img=12'
};

const stars = '★'.repeat(testimonial.rating) + '☆'.repeat(5 - testimonial.rating);
const safe = (value) => String(value).replace(/[&<>"']/g, (c) => ({ '&':'&amp;', '<':'&lt;', '>':'&gt;', '"':'&quot;', "'":'&#39;' }[c]));

const html = `<!doctype html>
<html>
<head>
<meta charset="utf-8">
<style>
  * { box-sizing: border-box; }
  body { margin: 0; width: 1200px; height: 675px; background: #eef2ff; font-family: Inter, Arial, sans-serif; }
  .card { width: 100%; height: 100%; padding: 76px; display: flex; flex-direction: column; justify-content: space-between; background: white; border: 1px solid #dbe3f0; border-radius: 28px; }
  .rating { color: #f59e0b; font-size: 34px; letter-spacing: 5px; }
  blockquote { margin: 28px 0 0; max-width: 980px; font-size: 42px; line-height: 1.18; color: #111827; }
  .author { display: flex; align-items: center; gap: 18px; }
  .author img { width: 72px; height: 72px; border-radius: 50%; object-fit: cover; }
  .name { font-size: 22px; font-weight: 700; color: #111827; }
  .description { margin-top: 5px; font-size: 18px; color: #64748b; }
</style>
</head>
<body>
  <main class="card">
    <section>
      <div class="rating" aria-label="${testimonial.rating} out of 5 stars">${stars}</div>
      <blockquote>“${safe(testimonial.text)}”</blockquote>
    </section>
    <footer class="author">
      <img src="${safe(testimonial.authorPhoto)}" alt="">
      <div><div class="name">${safe(testimonial.authorName)}</div><div class="description">${safe(testimonial.authorDescription)}</div></div>
    </footer>
  </main>
</body>
</html>`;

await writeFile('testimonial.html', html);
const browser = await chromium.launch();
const page = await browser.newPage({ viewport: { width: 1200, height: 675 }, deviceScaleFactor: 2 });
await page.goto(`file://${process.cwd()}/testimonial.html`, { waitUntil: 'networkidle' });
await page.screenshot({ path: 'testimonial.png', fullPage: true });
await browser.close();

For production, host avatar files yourself or use a trusted image proxy. Remote images can fail, change, or reveal visitor information. Add a fallback avatar and wait for image completion before capture.

Integrate a hosted API

Map the provider-neutral record to the vendor’s documented fields. Keep the endpoint and authentication details in environment variables because the researched product pages do not establish a universal endpoint or credential format.

{
  "testimonialText": "The reporting workflow saves our team hours every week.",
  "testimonialFontSize": 42,
  "rating": 5,
  "authorPhoto": "https://cdn.example.com/avatars/jordan.jpg",
  "authorName": "Jordan Lee",
  "authorDescription": "Operations lead, Acme",
  "authorMetaFontSize": 20,
  "width": 1200
}
  1. Choose the provider template and output format.
  2. Send the mapped payload with your API credential.
  3. Read the binary, base64, or direct-link response documented by that provider.
  4. Verify dimensions and content type, then store the result under a deterministic key such as testimonials/{reviewId}/{contentHash}.png.
  5. Retry transient failures with exponential backoff and an idempotency key when the provider supports one.

Or skip the browser setup

ScreenshotNeo captures a rendered testimonial page with one GET request. Put your testimonial HTML at a URL, then use the API to produce a clean PNG, JPEG, WebP, or PDF. See the ScreenshotNeo API documentation for the full option set.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

For testimonial pages, ScreenshotNeo can wait for a selector or delay, run custom JavaScript, apply custom CSS, capture one element, set a viewport or device preset, use retina scale, hide selectors, block unwanted requests, and cache a result for a TTL you choose. It accepts custom headers, cookies, user agents, Authorization, timezone, and geolocation when your page needs them. Async jobs with signed webhooks and bulk capture for up to 100 URLs per call help with campaigns. Signed links are available when a public <img> tag needs a protected result.

Cookie banners, newsletter popups, and chat widgets are removed before the shot. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed, and response headers identify the page verdict and whether it was billed. An MCP server provides take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients. The Free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000. Create a free ScreenshotNeo account.

Automation and batch generation

  • Trigger rendering when a review changes from pending to approved.
  • Keep moderation separate from rendering; never publish unapproved customer text automatically.
  • Generate variants for website, Open Graph, email, and social channels from the same record.
  • Use webhooks or a queue for long-running batches and persist each job’s status.
  • Limit concurrency to the provider’s quota and retry only timeout, rate-limit, and server errors.

Edge cases to handle

  • Long quotes: clamp, shorten with editorial approval, or select a taller template. Do not silently reduce type until it becomes unreadable.
  • Missing rating: omit the rating or use the provider’s documented hide value. Pika documents rating: 0 for hiding it.
  • Broken avatar: validate the URL before submission and provide a deterministic fallback.
  • Emoji and right-to-left text: confirm the selected font supports the characters and test both wrapping and screenshot output.
  • Unsafe user content: escape text when generating HTML and never insert untrusted markup directly.
  • Duplicate events: derive an idempotency key from review ID, template version, and content hash.
  • Privacy: obtain permission for names and photos, avoid putting private reviews in public URLs, and set retention rules for generated assets.

Troubleshooting

Symptom Likely cause Fix
Text is clipped Quote exceeds the template’s usable height Shorten the quote, increase canvas height, or lower the configured font size
Avatar is missing URL is blocked, expired, or not an image Check the URL with a HEAD/GET request, serve a stable image, and keep a fallback
Stars do not appear Rating is outside the accepted range or set to the hide value Validate the numeric range and confirm whether zero means hidden
Response cannot be opened Binary data was treated as text or base64 was not decoded Inspect the content type and follow the provider’s binary/base64 response handling
Intermittent timeouts Remote fonts, images, or page scripts are slow Self-host assets, wait for a specific selector, set a bounded timeout, and retry transient errors
Unexpected duplicate images Retries created new jobs Use an idempotency key or deterministic output key and check job status before resubmitting
Rate-limit errors Concurrency exceeds account quota Queue work, honor retry-after information, and cap parallel requests

Performance, reliability, and cost

Rendering time is dominated by page startup, fonts, avatar downloads, and image encoding. Measure p50 and p95 latency for your actual quote lengths and formats. Cache unchanged testimonials by content hash, reuse fonts, and avoid loading analytics or third-party widgets on the render page.

For reliability, record request IDs, provider status, output checksum, and the exact template version. Alert on elevated failure rates and keep the original structured testimonial so an image can be regenerated. Treat vendor speed statements as estimates until your own measurements confirm them.

Cost depends on the provider’s current pricing and quota, which are not established in the researched pages. Control spend with deduplication, caching, bounded retries, and asynchronous batches. ScreenshotNeo bills only clean shots; bot checks, blank pages, timeouts, failed loads, and cache hits cost nothing, and each response includes X-Page-Verdict and X-Billed headers.

FAQ

Can I generate testimonial images without a hosted service?

Yes. Render HTML/CSS with a headless browser such as Playwright, then store the resulting PNG or WebP. You own the layout and infrastructure, but you also own browser updates, fonts, retries, and asset loading.

Which format should I use?

Use PNG for crisp text and UI-style cards, WebP for smaller modern web assets, JPEG when photographic compression is acceptable, and PDF when the destination expects a document.

Can ratings be optional?

Yes. Model rating as nullable and map the absence of a rating to the provider’s documented hide behavior. Pika specifically documents zero as hiding the rating.

How do I prevent a customer’s quote from breaking the design?

Validate length, render a preview, test the longest approved quote, and choose a template with enough vertical space. Keep editorial shortening as an explicit step.

Can AI agents create these screenshots?

Yes. ScreenshotNeo provides an MCP server with take_screenshot, get_page_info, and capture_pdf tools for MCP clients such as Claude and Cursor.