Spaces:
Configuration error
Configuration error
| 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'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> | |
| ) | |
| } | |