PeerPush API
Free REST API to create and discover products. Find alternatives, compare tools, and access structured data on pricing, platforms, use cases, and audiences. Authenticate with a free API key you can create in one click.
The explorer sends real requests, so it needs a key too: paste yours into its Authentication panel before hitting Send.
Quick start
Set PEERPUSH_API_KEY to your free key and run any of these. "Try it" signs the request with your key for you. Get an API key.
Search products
Try itcurl -H "X-PeerPush-Api-Key: $PEERPUSH_API_KEY" \
"https://peerpush.com/api/v1/products?query=email+api&limit=3"Find alternatives
Try itcurl -H "X-PeerPush-Api-Key: $PEERPUSH_API_KEY" \
"https://peerpush.com/api/v1/products/notion/alternatives?limit=5"Compare products
Try itcurl -H "X-PeerPush-Api-Key: $PEERPUSH_API_KEY" \
"https://peerpush.com/api/v1/compare?products=vercel,netlify"Trending this week
Try itcurl -H "X-PeerPush-Api-Key: $PEERPUSH_API_KEY" \
"https://peerpush.com/api/v1/trending?period=week&limit=5"Create a product
Open explorercurl -X POST "https://peerpush.com/api/v1/products" \
-H "X-PeerPush-Api-Key: $PEERPUSH_API_KEY" \
-H "Content-Type: application/json" \
--data '{
"name": "Acme Notes",
"tagline": "Clear notes for your team",
"description": "Bring meeting notes, project plans, and shared knowledge together.",
"websiteUrl": "https://example.com",
"logoUrl": "https://peerpush.com/android-chrome-192x192.png",
"contactEmail": "[email protected]"
}'Replace the example details and logo with your own. These six fields are required; supplementary fields are optional. contactEmail records who to reach about the listing, is never published, and is not emailed by PeerPush. Returns 201 with data.id and a private data.claimUrl to sign in, claim the product, and confirm its details before choosing a plan. Keep the link private. Each successful request creates a new product.
Authentication
API requests need a free API key, sent in the X-PeerPush-Api-Key header, or as an api_key query parameter. There is no Authorization header: a PeerPush key is not a session token and never travels as one.
X-PeerPush-Api-Key header (recommended)
curl -H "X-PeerPush-Api-Key: $PEERPUSH_API_KEY" \
"https://peerpush.com/api/v1/trending?period=week&limit=5"api_key query parameter
curl "https://peerpush.com/api/v1/trending?period=week&limit=5&api_key=$PEERPUSH_API_KEY"A key in the URL is convenient for browsers and webhooks, but it leaks into server logs, browser history, and referrers. Responses authenticated that way are returned with Cache-Control: no-store so the key is never written to a shared or on-disk cache alongside its response. Discovery responses with header auth get private, max-age=60. Prefer a header wherever you control the client.
Requests without a valid key are rejected with 401 and a WWW-Authenticate header. Revoking a key takes effect on the next request.
{
"error": {
"code": "UNAUTHORIZED",
"message": "This endpoint requires a free PeerPush API key. Send it in the \"X-PeerPush-Api-Key\" header, or as an \"api_key\" query parameter.",
"details": {
"reason": "missing"
}
}
}Endpoints
Use the HTTP method shown for each endpoint. All endpoints return JSON. The interactive explorer documents request fields and limits.
| Endpoint |
|---|
POST /api/v1/productsCreate a product |
GET /api/v1/productsSearch and filter products |
GET /api/v1/products/:slugGet full product details |
GET /api/v1/products/:id/statusGet product creation status |
GET /api/v1/products/:slug/alternativesFind alternatives to a product |
GET /api/v1/compareCompare products side by side |
GET /api/v1/trendingGet trending products |
GET /api/v1/discoverDiscover products by criteria |
GET /api/v1/dealsProducts with active discounts |
GET /api/v1/launchesRecently launched products |
GET /api/v1/awardsAward-winning products |
GET /api/v1/categoriesList all categories |
GET /api/v1/use-casesList all use cases |
GET /api/v1/audiencesList all audiences |
Response shape
Discovery product endpoints return data in this structure.
{
"data": {
"id": "...",
"slug": "example-product",
"name": "Example Product",
"tagline": "A great tool for developers",
"description": "...",
"websiteUrl": "https://example.com?utm_source=peerpush&ref=peerpush",
"appStoreUrl": "https://apps.apple.com/app/id123456789?utm_source=peerpush&ref=peerpush",
"playStoreUrl": "https://play.google.com/store/apps/details?id=com.example&utm_source=peerpush&ref=peerpush",
"mcpServerUrl": "https://mcp.example.com/mcp",
"cliInstallCommand": "npm install -g example-cli",
"apiDocsUrl": "https://example.com/docs/api?utm_source=peerpush&ref=peerpush",
"peerpushUrl": "https://peerpush.com/p/example-product",
"logoUrl": "https://...",
"pricing": {
"type": "Freemium",
"startingPrice": 9.99
},
"platforms": [
"Web",
"Api",
"Cli"
],
"useCases": [
"Code Development",
"CI/CD"
],
"audiences": [
"Developers",
"Startups"
],
"categories": [
"Developer Tools"
],
"community": {
"upvotes": 142,
"followers": 38,
"comments": 24,
"rating": {
"average": 4.6,
"count": 23,
"weighted": 4.42
},
"peerPushScore": 56.5,
"totalScore": 268
},
"badges": {
"current": "Trending",
"productOfTheDay": null,
"productOfTheWeek": "2025-03-15T00:00:00.000Z",
"productOfTheMonth": null
},
"social": {
"twitterUsername": "example",
"linkedinUrl": null
},
"publishedAt": "2025-02-01T12:00:00.000Z",
"lastUpdatedAt": "2025-03-20T08:30:00.000Z",
"uptime": {
"status": "Online",
"checkedAt": "2025-03-20T03:00:00.000Z"
},
"activeDeals": [
{
"title": "Spring sale",
"percentOff": 25,
"validUntil": "2025-04-30T23:59:59.000Z"
}
],
"recentUpdates": [
{
"title": "Shipped v2",
"content": "Faster builds and a new CLI.",
"createdAt": "2025-03-18T10:00:00.000Z",
"engagement": {
"upvotes": 12,
"comments": 3
}
}
]
}
}Field reference
| Field | Description |
|---|---|
id | Stable internal identifier. |
slug | URL-safe slug, also used in /p/:slug and as the path param for product endpoints. |
name | Product name. |
tagline | Short one-line description. |
description | Long description. |
websiteUrl | External product URL. Carries PeerPush UTM tags. |
appStoreUrl | Apple App Store install link, or null. Carries PeerPush UTM tags. |
playStoreUrl | Google Play Store install link, or null. Carries PeerPush UTM tags. |
mcpServerUrl | Remote MCP server endpoint or registry/install page URL, or null. Returned raw - connect or install directly. |
cliInstallCommand | One-line CLI install command (e.g. "npm install -g example-cli"), or null. |
apiDocsUrl | API documentation link, or null. Carries PeerPush UTM tags. |
peerpushUrl | Canonical product page on peerpush.com. |
logoUrl | CDN URL of the product logo. |
pricing.type | One of: Free, Freemium, Subscription, OneTime, Paid. |
pricing.startingPrice | Starting price in USD, when applicable. |
platforms | Subset of: Web, Mobile, Api, Desktop, Mcp, Cli. |
useCases | Controlled-vocabulary use case labels (max 3). |
audiences | Controlled-vocabulary audience labels (max 3). |
categories | Category labels. |
community.upvotes | Total upvotes. |
community.followers | Followers count. |
community.comments | Visible comment count. |
community.rating | null when there are no public ratings; otherwise { average, count, weighted } where weighted is a Bayesian-smoothed score for ranking. |
community.peerPushScore | PeerPush engagement points. |
community.totalScore | Composite ranking score used internally to sort feeds. |
badges.current | One of: Live, Trending, ProductOfTheDay, ProductOfTheWeek, ProductOfTheMonth. |
badges.productOfTheDay | When the product won Product of the Day, if ever. |
badges.productOfTheWeek | When the product won Product of the Week, if ever. |
badges.productOfTheMonth | When the product won Product of the Month, if ever. |
social.twitterUsername | Twitter / X handle without @. |
social.linkedinUrl | LinkedIn company page URL. |
publishedAt | When the product was first published on PeerPush. |
lastUpdatedAt | When the owner last posted a product update. |
uptime.status | Result of the most recent uptime probes. One of: Online (all recent probes succeeded), Degraded (mixed results), Offline (all recent probes failed), Unknown (no probes recorded yet). |
uptime.checkedAt | When the status was last recomputed. |
activeDeals[] | Currently-valid discount codes: { title, percentOff (0-100), validUntil (ISO date) }. |
recentUpdates[] | Up to 10 most recent product updates, newest first: { title, content, createdAt (ISO date), engagement: { upvotes, comments } }. Empty array when the owner has posted none. |
Endpoints with a different shape
List endpoints return data as an array of product objects. These endpoints wrap the data differently.
POST /api/v1/products · GET /api/v1/products/:id/status
data.id | Product ID. Creation returns 201 and a Location header pointing to /api/v1/products/:id/status. Follow it with the originating API key to retrieve this status response. GET /api/v1/products/:slug continues to return full public product details. |
data.status | ready, claimed, expired, or revoked. Products start private and unclaimed; claiming does not publish or purchase anything. |
data.claimUrl | Private link to review and claim the product. Sign in to claim, then confirm the details in the normal product editor before choosing a plan. The link no longer grants access after claim, expiry, or revocation. Responses use Cache-Control: no-store. |
data.expiresAt | The link expires 30 days after creation. Retrying does not extend its expiry. |
data.claimedAt | When ownership was assigned, or null before claim. |
/api/v1/products/:slug/alternatives
product | The source product: { name, slug }. |
alternatives | Alternative products, each a full product object. This is the array that limit/offset paginate. |
externalAlternatives | Alternatives not on PeerPush: { name, websiteUrl (UTM-tagged), iconUrl (string | null) }. Not paginated - returned on the first page only, and empty when offset > 0. |
/api/v1/compare
products | The compared products, each a full product object, in the order requested. Each identifier resolves to at most one product (exact slug first, then name). |
notFound | Requested identifiers that matched no published product. The comparison still runs on the ones that resolved, as long as at least 2 did. |
comparison.sharedUseCases | Use cases every compared product has. |
comparison.sharedAudiences | Audiences every compared product has. |
comparison.sharedPlatforms | Platforms every compared product supports. |
comparison.uniqueFeatures | Keyed by product slug: { uniqueUseCases, uniquePlatforms } - what that product has and the others do not. |
/api/v1/deals
deal | On top of every product field: the highest-percentage currently-valid discount, { title, percentOff, validUntil (ISO date) }. Also present in that product’s activeDeals. |
/api/v1/awards
award | On top of every product field: { type (day | week | month), date (ISO date), position } where position is the placement in that award period (1 = winner). The most prestigious award a product holds is the one returned. |
/api/v1/categories, /api/v1/use-cases, /api/v1/audiences
data[] | Taxonomy items: { slug, name }. |
Errors
Every error uses the same envelope. The code is stable - branch on it rather than on the message.
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Invalid query parameters",
"details": [
{
"code": "invalid_value",
"path": [
"platforms",
0
],
"message": "Invalid option: expected one of \"Web\"|\"Api\"|\"Cli\""
}
]
}
}| Status | Code | When |
|---|---|---|
401 | UNAUTHORIZED / INVALID_API_KEY | No API key was sent (UNAUTHORIZED), or the key sent is unknown, revoked, or malformed (INVALID_API_KEY). details.reason narrows it to missing, unknown, revoked, or malformed. |
400 | VALIDATION_ERROR | A query parameter or request body field is missing, malformed, or outside its allowed range. details carries field validation errors when available. |
403 | FORBIDDEN | The requested operation is not permitted. |
404 | NOT_FOUND | The requested product is unavailable to this API key, or fewer than 2 identifiers resolved for /compare. |
409 | CONFLICT | An idempotency key or external reference was already used with different product data. |
413 | PAYLOAD_TOO_LARGE | The product creation JSON body exceeds 64 KB. |
429 | RATE_LIMITED | Hourly per-key quota exhausted. Retry-After and X-RateLimit-Reset tell you when to retry. |
500 | INTERNAL_ERROR | Unexpected server error. To safely retry product creation, supply an optional Idempotency-Key on the original request and reuse it with the same payload. |
Rate limits
The API allows 5,000 requests per hour per API key. Each key has its own quota, so a second key does not share the first one. Every response includes rate limit headers:
X-RateLimit-Limit: 5000
X-RateLimit-Remaining: 5000
X-RateLimit-Reset: 1711929600Need higher usage limits or new API endpoints? Contact us at [email protected]
Prefer AI-native access?
Connect your AI assistant directly to PeerPush via MCP. Your assistant will automatically use PeerPush when you ask about software products. Product creation is currently available through the REST API only.