open-prompt / src /app /docs /api /page.tsx
GitHub Action
Automated sync to Hugging Face
bcce530
import { Metadata } from 'next'
import { generateSEO } from '@/lib/seo'
import { Badge } from '@/components/ui/badge'
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
const BASE_URL = process.env.NEXT_PUBLIC_APP_URL || 'https://open-prompt.netlify.app'
export const metadata: Metadata = generateSEO({
title: 'API Documentation | OpenPrompt',
description: 'OpenPrompt Public REST API — run AI prompts and access 177+ AI tools programmatically. No signup required for public endpoints.',
url: `${BASE_URL}/docs/api`,
keywords: ['OpenPrompt API', 'AI prompts API', 'REST API', 'prompt API', 'AI tools API'],
}) as Metadata
const endpoints = [
{
method: 'GET',
path: '/api/v1',
description: 'API overview — lists all endpoints, rate limits, and authentication info.',
response: '{ name, version, endpoints[], rateLimits }',
auth: false,
},
{
method: 'GET',
path: '/api/v1/prompts',
description: 'List public prompts with search, filter, sort, and pagination.',
params: [
{ name: 'q', type: 'string', desc: 'Search term (title, description, tags)' },
{ name: 'category', type: 'string', desc: 'Filter by category' },
{ name: 'tags', type: 'string', desc: 'Comma-separated tag filter' },
{ name: 'sort', type: 'runs | stars | newest', desc: 'Sort order (default: newest)' },
{ name: 'page', type: 'number', desc: 'Page number (default: 1)' },
{ name: 'limit', type: 'number', desc: 'Results per page (max 100, default 20)' },
],
response: '{ data: Prompt[], pagination: { page, limit, total, totalPages } }',
auth: false,
},
{
method: 'GET',
path: '/api/v1/prompts/:slug',
description: 'Retrieve a single public prompt by its slug, including template and schema.',
response: '{ data: Prompt }',
auth: false,
},
{
method: 'POST',
path: '/api/v1/prompts/:slug/run',
description: 'Execute a prompt with variable substitution. Supports streaming.',
body: [
{ name: 'variables', type: 'object', desc: 'Key-value pairs matching the prompt schema' },
{ name: 'model', type: 'string', desc: 'Model override (must be in prompt\'s modelAllowed)' },
{ name: 'stream', type: 'boolean', desc: 'Stream response as text/plain (default: false)' },
],
response: '{ output: string, model: string, cached: boolean }',
auth: false,
rateLimit: '10/hr guest · 50/hr authenticated · 500/hr Pro',
},
{
method: 'GET',
path: '/api/v1/tools',
description: 'List all 177+ AI tools with name, description, category, and icon.',
params: [
{ name: 'category', type: 'string', desc: 'Filter by category' },
{ name: 'q', type: 'string', desc: 'Search by name or description' },
{ name: 'limit', type: 'number', desc: 'Max results (default 50, max 200)' },
],
response: '{ data: Tool[], total, totalAll }',
auth: false,
},
]
const codeExamples = {
listPrompts: `// List top prompts for "marketing"
const response = await fetch(
'https://open-prompt.netlify.app/api/v1/prompts?category=marketing&sort=stars&limit=10'
)
const { data, pagination } = await response.json()
console.log(\`Found \${pagination.total} prompts\`)`,
runPrompt: `// Execute the "blog-writer" prompt
const response = await fetch(
'https://open-prompt.netlify.app/api/v1/prompts/blog-writer/run',
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
variables: {
topic: 'The Future of AI Agents',
tone: 'professional',
length: '800 words',
},
model: 'gemini-2.0-flash',
}),
}
)
const { output } = await response.json()
console.log(output)`,
streamPrompt: `// Stream a prompt response in real-time
const response = await fetch(
'https://open-prompt.netlify.app/api/v1/prompts/blog-writer/run',
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
variables: { topic: 'AI trends 2026' },
stream: true, // ← enable streaming
}),
}
)
const reader = response.body.getReader()
const decoder = new TextDecoder()
while (true) {
const { done, value } = await reader.read()
if (done) break
process.stdout.write(decoder.decode(value))
}`,
listTools: `// Search for email-related tools
const response = await fetch(
'https://open-prompt.netlify.app/api/v1/tools?q=email&limit=5'
)
const { data } = await response.json()
data.forEach(tool => console.log(\`\${tool.icon} \${tool.name}\`))`,
}
const methodColors: Record<string, string> = {
GET: 'bg-green-500/15 text-green-600 dark:text-green-400 border-green-500/30',
POST: 'bg-blue-500/15 text-blue-600 dark:text-blue-400 border-blue-500/30',
PUT: 'bg-amber-500/15 text-amber-600 dark:text-amber-400 border-amber-500/30',
DELETE: 'bg-red-500/15 text-red-600 dark:text-red-400 border-red-500/30',
}
export default function ApiDocsPage() {
return (
<div className="container mx-auto px-4 py-12 max-w-5xl">
{/* Header */}
<div className="mb-12">
<div className="flex items-center gap-3 mb-4">
<h1 className="text-4xl font-serif font-medium">API Documentation</h1>
<Badge variant="secondary">v1.0</Badge>
<Badge className="bg-green-500/15 text-green-600 border-green-500/30 border">
Public Beta
</Badge>
</div>
<p className="text-muted-foreground text-lg max-w-3xl">
OpenPrompt&apos;s public REST API lets you run prompts, execute AI tools, and browse
the prompt library programmatically. No API key required for public endpoints.
</p>
</div>
{/* Base URL */}
<Card className="mb-10 border-primary/30">
<CardContent className="p-6">
<div className="flex flex-wrap items-center gap-4">
<div>
<p className="text-xs font-medium text-muted-foreground uppercase tracking-wider mb-1">Base URL</p>
<code className="text-primary font-mono text-lg">
{BASE_URL}/api/v1
</code>
</div>
<div className="h-8 w-px bg-border hidden md:block" />
<div>
<p className="text-xs font-medium text-muted-foreground uppercase tracking-wider mb-1">Rate Limits</p>
<p className="text-sm">
<span className="font-medium">10 req/hr</span> guest ·{' '}
<span className="font-medium">50 req/hr</span> authenticated ·{' '}
<span className="font-medium text-primary">500 req/hr</span> Pro
</p>
</div>
<div className="h-8 w-px bg-border hidden md:block" />
<div>
<p className="text-xs font-medium text-muted-foreground uppercase tracking-wider mb-1">Authentication</p>
<p className="text-sm text-muted-foreground">Not required for public data</p>
</div>
</div>
</CardContent>
</Card>
{/* Endpoints */}
<section className="mb-12">
<h2 className="text-2xl font-serif font-medium mb-6">Endpoints</h2>
<div className="space-y-4">
{endpoints.map((ep) => (
<Card key={`${ep.method}-${ep.path}`} className="overflow-hidden">
<CardHeader className="pb-3 pt-4 px-6">
<div className="flex items-start gap-3 flex-wrap">
<span className={`text-xs font-bold px-2.5 py-1 rounded border font-mono ${methodColors[ep.method]}`}>
{ep.method}
</span>
<code className="font-mono text-sm text-foreground bg-muted px-3 py-1 rounded-md">
{ep.path}
</code>
{!ep.auth && (
<Badge variant="outline" className="text-xs ml-auto">No auth</Badge>
)}
</div>
<p className="text-muted-foreground text-sm mt-2">{ep.description}</p>
</CardHeader>
<CardContent className="px-6 pb-4 space-y-4">
{ep.params && ep.params.length > 0 && (
<div>
<p className="text-xs font-semibold text-muted-foreground uppercase tracking-wider mb-2">
Query Parameters
</p>
<div className="rounded-lg border overflow-hidden">
{ep.params.map((p, i) => (
<div
key={p.name}
className={`flex items-start gap-3 px-4 py-2.5 text-sm ${
i % 2 === 0 ? 'bg-muted/30' : ''
}`}
>
<code className="font-mono text-primary font-medium w-24 shrink-0">{p.name}</code>
<code className="text-muted-foreground text-xs w-32 shrink-0 mt-0.5">{p.type}</code>
<span className="text-muted-foreground">{p.desc}</span>
</div>
))}
</div>
</div>
)}
{ep.body && ep.body.length > 0 && (
<div>
<p className="text-xs font-semibold text-muted-foreground uppercase tracking-wider mb-2">
Request Body (JSON)
</p>
<div className="rounded-lg border overflow-hidden">
{ep.body.map((p, i) => (
<div
key={p.name}
className={`flex items-start gap-3 px-4 py-2.5 text-sm ${
i % 2 === 0 ? 'bg-muted/30' : ''
}`}
>
<code className="font-mono text-primary font-medium w-24 shrink-0">{p.name}</code>
<code className="text-muted-foreground text-xs w-32 shrink-0 mt-0.5">{p.type}</code>
<span className="text-muted-foreground">{p.desc}</span>
</div>
))}
</div>
</div>
)}
<div>
<p className="text-xs font-semibold text-muted-foreground uppercase tracking-wider mb-2">
Response
</p>
<code className="text-xs font-mono bg-muted px-3 py-2 rounded-md block text-muted-foreground">
{ep.response}
</code>
</div>
{ep.rateLimit && (
<p className="text-xs text-muted-foreground">
<span className="font-medium">Rate limit:</span> {ep.rateLimit}
</p>
)}
</CardContent>
</Card>
))}
</div>
</section>
{/* Code Examples */}
<section className="mb-12">
<h2 className="text-2xl font-serif font-medium mb-6">Code Examples</h2>
<div className="space-y-6">
{[
{ title: 'List prompts by category', code: codeExamples.listPrompts },
{ title: 'Run a prompt with variables', code: codeExamples.runPrompt },
{ title: 'Stream a prompt response', code: codeExamples.streamPrompt },
{ title: 'Search AI tools', code: codeExamples.listTools },
].map(({ title, code }) => (
<div key={title}>
<h3 className="text-sm font-semibold mb-2 text-muted-foreground uppercase tracking-wide">
{title}
</h3>
<pre className="bg-[#0d1117] text-[#e6edf3] rounded-xl p-5 overflow-x-auto text-sm font-mono leading-relaxed border border-border/50">
<code>{code}</code>
</pre>
</div>
))}
</div>
</section>
{/* Rate Limit Headers */}
<section className="mb-12">
<h2 className="text-2xl font-serif font-medium mb-4">Rate Limit Headers</h2>
<p className="text-muted-foreground mb-4">
Every API response includes rate limit information in the response headers:
</p>
<div className="rounded-lg border overflow-hidden">
{[
{ header: 'X-RateLimit-Limit', desc: 'Maximum requests allowed in the current window' },
{ header: 'X-RateLimit-Remaining', desc: 'Requests remaining in the current window' },
{ header: 'X-RateLimit-Reset', desc: 'Unix timestamp when the window resets' },
{ header: 'X-Cache', desc: 'HIT if served from cache, MISS if freshly generated' },
].map((h, i) => (
<div
key={h.header}
className={`flex items-center gap-4 px-4 py-3 text-sm ${i % 2 === 0 ? 'bg-muted/30' : ''}`}
>
<code className="font-mono text-primary font-medium w-52 shrink-0">{h.header}</code>
<span className="text-muted-foreground">{h.desc}</span>
</div>
))}
</div>
</section>
{/* Coming Soon */}
<Card className="border-primary/20 bg-primary/5">
<CardContent className="p-6">
<h3 className="font-semibold mb-2">🔑 API Keys Coming Soon</h3>
<p className="text-muted-foreground text-sm">
Pro users will receive API keys for higher rate limits (500 req/hr) and access
to private prompts. Subscribe to Pro to be first in line.
</p>
</CardContent>
</Card>
</div>
)
}