# Promptster API Documentation (Full) # Generated at build time β€” all API doc pages concatenated for AI agent consumption. # Website: https://www.promptster.dev # OpenAPI Spec: https://www.promptster.dev/docs/api/openapi.yaml --- ## Section: API Overview & Quick Start --- # Promptster API Documentation Welcome to the Promptster Public API! Test and compare AI prompts programmatically across multiple providers (OpenAI, Anthropic, Google, DeepSeek, xAI, Groq, Mistral, Perplexity, Together AI) with a single unified API. --- ## Quick Start (5 Minutes) ### 1. Get Your API Key Visit [/developer/api-keys](https://www.promptster.dev/developer/api-keys) and create a new API key: - **Test keys**: Free, limited to 5 requests/minute (perfect for development) - **Live keys**: Based on your plan (see Rate Limits below) Copy your key immediately - you'll only see it once! ### 2. Get Provider API Keys You'll need API keys from the AI providers you want to use: - [OpenAI](https://platform.openai.com/api-keys) - For GPT models - [Anthropic](https://console.anthropic.com/) - For Claude models - [Google AI](https://makersuite.google.com/app/apikey) - For Gemini models - [DeepSeek](https://platform.deepseek.com/api_keys) - For DeepSeek models - [xAI](https://console.x.ai/) - For Grok models - [Groq](https://console.groq.com/keys) - For Groq-hosted models - [Mistral](https://console.mistral.ai/api-keys/) - For Mistral models - [Perplexity](https://www.perplexity.ai/settings/api) - For Sonar models - [Together AI](https://api.together.xyz/settings/api-keys) - For open-source models ### 3. Make Your First Request ```bash curl -X POST https://www.promptster.dev/v1/prompts/test \ -H "Authorization: Bearer YOUR_PROMPTSTER_KEY" \ -H "X-ANTHROPIC-API-KEY: YOUR_ANTHROPIC_KEY" \ -H "Content-Type: application/json" \ -d '{ "provider": "anthropic", "model": "claude-sonnet-4-20250514", "prompt": "Say hello in exactly 5 words" }' ``` ### 4. See the Response ```json { "id": "test_abc123def456", "provider": "anthropic", "model": "claude-sonnet-4-20250514", "prompt": "Say hello in exactly 5 words", "response": "Hello there, how are you?", "metadata": { "input_tokens": 13, "output_tokens": 8, "total_tokens": 21, "response_time_ms": 1847, "tokens_per_second": 4.3, "estimated_cost": 0.000105 }, "created_at": "2026-01-25T12:34:56.789Z" } ``` **That's it!** You just tested an AI prompt and got detailed performance metrics including cost estimation. --- ## Key Features ### 🎯 Single Unified API One API for all 11 providers - no need to integrate with multiple different APIs ### πŸ’° Transparent Cost Tracking Every response includes accurate cost estimates based on real provider pricing ### ⚑ Performance Metrics Track response time, tokens/second, and token usage for every request ### πŸ”„ Easy Comparisons Test the same prompt across multiple providers in parallel with a single API call ### πŸ“Š Usage Analytics Monitor your API usage, costs, and request history ### πŸ€– MCP for AI Editors Connect Claude Code, Cursor, or Windsurf to test prompts directly from your editor via MCP ### πŸ”’ Secure & Rate Limited SHA-256 hashed API keys with tiered rate limiting to protect your quota --- ## Core Endpoints | Endpoint | Method | Purpose | |----------|--------|---------| | [/v1/prompts/test](endpoints/test-prompt.md) | POST | Test a single prompt with one provider | | [/v1/prompts/compare](endpoints/compare-prompts.md) | POST | Compare the same prompt across multiple providers | | [/v1/usage](endpoints/usage.md) | GET | Get your current usage statistics and limits | | [/v1/history](endpoints/history.md) | GET | Retrieve your past API requests with filtering | | [MCP Server](mcp-integration.md) | POST | Connect AI coding assistants via Model Context Protocol | ## Schedule Endpoints | Endpoint | Method | Purpose | |----------|--------|---------| | [/v1/schedules](endpoints/schedules.md) | GET | List all scheduled tests | | [/v1/schedules](endpoints/schedules.md#post-v1schedules) | POST | Create a new scheduled test | | [/v1/schedules/:id](endpoints/schedules.md#get-v1schedulesid) | GET | Get schedule details | | [/v1/schedules/:id](endpoints/schedules.md#patch-v1schedulesid) | PATCH | Update a schedule | | [/v1/schedules/:id](endpoints/schedules.md#delete-v1schedulesid) | DELETE | Delete a schedule | | [/v1/schedules/:id/run](endpoints/schedules.md#post-v1schedulesidrun) | POST | Manually trigger execution | | [/v1/schedules/:id/runs](endpoints/schedule-runs.md) | GET | Get run history for a schedule | --- ## Authentication All API requests require two types of authentication: ### 1. Promptster API Key (Required) Include in the `Authorization` header: ``` Authorization: Bearer pk_live_abc123... ``` ### 2. Provider API Key (Required) Include the provider's API key in a custom header based on the provider: ``` X-OPENAI-API-KEY: sk-proj-... # For OpenAI X-ANTHROPIC-API-KEY: sk-ant-... # For Anthropic X-GOOGLE-API-KEY: AIza... # For Google X-DEEPSEEK-API-KEY: ... # For DeepSeek X-XAI-API-KEY: xai-... # For xAI (Grok) X-GROQ-API-KEY: gsk_... # For Groq X-MISTRAL-API-KEY: ... # For Mistral X-PERPLEXITY-API-KEY: pplx-... # For Perplexity X-TOGETHER-API-KEY: ... # For Together AI ``` **Why both keys?** - Your Promptster key tracks your usage and enforces rate limits - Provider keys are passed directly to the AI services (we don't store them) See [Authentication Guide](authentication.md) for details. --- ## Rate Limits Rate limits and included calls are based on your subscription tier: | Tier | Included Calls/Month | Rate Limit (per minute) | Price | |------|---------------------|------------------------|-------| | **Free** | 500 | 5 | $0 | | **Builder** | 5,000 | 30 | $19/mo | | **Scale** | 25,000 | 120 | $49/mo | Every response includes rate limit and billing headers: ``` X-RateLimit-Remaining-Calls: 4953 X-Credit-Balance: 0 X-Billing-Source: plan_included X-RateLimit-Tier: builder ``` When you exceed your per-minute rate limit, you'll receive a `429 Too Many Requests` response with a `Retry-After` header. When you exhaust your included calls, overages are billed per-call (Builder: $0.005, Scale: $0.003) or you can purchase credit packs. --- ## Error Handling All errors follow a consistent format: ```json { "error": { "type": "rate_limit_exceeded", "message": "You have exceeded your per-minute rate limit of 30 requests.", "retry_after": 3600 } } ``` See the [Error Reference](errors.md) for all error types and how to handle them. --- ## Code Examples ### JavaScript / Node.js ```javascript const response = await fetch('https://www.promptster.dev/v1/prompts/test', { method: 'POST', headers: { 'Authorization': 'Bearer pk_live_abc123...', 'X-ANTHROPIC-API-KEY': 'sk-ant-...', 'Content-Type': 'application/json' }, body: JSON.stringify({ provider: 'anthropic', model: 'claude-sonnet-4-20250514', prompt: 'What is the capital of France?' }) }); const data = await response.json(); console.log('Response:', data.response); console.log('Cost:', `$${data.metadata.estimated_cost}`); ``` ### Python ```python import requests response = requests.post( 'https://www.promptster.dev/v1/prompts/test', headers={ 'Authorization': 'Bearer pk_live_abc123...', 'X-ANTHROPIC-API-KEY': 'sk-ant-...', 'Content-Type': 'application/json' }, json={ 'provider': 'anthropic', 'model': 'claude-sonnet-4-20250514', 'prompt': 'What is the capital of France?' } ) data = response.json() print(f"Response: {data['response']}") print(f"Cost: ${data['metadata']['estimated_cost']}") ``` ### cURL ```bash curl -X POST https://www.promptster.dev/v1/prompts/test \ -H "Authorization: Bearer pk_live_abc123..." \ -H "X-ANTHROPIC-API-KEY: sk-ant-..." \ -H "Content-Type: application/json" \ -d '{ "provider": "anthropic", "model": "claude-sonnet-4-20250514", "prompt": "What is the capital of France?" }' ``` More examples in [Code Examples](examples.md). --- ## Base URL **Production**: `https://www.promptster.dev` **Development**: `http://localhost:5000` (if running locally) All endpoints are versioned under `/v1/`. > **⚠️ Important: Always use `www.promptster.dev`** > > Requests to `promptster.dev` (without `www`) will be 307-redirected to `www.promptster.dev`. This redirect **strips the `Authorization` header**, causing authentication failures. Always use the full `https://www.promptster.dev` base URL in your applications. --- ## Next Steps 1. **[Authentication](authentication.md)** - Learn about API key management 2. **[Test Endpoint](endpoints/test-prompt.md)** - Test a single prompt 3. **[Compare Endpoint](endpoints/compare-prompts.md)** - Compare multiple providers 4. **[Schedules](endpoints/schedules.md)** - Automate prompt testing 5. **[Examples](examples.md)** - See complete code examples 6. **[Error Reference](errors.md)** - Handle errors gracefully 7. **[MCP Integration](mcp-integration.md)** - Connect your AI coding assistant --- ## Support - **Documentation**: You're reading it! - **Issues**: [GitHub Issues](https://github.com/yourusername/promptster/issues) - **Email**: support@promptster.dev --- **API Version**: v1 **Last Updated**: February 8, 2026 --- ## Section: Authentication --- # API Authentication Promptster API uses a dual-key authentication system for security and flexibility. --- ## Authentication Overview Every API request requires **two types of keys**: 1. **Promptster API Key** - Authenticates you with Promptster (tracks usage, enforces rate limits) 2. **Provider API Key** - Authenticates with the AI provider (OpenAI, Anthropic, etc.) --- ## 1. Promptster API Keys ### Creating API Keys 1. Visit [/developer/api-keys](https://www.promptster.dev/developer/api-keys) 2. Click "Create New Key" 3. Choose a name (e.g., "Production Server") 4. Select key type: - **Test Key** (`pk_test_...`) - Free, 5 requests/minute, for development - **Live Key** (`pk_live_...`) - Based on your subscription tier **⚠️ Important**: You'll only see the full key once! Copy it immediately and store it securely. ### Using Your Promptster API Key Include your key in the `Authorization` header using the Bearer scheme: ```bash Authorization: Bearer pk_live_abc123def456... ``` > **⚠️ Important**: Always send requests to `https://www.promptster.dev` (with `www`). Requests to `promptster.dev` without `www` are 307-redirected, which strips the `Authorization` header and causes auth failures. **Example**: ```bash curl -X POST https://www.promptster.dev/v1/prompts/test \ -H "Authorization: Bearer pk_live_abc123def456..." \ -H "Content-Type: application/json" \ -d '{"provider": "openai", "model": "gpt-4", "prompt": "Hello"}' ``` ### Viewing Your API Keys In the [API Keys dashboard](https://www.promptster.dev/developer/api-keys), you can see: - Key name - Key prefix (first 12 characters) - Type (Live/Test) - Total requests made - Last used date - Status (Active/Revoked) **Note**: Full keys are never shown again after creation for security. ### Revoking API Keys If a key is compromised or no longer needed: 1. Go to [API Keys](https://www.promptster.dev/developer/api-keys) 2. Click "Revoke" on the key 3. Confirm the action **⚠️ Warning**: Revoking a key immediately stops all applications using it. You cannot undo this action. --- ## 2. Provider API Keys You need API keys from the AI providers you want to use. Get them here: | Provider | Where to Get Key | Header Name | |----------|-----------------|-------------| | **OpenAI** | [platform.openai.com/api-keys](https://platform.openai.com/api-keys) | `X-OPENAI-API-KEY` | | **Anthropic** | [console.anthropic.com](https://console.anthropic.com/) | `X-ANTHROPIC-API-KEY` | | **Google AI** | [makersuite.google.com/app/apikey](https://makersuite.google.com/app/apikey) | `X-GOOGLE-API-KEY` | | **DeepSeek** | [platform.deepseek.com/api_keys](https://platform.deepseek.com/api_keys) | `X-DEEPSEEK-API-KEY` | | **xAI** | [console.x.ai](https://console.x.ai/) | `X-XAI-API-KEY` | | **Groq** | [console.groq.com/keys](https://console.groq.com/keys) | `X-GROQ-API-KEY` | | **Mistral** | [console.mistral.ai/api-keys](https://console.mistral.ai/api-keys/) | `X-MISTRAL-API-KEY` | | **Perplexity** | [perplexity.ai/settings/api](https://www.perplexity.ai/settings/api) | `X-PERPLEXITY-API-KEY` | | **Together AI** | [api.together.xyz/settings/api-keys](https://api.together.xyz/settings/api-keys) | `X-TOGETHER-API-KEY` | ### Using Provider API Keys Include the provider's API key in a custom header based on which provider you're using: **For OpenAI**: ```bash -H "X-OPENAI-API-KEY: sk-proj-abc123..." ``` **For Anthropic**: ```bash -H "X-ANTHROPIC-API-KEY: sk-ant-abc123..." ``` **For Google AI**: ```bash -H "X-GOOGLE-API-KEY: AIza..." ``` **For xAI (Grok)**: ```bash -H "X-XAI-API-KEY: xai-abc123..." ``` **For Groq**: ```bash -H "X-GROQ-API-KEY: gsk_abc123..." ``` **For Mistral**: ```bash -H "X-MISTRAL-API-KEY: abc123..." ``` ### Complete Request Example ```bash curl -X POST https://www.promptster.dev/v1/prompts/test \ -H "Authorization: Bearer pk_live_YOUR_PROMPTSTER_KEY" \ -H "X-ANTHROPIC-API-KEY: sk-ant-YOUR_ANTHROPIC_KEY" \ -H "Content-Type: application/json" \ -d '{ "provider": "anthropic", "model": "claude-sonnet-4-20250514", "prompt": "Explain quantum computing in simple terms" }' ``` --- ## Security Best Practices ### βœ… Do - **Store keys in environment variables** ```bash export PROMPTSTER_KEY='pk_live_...' export ANTHROPIC_KEY='sk-ant-...' ``` - **Use `.env` files** (and add to `.gitignore`) ``` PROMPTSTER_KEY=pk_live_abc123... ANTHROPIC_KEY=sk-ant-def456... ``` - **Rotate keys regularly** (every 90 days recommended) - **Use separate keys** for development and production - **Revoke compromised keys immediately** ### ❌ Don't - **Hardcode keys in your code** ```javascript // ❌ BAD const apiKey = 'pk_live_abc123...'; ``` - **Commit keys to version control** (Git, GitHub, etc.) - **Share keys via email or Slack** - **Use the same key for multiple environments** - **Expose keys in client-side JavaScript** (use a backend proxy instead) --- ## Authentication Errors ### Invalid Promptster API Key **Status**: 401 Unauthorized ```json { "error": { "type": "authentication_error", "message": "Invalid API key. Please check your Authorization header." } } ``` **Solutions**: - Check your key is correct (copy-paste carefully) - Verify the key hasn't been revoked - Ensure you're using the correct key type (test vs live) ### Missing Provider API Key **Status**: 400 Bad Request ```json { "error": { "type": "validation_error", "message": "Missing required header: X-ANTHROPIC-API-KEY", "field": "headers" } } ``` **Solutions**: - Include the provider-specific header - Check header name matches the provider (e.g., `X-OPENAI-API-KEY` for OpenAI) - Verify the key format is correct ### Invalid Provider API Key **Status**: 503 Service Unavailable ```json { "error": { "type": "provider_error", "message": "OpenAI API authentication failed. Please check your X-OPENAI-API-KEY.", "provider": "openai" } } ``` **Solutions**: - Verify your provider API key is valid - Check you have credits/quota remaining with the provider - Ensure the key has the necessary permissions --- ## API Key Format ### Promptster Keys - **Test**: `pk_test_` + 24 random characters - **Live**: `pk_live_` + 24 random characters Example: `pk_live_7f3k9m2n8p4q1r6s5t0u9v8w` ### Provider Keys Each provider has their own format: - **OpenAI**: `sk-proj-...` (64+ characters) - **Anthropic**: `sk-ant-...` (variable length) - **Google**: `AIza...` (39 characters) - **DeepSeek**: `sk-...` (variable length) - **xAI**: `xai-...` (variable length) - **Groq**: `gsk_...` (variable length) - **Mistral**: variable format (variable length) - **Perplexity**: `pplx-...` (variable length) - **Together AI**: variable format (variable length) --- ## Rate Limiting by Key Type ### Test Keys (`pk_test_...`) - **Included calls**: Unlimited (development only) - **Requests/minute**: 5 - **Purpose**: Development and testing - **Cost**: Free ### Live Keys (`pk_live_...`) Limits based on your subscription tier: | Tier | Included Calls/Month | Rate Limit (per minute) | Price | |------|---------------------|------------------------|-------| | Free | 500 | 5 | $0 | | Builder | 5,000 | 30 | $19/mo | | Scale | 25,000 | 120 | $49/mo | --- ## Managing API Keys Programmatically Currently, API key management is only available through the web interface at [/developer/api-keys](https://www.promptster.dev/developer/api-keys). **Coming soon**: API endpoints for creating and revoking keys programmatically. --- ## FAQs ### Can I use the same Promptster key for multiple applications? Yes, but we recommend using separate keys for: - Different environments (dev, staging, production) - Different applications - Different team members This makes it easier to track usage and revoke keys if needed. ### What happens if I lose my API key? You cannot recover a lost API key. You'll need to: 1. Create a new key 2. Update your applications with the new key 3. Revoke the old key (optional, if you think it might be compromised) ### Do provider API keys count against my Promptster quota? No. Your Promptster API key tracks usage with Promptster's API. Provider costs are separate and billed directly by the provider. ### Can I see which requests used which API key? Yes! The [/v1/history](endpoints/history.md) endpoint shows all your API requests, including which API key was used (by key prefix). --- ## Next Steps - [Test Endpoint](endpoints/test-prompt.md) - Make your first API call - [Compare Endpoint](endpoints/compare-prompts.md) - Compare multiple providers - [Error Reference](errors.md) - Handle authentication errors --- **Need help?** Contact support@promptster.dev --- ## Section: Test Prompt --- # POST /v1/prompts/test Test a single prompt with one AI provider and get detailed performance metrics including cost estimates. --- ## Endpoint ``` POST /v1/prompts/test ``` --- ## Authentication Requires two headers: ```bash Authorization: Bearer YOUR_PROMPTSTER_KEY X-{PROVIDER}-API-KEY: YOUR_PROVIDER_KEY ``` See [Authentication](../authentication.md) for details. --- ## Request ### Headers ```http Authorization: Bearer pk_live_abc123... X-ANTHROPIC-API-KEY: sk-ant-def456... Content-Type: application/json ``` ### Body Parameters | Parameter | Type | Required | Default | Description | |-----------|------|----------|---------|-------------| | `provider` | string | βœ… Yes | - | AI provider: `"openai"`, `"anthropic"`, `"google"`, `"deepseek"`, `"xai"`, `"groq"`, `"mistral"`, `"perplexity"`, `"together"`, `"cerebras"`, or `"fireworks"` | | `model` | string | βœ… Yes | - | Model name (see [Supported Models](#supported-models)) | | `prompt` | string | βœ… Yes | - | Your prompt text (1-10,000 characters) | | `max_tokens` | integer | ❌ No | 2000 | Maximum tokens in response (1-4096) | | `temperature` | number | ❌ No | 0.7 | Randomness (0.0-2.0). Lower = more focused, Higher = more creative | ### Request Example ```json { "provider": "anthropic", "model": "claude-sonnet-4-20250514", "prompt": "Explain quantum computing in simple terms", "max_tokens": 500, "temperature": 0.7 } ``` --- ## Response ### Success (200 OK) ```json { "id": "test_abc123def456", "provider": "anthropic", "model": "claude-sonnet-4-20250514", "prompt": "Explain quantum computing in simple terms", "response": "Quantum computing is a revolutionary approach to computation that harnesses the principles of quantum mechanics...", "metadata": { "input_tokens": 13, "output_tokens": 87, "total_tokens": 100, "response_time_ms": 2152, "tokens_per_second": 40.4, "estimated_cost": 0.000195 }, "created_at": "2026-01-25T12:34:56.789Z" } ``` ### Response Fields | Field | Type | Description | |-------|------|-------------| | `id` | string | Unique identifier for this test | | `provider` | string | Provider used | | `model` | string | Model used | | `prompt` | string | Your original prompt | | `response` | string | AI-generated response | | `metadata` | object | Performance and cost metrics | | `metadata.input_tokens` | integer | Tokens in your prompt | | `metadata.output_tokens` | integer | Tokens in the response | | `metadata.total_tokens` | integer | Sum of input + output tokens | | `metadata.response_time_ms` | integer | Time taken in milliseconds | | `metadata.tokens_per_second` | number | Generation speed | | `metadata.estimated_cost` | number | Estimated cost in USD | | `created_at` | string | ISO 8601 timestamp | --- ## Supported Models ### OpenAI ```javascript "gpt-4" // Best quality, most expensive "gpt-4o" // Optimized GPT-4 "gpt-3.5-turbo" // Fast and affordable ``` ### Anthropic ```javascript "claude-opus-4-20250514" // Most capable "claude-sonnet-4-20250514" // Balanced performance "claude-haiku-4-20250514" // Fast and efficient ``` ### Google AI ```javascript "gemini-2.0-flash-exp" // Latest experimental "gemini-1.5-pro" // Best quality "gemini-1.5-flash" // Fast and efficient ``` ### DeepSeek ```javascript "deepseek-chat" // DeepSeek Chat (V3) ``` ### xAI (Grok) ```javascript "grok-3" // Most capable "grok-3-fast" // Faster variant "grok-3-mini" // Compact reasoning "grok-3-mini-fast" // Fastest compact ``` ### Groq ```javascript "llama-3.3-70b-versatile" // Llama 3.3 70B "llama-3.1-8b-instant" // Llama 3.1 8B (fastest) ``` ### Mistral ```javascript "mistral-large-latest" // Most capable "mistral-small-latest" // Balanced performance "open-mistral-nemo" // Open-weight, efficient ``` ### Cerebras ```javascript "llama-3.3-70b" // Llama 3.3 70B (fastest inference) "llama-3.1-8b" // Llama 3.1 8B "llama-4-scout-17b-16e" // Llama 4 Scout ``` ### Fireworks AI ```javascript "accounts/fireworks/models/llama-v3p3-70b-instruct" // Llama 3.3 70B "accounts/fireworks/models/llama4-scout-instruct-basic" // Llama 4 Scout "accounts/fireworks/models/deepseek-v3" // DeepSeek V3 "accounts/fireworks/models/qwen3-235b-a22b" // Qwen 3 235B ``` --- ## Code Examples ### JavaScript / Node.js ```javascript async function testPrompt() { const response = await fetch('https://www.promptster.dev/v1/prompts/test', { method: 'POST', headers: { 'Authorization': 'Bearer pk_live_abc123...', 'X-ANTHROPIC-API-KEY': 'sk-ant-def456...', 'Content-Type': 'application/json' }, body: JSON.stringify({ provider: 'anthropic', model: 'claude-sonnet-4-20250514', prompt: 'What is 2+2?', max_tokens: 100, temperature: 0.5 }) }); const data = await response.json(); console.log('Response:', data.response); console.log('Cost:', `$${data.metadata.estimated_cost}`); console.log('Time:', `${data.metadata.response_time_ms}ms`); console.log('Speed:', `${data.metadata.tokens_per_second.toFixed(1)} tokens/sec`); return data; } testPrompt(); ``` ### Python ```python import requests import json def test_prompt(): response = requests.post( 'https://www.promptster.dev/v1/prompts/test', headers={ 'Authorization': 'Bearer pk_live_abc123...', 'X-ANTHROPIC-API-KEY': 'sk-ant-def456...', 'Content-Type': 'application/json' }, json={ 'provider': 'anthropic', 'model': 'claude-sonnet-4-20250514', 'prompt': 'What is 2+2?', 'max_tokens': 100, 'temperature': 0.5 } ) data = response.json() print(f"Response: {data['response']}") print(f"Cost: ${data['metadata']['estimated_cost']}") print(f"Time: {data['metadata']['response_time_ms']}ms") print(f"Speed: {data['metadata']['tokens_per_second']:.1f} tokens/sec") return data test_prompt() ``` ### cURL ```bash curl -X POST https://www.promptster.dev/v1/prompts/test \ -H "Authorization: Bearer pk_live_abc123..." \ -H "X-ANTHROPIC-API-KEY: sk-ant-def456..." \ -H "Content-Type: application/json" \ -d '{ "provider": "anthropic", "model": "claude-sonnet-4-20250514", "prompt": "What is 2+2?", "max_tokens": 100, "temperature": 0.5 }' ``` --- ## Cost Estimation Costs are calculated based on official provider pricing and token usage: ### Example: OpenAI GPT-4 **Pricing** (as of Jan 2026): - Input: $0.03 per 1,000 tokens - Output: $0.06 per 1,000 tokens **Your request**: - Input: 8 tokens - Output: 120 tokens **Cost calculation**: - Input: 8 Γ— $0.03 / 1000 = $0.00024 - Output: 120 Γ— $0.06 / 1000 = $0.0072 - **Total: $0.00744** The API returns this as `"estimated_cost": 0.0074` (rounded to 4 decimals). ### Cost Comparison | Provider | Model | Avg Cost (100 tokens) | |----------|-------|----------------------| | OpenAI | GPT-4 | $0.0090 | | OpenAI | GPT-3.5-turbo | $0.0002 | | Anthropic | Claude Sonnet 4 | $0.0006 | | Google | Gemini 1.5 Pro | $0.0003 | | DeepSeek | Chat (V3) | $0.0002 | **Tip**: Use the [compare endpoint](compare-prompts.md) to see real cost differences for your prompt! --- ## Error Responses ### 400 Bad Request - Invalid Parameters ```json { "error": { "type": "validation_error", "message": "Invalid provider. Must be one of: openai, anthropic, google, deepseek, xai, groq, mistral, perplexity, together, cerebras, fireworks", "field": "provider" } } ``` **Common causes**: - Invalid provider name (check spelling) - Invalid model name for the provider - Prompt too long (>10,000 characters) - `max_tokens` out of range (1-4096) - `temperature` out of range (0.0-2.0) ### 401 Unauthorized - Invalid API Key ```json { "error": { "type": "authentication_error", "message": "Invalid API key. Please check your Authorization header." } } ``` **Solutions**: - Check your Promptster API key is correct - Verify the key hasn't been revoked - Ensure you're using the correct format: `Bearer pk_live_...` ### 429 Too Many Requests - Rate Limited ```json { "error": { "type": "rate_limit_exceeded", "message": "You have exceeded your daily request quota of 100 requests.", "retry_after": 3600 } } ``` **Solutions**: - Wait for the time specified in `retry_after` (seconds) - Upgrade your plan for higher limits - Use test keys for development (2/minute, unlimited daily) ### 503 Service Unavailable - Provider Error ```json { "error": { "type": "provider_error", "message": "Anthropic API is currently unavailable. Please try again later.", "provider": "anthropic" } } ``` **Common causes**: - Invalid provider API key (check `X-{PROVIDER}-API-KEY` header) - Provider service is down (check provider status page) - No credits/quota remaining with the provider - Provider API rate limit exceeded --- ## Performance Tips ### 1. Optimize Token Usage Shorter prompts = lower costs: ```javascript // ❌ Verbose (15 tokens) "I would like you to please explain to me what quantum computing is" // βœ… Concise (5 tokens) "Explain quantum computing" ``` ### 2. Choose the Right Model Not every task needs GPT-4: ```javascript // Simple tasks β†’ Use cheaper models "What is 2+2?" β†’ gpt-3.5-turbo ($0.0002 vs $0.0090) // Complex tasks β†’ Use premium models "Write a technical architecture document" β†’ gpt-4 ``` ### 3. Control Response Length Set `max_tokens` to avoid unnecessary costs: ```javascript { "prompt": "Say hello", "max_tokens": 20 // Prevent 2000-token response for 2-word answer } ``` ### 4. Adjust Temperature Lower temperature = more focused = fewer tokens: ```javascript { "temperature": 0.3 // More deterministic, often shorter responses } ``` --- ## Rate Limit Headers Every response includes rate limit information: ```http HTTP/1.1 200 OK X-RateLimit-Remaining-Calls: 4953 X-Credit-Balance: 0 X-Billing-Source: plan_included X-RateLimit-Tier: builder ``` Use these to track your usage programmatically: ```javascript const response = await fetch('/v1/prompts/test', { ... }); const remaining = response.headers.get('X-RateLimit-Remaining'); if (remaining < 10) { console.warn('⚠️ Only', remaining, 'requests remaining today!'); } ``` --- ## Next Steps - **[Compare Prompts](compare-prompts.md)** - Test multiple providers at once - **[View Usage](usage.md)** - Check your quota and costs - **[Request History](history.md)** - Analyze your past requests - **[Error Reference](../errors.md)** - Handle all error types --- **Questions?** See the [full API reference](../index.md) or email support@promptster.dev --- ## Section: Compare Prompts --- # POST /v1/prompts/compare Compare the same prompt across multiple AI providers in parallel. Get side-by-side results with cost comparisons to find the best model for your use case. --- ## Endpoint ``` POST /v1/prompts/compare ``` --- ## Authentication Requires Promptster API key + all provider API keys you want to compare: ```bash Authorization: Bearer YOUR_PROMPTSTER_KEY X-OPENAI-API-KEY: YOUR_OPENAI_KEY X-ANTHROPIC-API-KEY: YOUR_ANTHROPIC_KEY # Include other provider keys as needed ``` --- ## Request ### Headers ```http Authorization: Bearer pk_live_abc123... X-OPENAI-API-KEY: sk-proj-def456... X-ANTHROPIC-API-KEY: sk-ant-ghi789... Content-Type: application/json ``` ### Body Parameters | Parameter | Type | Required | Default | Description | |-----------|------|----------|---------|-------------| | `prompt` | string | βœ… Yes | - | The prompt to test across all providers | | `configurations` | array | βœ… Yes | - | Array of provider/model configurations (2-5 items) | | `max_tokens` | integer | ❌ No | 2000 | Maximum tokens for all responses | ### Configuration Object Each item in `configurations` array: | Field | Type | Required | Default | Description | |-------|------|----------|---------|-------------| | `provider` | string | βœ… Yes | - | `"openai"`, `"anthropic"`, `"google"`, `"deepseek"`, `"xai"`, `"groq"`, `"mistral"`, `"perplexity"`, `"together"`, `"cerebras"`, or `"fireworks"` | | `model` | string | βœ… Yes | - | Model name for this provider | | `temperature` | number | ❌ No | 0.7 | Randomness (0.0-2.0) | ### Request Example ```json { "prompt": "Explain quantum computing in 50 words", "configurations": [ { "provider": "openai", "model": "gpt-4", "temperature": 0.7 }, { "provider": "anthropic", "model": "claude-sonnet-4-20250514", "temperature": 0.7 }, { "provider": "google", "model": "gemini-1.5-pro", "temperature": 0.7 } ], "max_tokens": 100 } ``` --- ## Response ### Success (200 OK) ```json { "id": "compare_abc123def456", "prompt": "Explain quantum computing in 50 words", "results": [ { "provider": "openai", "model": "gpt-4", "response": "Quantum computing harnesses quantum mechanics principles like superposition and entanglement to process information. Unlike classical bits, quantum bits (qubits) can exist in multiple states simultaneously, enabling parallel computation. This allows quantum computers to solve certain complex problems exponentially faster than traditional computers, particularly in cryptography, drug discovery, and optimization.", "metadata": { "input_tokens": 12, "output_tokens": 58, "total_tokens": 70, "response_time_ms": 2340, "tokens_per_second": 24.8, "estimated_cost": 0.0052 } }, { "provider": "anthropic", "model": "claude-sonnet-4-20250514", "response": "Quantum computing uses quantum mechanics principlesβ€”superposition and entanglementβ€”to process information differently than classical computers. Qubits can represent multiple states simultaneously, allowing quantum computers to perform certain calculations exponentially faster. Applications include cryptography, drug discovery, and solving complex optimization problems that classical computers struggle with.", "metadata": { "input_tokens": 12, "output_tokens": 52, "total_tokens": 64, "response_time_ms": 1890, "tokens_per_second": 27.5, "estimated_cost": 0.000156 } }, { "provider": "google", "model": "gemini-1.5-pro", "response": "Quantum computing leverages quantum phenomena like superposition and entanglement. Qubits, unlike classical bits, can be in multiple states at once, enabling parallel processing of vast datasets. This promises breakthroughs in drug discovery, materials science, and cryptography, solving problems currently impossible for classical computers.", "metadata": { "input_tokens": 12, "output_tokens": 51, "total_tokens": 63, "response_time_ms": 2100, "tokens_per_second": 24.3, "estimated_cost": 0.000095 } } ], "created_at": "2026-01-25T14:22:18.543Z" } ``` ### Response Fields | Field | Type | Description | |-------|------|-------------| | `id` | string | Unique identifier for this comparison | | `prompt` | string | Your original prompt | | `results` | array | Array of results, one per configuration | | `results[].provider` | string | Provider used | | `results[].model` | string | Model used | | `results[].response` | string | AI-generated response | | `results[].metadata` | object | Performance and cost metrics | | `created_at` | string | ISO 8601 timestamp | --- ## Code Examples ### JavaScript / Node.js ```javascript async function comparePrompt() { const response = await fetch('https://www.promptster.dev/v1/prompts/compare', { method: 'POST', headers: { 'Authorization': 'Bearer pk_live_abc123...', 'X-OPENAI-API-KEY': 'sk-proj-def456...', 'X-ANTHROPIC-API-KEY': 'sk-ant-ghi789...', 'Content-Type': 'application/json' }, body: JSON.stringify({ prompt: 'What is 2+2? Answer in one sentence.', configurations: [ { provider: 'openai', model: 'gpt-4' }, { provider: 'openai', model: 'gpt-3.5-turbo' }, { provider: 'anthropic', model: 'claude-sonnet-4-20250514' } ], max_tokens: 50 }) }); const data = await response.json(); console.log('Comparison Results:\n'); data.results.forEach(result => { console.log(`${result.provider}/${result.model}:`); console.log(` Response: ${result.response}`); console.log(` Cost: $${result.metadata.estimated_cost}`); console.log(` Time: ${result.metadata.response_time_ms}ms`); console.log(` Speed: ${result.metadata.tokens_per_second.toFixed(1)} tok/s\n`); }); // Find cheapest option const cheapest = data.results.reduce((min, r) => r.metadata.estimated_cost < min.metadata.estimated_cost ? r : min ); console.log(`πŸ’° Cheapest: ${cheapest.provider}/${cheapest.model} ($${cheapest.metadata.estimated_cost})`); // Find fastest option const fastest = data.results.reduce((min, r) => r.metadata.response_time_ms < min.metadata.response_time_ms ? r : min ); console.log(`⚑ Fastest: ${fastest.provider}/${fastest.model} (${fastest.metadata.response_time_ms}ms)`); return data; } comparePrompt(); ``` ### Python ```python import requests def compare_prompt(): response = requests.post( 'https://www.promptster.dev/v1/prompts/compare', headers={ 'Authorization': 'Bearer pk_live_abc123...', 'X-OPENAI-API-KEY': 'sk-proj-def456...', 'X-ANTHROPIC-API-KEY': 'sk-ant-ghi789...', 'Content-Type': 'application/json' }, json={ 'prompt': 'What is 2+2? Answer in one sentence.', 'configurations': [ {'provider': 'openai', 'model': 'gpt-4'}, {'provider': 'openai', 'model': 'gpt-3.5-turbo'}, {'provider': 'anthropic', 'model': 'claude-sonnet-4-20250514'} ], 'max_tokens': 50 } ) data = response.json() print('Comparison Results:\n') for result in data['results']: print(f"{result['provider']}/{result['model']}:") print(f" Response: {result['response']}") print(f" Cost: ${result['metadata']['estimated_cost']}") print(f" Time: {result['metadata']['response_time_ms']}ms") print(f" Speed: {result['metadata']['tokens_per_second']:.1f} tok/s\n") # Find cheapest option cheapest = min(data['results'], key=lambda r: r['metadata']['estimated_cost']) print(f"πŸ’° Cheapest: {cheapest['provider']}/{cheapest['model']} (${cheapest['metadata']['estimated_cost']})") # Find fastest option fastest = min(data['results'], key=lambda r: r['metadata']['response_time_ms']) print(f"⚑ Fastest: {fastest['provider']}/{fastest['model']} ({fastest['metadata']['response_time_ms']}ms)") return data compare_prompt() ``` ### cURL ```bash curl -X POST https://www.promptster.dev/v1/prompts/compare \ -H "Authorization: Bearer pk_live_abc123..." \ -H "X-OPENAI-API-KEY: sk-proj-def456..." \ -H "X-ANTHROPIC-API-KEY: sk-ant-ghi789..." \ -H "Content-Type: application/json" \ -d '{ "prompt": "What is 2+2? Answer in one sentence.", "configurations": [ {"provider": "openai", "model": "gpt-4"}, {"provider": "openai", "model": "gpt-3.5-turbo"}, {"provider": "anthropic", "model": "claude-sonnet-4-20250514"} ], "max_tokens": 50 }' ``` --- ## Use Cases ### 1. Cost Optimization Find the cheapest model that meets your quality requirements: ```javascript { "prompt": "Summarize this article in 3 bullet points: [article text]", "configurations": [ {"provider": "openai", "model": "gpt-4"}, // Premium {"provider": "openai", "model": "gpt-3.5-turbo"}, // Budget {"provider": "anthropic", "model": "claude-sonnet-4-20250514"}, // Mid-range {"provider": "google", "model": "gemini-1.5-flash"} // Budget ] } ``` Compare costs and choose the cheapest model with acceptable quality. ### 2. Quality Comparison Test which model gives the best answers for your domain: ```javascript { "prompt": "Explain this code: def fib(n): return n if n<2 else fib(n-1)+fib(n-2)", "configurations": [ {"provider": "openai", "model": "gpt-4"}, {"provider": "anthropic", "model": "claude-opus-4-20250514"}, {"provider": "google", "model": "gemini-1.5-pro"} ] } ``` Read all responses and pick the model that explains it best. ### 3. Speed Testing Find the fastest model for real-time applications: ```javascript { "prompt": "Generate a product title for wireless headphones", "configurations": [ {"provider": "openai", "model": "gpt-3.5-turbo"}, {"provider": "anthropic", "model": "claude-haiku-4-20250514"}, {"provider": "google", "model": "gemini-1.5-flash"} ] } ``` Check `response_time_ms` and `tokens_per_second` to find the fastest. ### 4. A/B Testing Test temperature settings or model versions: ```javascript { "prompt": "Write a creative story opening", "configurations": [ {"provider": "openai", "model": "gpt-4", "temperature": 0.3}, // Focused {"provider": "openai", "model": "gpt-4", "temperature": 0.9}, // Creative {"provider": "openai", "model": "gpt-4", "temperature": 1.5} // Very creative ] } ``` --- ## Best Practices ### 1. Include Provider API Keys You must include API keys for all providers you want to test: ```javascript // βœ… Good - Including both keys headers: { 'X-OPENAI-API-KEY': '...', 'X-ANTHROPIC-API-KEY': '...' } // ❌ Bad - Missing Anthropic key headers: { 'X-OPENAI-API-KEY': '...' } // Will fail with 400 error for Anthropic ``` ### 2. Use Same Temperature For fair comparisons, use the same temperature across all configurations: ```javascript // βœ… Good - Same temperature configurations: [ {provider: 'openai', model: 'gpt-4', temperature: 0.7}, {provider: 'anthropic', model: 'claude-sonnet-4-20250514', temperature: 0.7} ] // ⚠️ Less comparable - Different temperatures configurations: [ {provider: 'openai', model: 'gpt-4', temperature: 0.3}, {provider: 'anthropic', model: 'claude-sonnet-4-20250514', temperature: 0.9} ] ``` ### 3. Limit Configurations Maximum 5 configurations per request (API limit): ```javascript // βœ… Good - 3 configurations configurations: [/* 3 items */] // ⚠️ Will be rejected - 6 configurations configurations: [/* 6 items */] // Error: max 5 ``` ### 4. Set Appropriate max_tokens Different models may have different token limits: ```javascript { "max_tokens": 500 // Safe for all models } ``` --- ## Cost Analysis The comparison endpoint shows cost differences clearly: ### Example Results ``` GPT-4: $0.0096 (premium quality, high cost) GPT-3.5 Turbo: $0.0002 (good quality, very low cost) ⭐ Claude Sonnet 4: $0.0006 (excellent quality, mid cost) Gemini 1.5 Pro: $0.0003 (good quality, low cost) ``` **Insight**: GPT-3.5 Turbo is 48Γ— cheaper than GPT-4! ### Cost vs Quality Trade-off ``` Expensive + Best Quality: - GPT-4 - Claude Opus 4 Mid-range: - Claude Sonnet 4 (best value) - Gemini 1.5 Pro Cheap + Fast: - GPT-3.5 Turbo - Claude Haiku 4 - Gemini 1.5 Flash ``` --- ## Error Responses ### 400 Bad Request - Invalid Configuration ```json { "error": { "type": "validation_error", "message": "configurations must contain 2-5 items", "field": "configurations" } } ``` **Solutions**: - Include at least 2 configurations - Don't exceed 5 configurations - Ensure each configuration has required fields ### 400 Bad Request - Missing Provider Key ```json { "error": { "type": "validation_error", "message": "Missing required header: X-ANTHROPIC-API-KEY", "field": "headers" } } ``` **Solutions**: - Include API keys for all providers in your configurations - Check header names match the provider ### 503 Service Unavailable - Partial Failure If one provider fails but others succeed, you'll get a 200 OK with error details in results: ```json { "results": [ { "provider": "openai", "response": "Success!", "metadata": { /* ... */ } }, { "provider": "anthropic", "error": { "type": "provider_error", "message": "Invalid API key" } } ] } ``` --- ## Performance Notes ### Parallel Execution All providers are called **in parallel** for maximum speed: ``` Traditional (sequential): 2s + 3s + 2.5s = 7.5s total Promptster (parallel): max(2s, 3s, 2.5s) = 3s total ``` This means comparisons are nearly as fast as testing a single provider! ### Timeout Handling Each provider has a 30-second timeout. If a provider times out: - Other providers continue normally - The result will show an error for that provider - You still get results from successful providers --- ## Next Steps - **[Test Endpoint](test-prompt.md)** - Test a single provider - **[Usage Statistics](usage.md)** - Track your costs - **[Request History](history.md)** - Review past comparisons - **[Cost Examples](../examples.md#cost-optimization)** - More cost optimization tips --- **Need help?** Contact support@promptster.dev --- ## Section: Usage Statistics --- # GET /v1/usage Get your current API usage statistics, remaining quota, and cost breakdown. --- ## Endpoint ``` GET /v1/usage ``` --- ## Authentication Requires only your Promptster API key: ```bash Authorization: Bearer YOUR_PROMPTSTER_KEY ``` --- ## Request ### Headers ```http Authorization: Bearer pk_live_abc123... ``` ### Query Parameters None required - your usage is determined by your API key. ### Request Example ```bash curl -X GET https://www.promptster.dev/v1/usage \ -H "Authorization: Bearer pk_live_abc123..." ``` --- ## Response ### Success (200 OK) ```json { "user_id": "user_abc123def456", "tier": "builder", "limits": { "included_calls_per_month": 5000, "requests_per_minute": 30 }, "usage": { "today": { "requests": 47, "remaining": 953, "reset_at": "2026-01-26T00:00:00Z" }, "this_month": { "requests": 423, "estimated_cost": 0 } }, "billing_period": { "included_calls": 5000, "used_calls": 423, "overage_calls": 0, "period_end": "2026-02-25T00:00:00Z" }, "credits": { "balance": 0, "total_purchased": 0 }, "breakdown": { "by_provider": { "/v1/prompts/test": 234, "/v1/prompts/compare": 143, "/v1/history": 46 } } } ``` ### Response Fields | Field | Type | Description | |-------|------|-------------| | `user_id` | string | Your Promptster user ID | | `tier` | string | Your subscription tier: `"free"`, `"builder"`, `"scale"`, or `"enterprise"` | | `limits` | object | Your rate limits | | `limits.included_calls_per_month` | integer | Included API calls per billing period | | `limits.requests_per_minute` | integer | Max requests per minute | | `usage` | object | Your current usage | | `usage.today` | object | Today's usage stats | | `usage.today.requests` | integer | Requests made today | | `usage.today.remaining` | integer | Requests remaining today | | `usage.today.reset_at` | string | When daily quota resets (ISO 8601) | | `usage.this_month` | object | This month's totals | | `usage.this_month.requests` | integer | Total requests this month | | `usage.this_month.estimated_cost` | number | Estimated cost this month (USD) | | `billing_period` | object | Current billing period stats | | `billing_period.included_calls` | integer | Calls included in your plan | | `billing_period.used_calls` | integer | Calls used this period | | `billing_period.overage_calls` | integer | Overage calls this period | | `billing_period.period_end` | string | When current period ends (ISO 8601) | | `credits` | object | Prepaid credit balance | | `credits.balance` | integer | Remaining credit balance | | `credits.total_purchased` | integer | Total credits ever purchased | | `breakdown` | object | Usage breakdown by endpoint | | `breakdown.by_provider` | object | Count per endpoint | --- ## Code Examples ### JavaScript / Node.js ```javascript async function checkUsage() { const response = await fetch('https://www.promptster.dev/v1/usage', { headers: { 'Authorization': 'Bearer pk_live_abc123...' } }); const data = await response.json(); console.log(`πŸ“Š Usage Statistics`); console.log(`Tier: ${data.tier}`); console.log(`\nToday: ${data.usage.today.requests}/${data.limits.requests_per_day} requests`); console.log(`Remaining: ${data.usage.today.remaining}`); console.log(`Resets: ${new Date(data.usage.today.reset_at).toLocaleString()}`); console.log(`\nThis month: ${data.usage.this_month.requests} requests`); console.log(`Estimated cost: $${data.usage.this_month.estimated_cost}`); // Warn if running low const percentUsed = (data.usage.today.requests / data.limits.requests_per_day) * 100; if (percentUsed > 80) { console.warn(`⚠️ Warning: ${percentUsed.toFixed(1)}% of daily quota used!`); } return data; } checkUsage(); ``` ### Python ```python import requests from datetime import datetime def check_usage(): response = requests.get( 'https://www.promptster.dev/v1/usage', headers={ 'Authorization': 'Bearer pk_live_abc123...' } ) data = response.json() print('πŸ“Š Usage Statistics') print(f"Tier: {data['tier']}") print(f"\nToday: {data['usage']['today']['requests']}/{data['limits']['requests_per_day']} requests") print(f"Remaining: {data['usage']['today']['remaining']}") reset_time = datetime.fromisoformat(data['usage']['today']['reset_at'].replace('Z', '+00:00')) print(f"Resets: {reset_time.strftime('%Y-%m-%d %H:%M:%S')}") print(f"\nThis month: {data['usage']['this_month']['requests']} requests") print(f"Estimated cost: ${data['usage']['this_month']['estimated_cost']}") # Warn if running low percent_used = (data['usage']['today']['requests'] / data['limits']['requests_per_day']) * 100 if percent_used > 80: print(f"⚠️ Warning: {percent_used:.1f}% of daily quota used!") return data check_usage() ``` ### cURL ```bash curl -X GET https://www.promptster.dev/v1/usage \ -H "Authorization: Bearer pk_live_abc123..." ``` --- ## Usage Monitoring ### Check Before Making Requests ```javascript async function makeRequestSafely(promptData) { // Check usage first const usage = await fetch('https://www.promptster.dev/v1/usage', { headers: { 'Authorization': 'Bearer pk_live_abc123...' } }).then(r => r.json()); if (usage.usage.today.remaining === 0) { throw new Error('Daily quota exceeded! Resets at ' + usage.usage.today.reset_at); } if (usage.usage.today.remaining < 10) { console.warn(`⚠️ Only ${usage.usage.today.remaining} requests remaining today`); } // Make the request return fetch('https://www.promptster.dev/v1/prompts/test', { method: 'POST', headers: { 'Authorization': 'Bearer pk_live_abc123...', 'Content-Type': 'application/json' }, body: JSON.stringify(promptData) }); } ``` ### Track Costs Over Time ```javascript async function trackMonthlyCost() { const response = await fetch('https://www.promptster.dev/v1/usage', { headers: { 'Authorization': 'Bearer pk_live_abc123...' } }); const data = await response.json(); const monthlyCost = data.usage.this_month.estimated_cost; // Save to database or log console.log(`${new Date().toISOString()}: $${monthlyCost}`); // Alert if exceeding budget const MONTHLY_BUDGET = 10.00; if (monthlyCost > MONTHLY_BUDGET) { alert(`⚠️ Monthly cost ($${monthlyCost}) exceeds budget ($${MONTHLY_BUDGET})`); } } ``` ### Usage Dashboard ```javascript async function displayUsageDashboard() { const response = await fetch('https://www.promptster.dev/v1/usage', { headers: { 'Authorization': 'Bearer pk_live_abc123...' } }); const data = await response.json(); // Calculate percentages const dailyPercent = (data.usage.today.requests / data.limits.requests_per_day) * 100; // Display in UI document.getElementById('tier').textContent = data.tier.toUpperCase(); document.getElementById('daily-usage').textContent = `${data.usage.today.requests} / ${data.limits.requests_per_day}`; document.getElementById('daily-bar').style.width = `${dailyPercent}%`; document.getElementById('monthly-requests').textContent = data.usage.this_month.requests; document.getElementById('monthly-cost').textContent = `$${data.usage.this_month.estimated_cost.toFixed(2)}`; // Update breakdown chart const breakdown = data.breakdown.by_provider; createBarChart(breakdown); } ``` --- ## Subscription Tiers Your `tier` determines your rate limits: ### Free Tier ```json { "tier": "free", "limits": { "included_calls_per_month": 500, "requests_per_minute": 5 } } ``` **Best for**: Testing, personal projects, low-volume use ### Builder Tier ($19/month) ```json { "tier": "builder", "limits": { "included_calls_per_month": 5000, "requests_per_minute": 30 } } ``` **Best for**: Side projects, small applications, active development. Overages at $0.005/call. ### Scale Tier ($49/month) ```json { "tier": "scale", "limits": { "included_calls_per_month": 25000, "requests_per_minute": 120 } } ``` **Best for**: Production applications, businesses, high-volume use. Overages at $0.003/call. ### Enterprise Tier (Custom) ```json { "tier": "enterprise", "limits": { "included_calls_per_month": 100000, "requests_per_minute": 500 } } ``` **Best for**: Large teams, custom SLAs, dedicated support. Contact sales@promptster.dev. --- ## Understanding Rate Limits ### Daily Limit Resets at **00:00 UTC** every day: ``` Day 1: 100 requests ──┐ β”‚ Reset at midnight UTC Day 2: 100 requests β†β”€β”˜ (fresh quota) ``` Check `usage.today.reset_at` to see exactly when your quota resets. ### Per-Minute Limit Rolling window - not based on clock minute: ``` 12:00:00 - Request 1 βœ… 12:00:05 - Request 2 βœ… 12:00:10 - Request 3 ❌ (exceeded 2/min for free tier) 12:01:01 - Request 4 βœ… (first request now outside 60-second window) ``` --- ## Cost Tracking ### How Costs Are Calculated ```javascript { "estimated_cost": 0 // Currently always 0 } ``` **Note**: Cost tracking is currently disabled. This field will show actual costs in a future update based on: - Provider API costs (OpenAI, Anthropic, etc.) - Token usage per request - Model pricing tiers ### Future Cost Calculation When enabled, costs will be calculated as: ``` Request cost = (input_tokens Γ— input_rate) + (output_tokens Γ— output_rate) Example (GPT-4): Input: 100 tokens Γ— $0.03/1000 = $0.003 Output: 200 tokens Γ— $0.06/1000 = $0.012 Total: $0.015 ``` --- ## Error Responses ### 401 Unauthorized - Invalid API Key ```json { "error": { "type": "authentication_error", "message": "Invalid API key. Please check your Authorization header." } } ``` **Solutions**: - Verify your API key is correct - Check the key hasn't been revoked - Ensure proper format: `Bearer pk_live_...` --- ## Rate Limit Headers Usage endpoint also includes rate limit headers in the response: ```http HTTP/1.1 200 OK X-RateLimit-Remaining-Calls: 4577 X-Credit-Balance: 0 X-Billing-Source: plan_included X-RateLimit-Tier: builder ``` --- ## Best Practices ### 1. Check Before High-Volume Operations ```javascript async function bulkProcess(items) { const usage = await getUsage(); if (usage.usage.today.remaining < items.length) { throw new Error( `Not enough quota! Need ${items.length}, have ${usage.usage.today.remaining}` ); } // Proceed with bulk operation for (const item of items) { await processItem(item); } } ``` ### 2. Display Usage in Your App Show users their quota in real-time: ``` Daily Usage: 47 / 1,000 (4.7%) [β–ˆβ–ˆβ–ˆβ–ˆβ–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘] Remaining today: 953 requests Resets in: 14 hours, 23 minutes ``` ### 3. Set Up Alerts ```javascript setInterval(async () => { const usage = await getUsage(); const remaining = usage.usage.today.remaining; if (remaining <= 10) { sendAlert('⚠️ Low quota warning: ' + remaining + ' requests left'); } }, 60000); // Check every minute ``` ### 4. Cache Usage Data Don't call `/v1/usage` on every request - it counts against your quota! ```javascript let cachedUsage = null; let cacheTime = null; const CACHE_DURATION = 60000; // 1 minute async function getUsageCached() { const now = Date.now(); if (!cachedUsage || now - cacheTime > CACHE_DURATION) { cachedUsage = await fetch('/v1/usage').then(r => r.json()); cacheTime = now; } return cachedUsage; } ``` --- ## Next Steps - **[Request History](history.md)** - See detailed request logs - **[Test Endpoint](test-prompt.md)** - Make your first request - **[Upgrade](https://www.promptster.dev/pricing)** - Increase your limits --- **Need help?** Contact support@promptster.dev --- ## Section: Request History --- # GET /v1/history Retrieve your past API requests with detailed metrics, filtering, and pagination. Perfect for debugging, analytics, and cost tracking. --- ## Endpoint ``` GET /v1/history ``` --- ## Authentication Requires only your Promptster API key: ```bash Authorization: Bearer YOUR_PROMPTSTER_KEY ``` --- ## Request ### Headers ```http Authorization: Bearer pk_live_abc123... ``` ### Query Parameters | Parameter | Type | Required | Default | Description | |-----------|------|----------|---------|-------------| | `limit` | integer | ❌ No | 50 | Number of results (1-100) | | `offset` | integer | ❌ No | 0 | Pagination offset | | `provider` | string | ❌ No | - | Filter by endpoint (e.g., `/v1/prompts/test`) | | `start_date` | string | ❌ No | - | Filter by start date (ISO 8601) | | `end_date` | string | ❌ No | - | Filter by end date (ISO 8601) | | `status_code` | integer | ❌ No | - | Filter by HTTP status (e.g., `200`, `400`) | ### Request Examples **Basic - Last 10 requests:** ```bash curl -X GET "https://www.promptster.dev/v1/history?limit=10" \ -H "Authorization: Bearer pk_live_abc123..." ``` **Filter by endpoint:** ```bash curl -X GET "https://www.promptster.dev/v1/history?provider=/v1/prompts/test" \ -H "Authorization: Bearer pk_live_abc123..." ``` **Filter by date range:** ```bash curl -X GET "https://www.promptster.dev/v1/history?start_date=2026-01-20T00:00:00Z&end_date=2026-01-25T23:59:59Z" \ -H "Authorization: Bearer pk_live_abc123..." ``` **Pagination:** ```bash # First page curl -X GET "https://www.promptster.dev/v1/history?limit=50&offset=0" \ -H "Authorization: Bearer pk_live_abc123..." # Second page curl -X GET "https://www.promptster.dev/v1/history?limit=50&offset=50" \ -H "Authorization: Bearer pk_live_abc123..." ``` **Filter by errors only:** ```bash curl -X GET "https://www.promptster.dev/v1/history?status_code=400" \ -H "Authorization: Bearer pk_live_abc123..." ``` --- ## Response ### Success (200 OK) ```json { "data": [ { "id": "log_abc123", "user_id": "user_xyz789", "api_key_id": "key_def456", "endpoint": "/v1/prompts/test", "method": "POST", "status_code": 200, "response_time_ms": 2152, "rate_limited": false, "error_message": null, "created_at": "2026-01-25T14:32:18.543Z" }, { "id": "log_abc124", "user_id": "user_xyz789", "api_key_id": "key_def456", "endpoint": "/v1/prompts/compare", "method": "POST", "status_code": 200, "response_time_ms": 3890, "rate_limited": false, "error_message": null, "created_at": "2026-01-25T14:28:05.123Z" }, { "id": "log_abc125", "user_id": "user_xyz789", "api_key_id": "key_def456", "endpoint": "/v1/prompts/test", "method": "POST", "status_code": 429, "response_time_ms": 12, "rate_limited": true, "error_message": "Rate limit exceeded", "created_at": "2026-01-25T14:20:42.987Z" } ], "pagination": { "total": 247, "limit": 50, "offset": 0, "has_more": true }, "statistics": { "avg_response_time_ms": 2018, "success_rate": 0.96, "total_requests": 247, "error_count": 10 }, "filters": { "provider": null, "start_date": null, "end_date": null, "status_code": null } } ``` ### Response Fields | Field | Type | Description | |-------|------|-------------| | `data` | array | Array of request log entries | | `data[].id` | string | Unique log ID | | `data[].user_id` | string | Your user ID | | `data[].api_key_id` | string | API key used (by ID) | | `data[].endpoint` | string | API endpoint called | | `data[].method` | string | HTTP method (`POST`, `GET`) | | `data[].status_code` | integer | HTTP status code | | `data[].response_time_ms` | integer | Response time in milliseconds | | `data[].rate_limited` | boolean | Whether request was rate limited | | `data[].error_message` | string\|null | Error message if failed | | `data[].created_at` | string | Timestamp (ISO 8601) | | `pagination` | object | Pagination metadata | | `pagination.total` | integer | Total matching records | | `pagination.limit` | integer | Requested limit | | `pagination.offset` | integer | Current offset | | `pagination.has_more` | boolean | More results available? | | `statistics` | object | Aggregate statistics | | `statistics.avg_response_time_ms` | number | Average response time | | `statistics.success_rate` | number | Success rate (0.0-1.0) | | `statistics.total_requests` | integer | Total requests | | `statistics.error_count` | integer | Failed requests | | `filters` | object | Applied filters (echo) | --- ## Code Examples ### JavaScript / Node.js ```javascript async function getHistory(options = {}) { const params = new URLSearchParams({ limit: options.limit || 50, offset: options.offset || 0, ...(options.provider && { provider: options.provider }), ...(options.startDate && { start_date: options.startDate }), ...(options.endDate && { end_date: options.endDate }) }); const response = await fetch(`https://www.promptster.dev/v1/history?${params}`, { headers: { 'Authorization': 'Bearer pk_live_abc123...' } }); const data = await response.json(); console.log(`πŸ“Š Request History (${data.pagination.total} total)`); console.log(`Average response time: ${data.statistics.avg_response_time_ms}ms`); console.log(`Success rate: ${(data.statistics.success_rate * 100).toFixed(1)}%`); console.log(); data.data.forEach(log => { const status = log.status_code === 200 ? 'βœ…' : '❌'; console.log(`${status} ${log.endpoint} [${log.status_code}] - ${log.response_time_ms}ms`); console.log(` ${new Date(log.created_at).toLocaleString()}`); if (log.error_message) { console.log(` Error: ${log.error_message}`); } console.log(); }); return data; } // Examples: getHistory(); // Last 50 requests getHistory({ limit: 10 }); // Last 10 getHistory({ provider: '/v1/prompts/test' }); // Test endpoint only getHistory({ startDate: '2026-01-25T00:00:00Z' }); // Today's requests ``` ### Python ```python import requests from urllib.parse import urlencode from datetime import datetime def get_history(limit=50, offset=0, provider=None, start_date=None, end_date=None): params = { 'limit': limit, 'offset': offset } if provider: params['provider'] = provider if start_date: params['start_date'] = start_date if end_date: params['end_date'] = end_date response = requests.get( f'https://www.promptster.dev/v1/history?{urlencode(params)}', headers={ 'Authorization': 'Bearer pk_live_abc123...' } ) data = response.json() print(f"πŸ“Š Request History ({data['pagination']['total']} total)") print(f"Average response time: {data['statistics']['avg_response_time_ms']}ms") print(f"Success rate: {data['statistics']['success_rate'] * 100:.1f}%") print() for log in data['data']: status = 'βœ…' if log['status_code'] == 200 else '❌' print(f"{status} {log['endpoint']} [{log['status_code']}] - {log['response_time_ms']}ms") timestamp = datetime.fromisoformat(log['created_at'].replace('Z', '+00:00')) print(f" {timestamp.strftime('%Y-%m-%d %H:%M:%S')}") if log['error_message']: print(f" Error: {log['error_message']}") print() return data # Examples: get_history() # Last 50 requests get_history(limit=10) # Last 10 get_history(provider='/v1/prompts/test') # Test endpoint only get_history(start_date='2026-01-25T00:00:00Z') # Today's requests ``` ### cURL ```bash # Last 10 requests curl -X GET "https://www.promptster.dev/v1/history?limit=10" \ -H "Authorization: Bearer pk_live_abc123..." # Filter by endpoint curl -X GET "https://www.promptster.dev/v1/history?provider=/v1/prompts/test" \ -H "Authorization: Bearer pk_live_abc123..." # Today's requests TODAY=$(date -u +%Y-%m-%dT00:00:00Z) curl -X GET "https://www.promptster.dev/v1/history?start_date=$TODAY" \ -H "Authorization: Bearer pk_live_abc123..." ``` --- ## Use Cases ### 1. Debugging Errors Find all failed requests: ```javascript async function debugErrors() { const response = await fetch('https://www.promptster.dev/v1/history?status_code=400', { headers: { 'Authorization': 'Bearer pk_live_abc123...' } }); const data = await response.json(); console.log(`Found ${data.pagination.total} errors`); data.data.forEach(log => { console.log(`Error at ${log.created_at}:`); console.log(` Endpoint: ${log.endpoint}`); console.log(` Message: ${log.error_message}`); }); } ``` ### 2. Performance Analysis Find slow requests: ```javascript async function findSlowRequests() { const response = await fetch('https://www.promptster.dev/v1/history?limit=100', { headers: { 'Authorization': 'Bearer pk_live_abc123...' } }); const data = await response.json(); // Find requests over 5 seconds const slowRequests = data.data.filter(log => log.response_time_ms > 5000); console.log(`${slowRequests.length} slow requests (>5s):`); slowRequests.forEach(log => { console.log(` ${log.endpoint}: ${log.response_time_ms}ms`); }); } ``` ### 3. Usage Analytics Analyze endpoint usage: ```javascript async function analyzeUsage() { const response = await fetch('https://www.promptster.dev/v1/history?limit=100', { headers: { 'Authorization': 'Bearer pk_live_abc123...' } }); const data = await response.json(); // Group by endpoint const byEndpoint = {}; data.data.forEach(log => { byEndpoint[log.endpoint] = (byEndpoint[log.endpoint] || 0) + 1; }); console.log('Endpoint usage:'); Object.entries(byEndpoint).forEach(([endpoint, count]) => { console.log(` ${endpoint}: ${count} requests`); }); } ``` ### 4. Daily Reports Generate daily usage report: ```javascript async function dailyReport() { const today = new Date().toISOString().split('T')[0]; const startDate = `${today}T00:00:00Z`; const endDate = `${today}T23:59:59Z`; const response = await fetch( `https://www.promptster.dev/v1/history?start_date=${startDate}&end_date=${endDate}`, { headers: { 'Authorization': 'Bearer pk_live_abc123...' } } ); const data = await response.json(); console.log(`Daily Report for ${today}`); console.log(`Total requests: ${data.statistics.total_requests}`); console.log(`Success rate: ${(data.statistics.success_rate * 100).toFixed(1)}%`); console.log(`Avg response time: ${data.statistics.avg_response_time_ms}ms`); console.log(`Errors: ${data.statistics.error_count}`); } ``` --- ## Pagination ### Sequential Pagination ```javascript async function getAllHistory() { const allLogs = []; let offset = 0; const limit = 100; while (true) { const response = await fetch( `https://www.promptster.dev/v1/history?limit=${limit}&offset=${offset}`, { headers: { 'Authorization': 'Bearer pk_live_abc123...' } } ); const data = await response.json(); allLogs.push(...data.data); if (!data.pagination.has_more) break; offset += limit; } return allLogs; } ``` ### Page-Based Navigation ```javascript function getPage(pageNumber, pageSize = 50) { const offset = (pageNumber - 1) * pageSize; return fetch( `https://www.promptster.dev/v1/history?limit=${pageSize}&offset=${offset}`, { headers: { 'Authorization': 'Bearer pk_live_abc123...' } } ).then(r => r.json()); } // Usage: const page1 = await getPage(1); // First 50 results const page2 = await getPage(2); // Next 50 results ``` --- ## Filtering Tips ### Combine Multiple Filters ```bash # Rate-limited requests on a specific endpoint curl -X GET "https://www.promptster.dev/v1/history?provider=/v1/prompts/test&status_code=429" \ -H "Authorization: Bearer pk_live_abc123..." ``` ### Date Range Formatting Always use ISO 8601 format with timezone: ```javascript // βœ… Correct start_date=2026-01-25T00:00:00Z end_date=2026-01-25T23:59:59Z // ❌ Incorrect start_date=2026-01-25 start_date=01/25/2026 ``` ### Getting Today's Requests ```javascript const today = new Date(); today.setHours(0, 0, 0, 0); const startDate = today.toISOString(); const response = await fetch( `https://www.promptster.dev/v1/history?start_date=${startDate}`, { headers: { 'Authorization': 'Bearer pk_live_abc123...' } } ); ``` --- ## Statistics The `statistics` object provides useful aggregate metrics: ```json { "avg_response_time_ms": 2018, // Average time across all requests "success_rate": 0.96, // 96% success rate "total_requests": 247, // Total matching requests "error_count": 10 // Failed requests } ``` Use these for monitoring: ```javascript const { statistics } = await getHistory(); // Alert on high error rate if (statistics.success_rate < 0.90) { alert('⚠️ Success rate below 90%!'); } // Alert on slow responses if (statistics.avg_response_time_ms > 3000) { alert('⚠️ Average response time > 3 seconds!'); } ``` --- ## Error Responses ### 400 Bad Request - Invalid Parameters ```json { "error": { "type": "validation_error", "message": "limit must be between 1 and 100", "field": "limit" } } ``` **Solutions**: - Check limit is 1-100 - Verify date formats (ISO 8601) - Ensure valid endpoint names ### 401 Unauthorized - Invalid API Key ```json { "error": { "type": "authentication_error", "message": "Invalid API key. Please check your Authorization header." } } ``` --- ## Best Practices ### 1. Use Appropriate Limits ```javascript // βœ… Good - Reasonable pagination limit=50, offset=0 limit=50, offset=50 // ⚠️ Less efficient - Too small limit=10 // Makes more API calls // ❌ Bad - Too large limit=1000 // Will be rejected (max 100) ``` ### 2. Cache Results Don't query history every second - cache results: ```javascript let cachedHistory = null; let cacheTime = null; async function getHistoryCached() { const now = Date.now(); if (!cachedHistory || now - cacheTime > 60000) { // 1 minute cache cachedHistory = await fetch('/v1/history?limit=20').then(r => r.json()); cacheTime = now; } return cachedHistory; } ``` ### 3. Filter Server-Side ```javascript // βœ… Good - Filter on server fetch('/v1/history?provider=/v1/prompts/test') // ❌ Bad - Fetch all then filter client-side fetch('/v1/history?limit=100').then(data => data.data.filter(log => log.endpoint === '/v1/prompts/test') ) ``` ### 4. Export for Analysis ```javascript async function exportToCSV() { const allLogs = await getAllHistory(); const csv = [ 'Timestamp,Endpoint,Status,Response Time (ms),Error', ...allLogs.map(log => [ log.created_at, log.endpoint, log.status_code, log.response_time_ms, log.error_message || '' ].join(',')) ].join('\n'); // Download or save CSV downloadFile('history.csv', csv); } ``` --- ## Next Steps - **[Usage Statistics](usage.md)** - Monitor your quota - **[Test Endpoint](test-prompt.md)** - Make your first request - **[Error Reference](../errors.md)** - Understand error codes --- **Need help?** Contact support@promptster.dev --- ## Section: Data Export --- # GET /v1/export Export all your saved tests, runs, schedules, and schedule runs as JSON or CSV. Available on Builder and Scale plans. --- ## Endpoint ``` GET /v1/export ``` --- ## Authentication Requires your Promptster API key: ```bash Authorization: Bearer YOUR_PROMPTSTER_KEY ``` --- ## Tier Requirements | Tier | Access | |------|--------| | Free | **Not available** β€” returns 403 | | Builder | Full access | | Scale | Full access | --- ## Request ### Headers ```http Authorization: Bearer pk_live_abc123... ``` ### Query Parameters | Parameter | Type | Default | Description | |-----------|------|---------|-------------| | `format` | string | `json` | Export format: `json` or `csv` | | `include` | string | all | Comma-separated list of data types to include: `tests`, `runs`, `schedules`, `schedule_runs` | ### Request Examples **Export everything as JSON:** ```bash curl -X GET "https://www.promptster.dev/v1/export" \ -H "Authorization: Bearer pk_live_abc123..." ``` **Export only tests and runs as CSV:** ```bash curl -X GET "https://www.promptster.dev/v1/export?format=csv&include=tests,runs" \ -H "Authorization: Bearer pk_live_abc123..." ``` --- ## Response ### JSON Format (200 OK) ```json { "exported_at": "2026-02-16T12:00:00Z", "data": { "tests": [ { "id": "abc123", "title": "GPT-4o vs Claude comparison", "prompt": "Explain quantum computing", "provider": "openai", "model": "gpt-4o", "created_at": "2026-02-10T08:30:00Z" } ], "runs": [ { "id": "run_001", "test_id": "abc123", "response_time_ms": 1200, "input_tokens": 15, "output_tokens": 450, "cost": 0.0023, "created_at": "2026-02-10T08:30:01Z" } ], "schedules": [ { "id": "sched_001", "name": "Daily GPT-4o check", "provider": "openai", "model": "gpt-4o", "cron": "0 9 * * *", "enabled": true, "created_at": "2026-02-05T10:00:00Z" } ], "schedule_runs": [ { "id": "sr_001", "schedule_id": "sched_001", "response_time_ms": 1100, "sla_breach": false, "created_at": "2026-02-16T09:00:00Z" } ] }, "counts": { "tests": 42, "runs": 128, "schedules": 5, "schedule_runs": 87 } } ``` ### CSV Format (200 OK) Returns a CSV file as an attachment download. Each included data type is exported as a separate section within the file. ``` Content-Type: text/csv Content-Disposition: attachment; filename="promptster-export-2026-02-16.csv" ``` ### Response Fields (JSON) | Field | Type | Description | |-------|------|-------------| | `exported_at` | string | Export timestamp (ISO 8601) | | `data.tests` | array | Saved test configurations | | `data.runs` | array | Test execution results with metrics | | `data.schedules` | array | Schedule configurations | | `data.schedule_runs` | array | Schedule execution history | | `counts` | object | Row counts per data type | --- ## Limits - Maximum **10,000 rows** per data category - If a category exceeds 10,000 rows, the most recent 10,000 are returned --- ## Error Responses ### 403 Forbidden β€” Free Tier ```json { "error": { "type": "forbidden", "message": "Data export requires Builder or Scale plan. Upgrade at https://www.promptster.dev/pricing" } } ``` ### 401 Unauthorized β€” Invalid API Key ```json { "error": { "type": "authentication_error", "message": "Invalid API key. Please check your Authorization header." } } ``` --- ## Code Examples ### JavaScript / Node.js ```javascript async function exportData() { const response = await fetch('https://www.promptster.dev/v1/export?format=json&include=tests,runs', { headers: { 'Authorization': 'Bearer pk_live_abc123...' } }); const data = await response.json(); console.log(`Exported at: ${data.exported_at}`); console.log(`Tests: ${data.counts.tests}`); console.log(`Runs: ${data.counts.runs}`); return data; } exportData(); ``` ### Python ```python import requests def export_data(): response = requests.get( 'https://www.promptster.dev/v1/export', params={'format': 'json', 'include': 'tests,runs'}, headers={ 'Authorization': 'Bearer pk_live_abc123...' } ) data = response.json() print(f"Exported at: {data['exported_at']}") print(f"Tests: {data['counts']['tests']}") print(f"Runs: {data['counts']['runs']}") return data export_data() ``` ### cURL β€” CSV Download ```bash curl -X GET "https://www.promptster.dev/v1/export?format=csv" \ -H "Authorization: Bearer pk_live_abc123..." \ -o promptster-export.csv ``` --- ## Next Steps - **[Schedules](schedules.md)** β€” Set up automated scheduled tests - **[Usage Statistics](usage.md)** β€” Monitor your API usage - **[Request History](history.md)** β€” Browse individual request logs --- **Need help?** Contact support@promptster.dev --- ## Section: Schedules --- # Schedules API Manage scheduled tests programmatically. Create automated test runs that execute on intervals, cron schedules, or via webhooks with SLA monitoring and notifications. --- ## Overview Scheduled tests allow you to: - **Automate regression testing** - Run prompts on a schedule to detect performance changes - **Monitor SLA compliance** - Set thresholds for response time and cost - **Get notified** - Receive alerts on failures, SLA breaches, or successes - **Trigger via webhook** - Integrate with CI/CD pipelines or external systems --- ## Schedule Limits Limits are based on your subscription tier: | Tier | Max Schedules | |------|---------------| | **Free** | 3 | | **Builder** ($19/mo) | 20 | | **Scale** ($49/mo) | 100 | --- ## Endpoints | Method | Endpoint | Description | |--------|----------|-------------| | GET | `/v1/schedules` | List all schedules | | POST | `/v1/schedules` | Create a new schedule | | GET | `/v1/schedules/:id` | Get schedule details | | PATCH | `/v1/schedules/:id` | Update a schedule | | DELETE | `/v1/schedules/:id` | Delete a schedule | | POST | `/v1/schedules/:id/run` | Manually trigger execution | | GET | `/v1/schedules/:id/runs` | Get run history | --- ## Authentication All schedule endpoints require your Promptster API key: ```bash Authorization: Bearer YOUR_PROMPTSTER_KEY ``` See [Authentication](../authentication.md) for details. --- ## GET /v1/schedules List all your schedules. ### Request ```bash curl https://www.promptster.dev/v1/schedules \ -H "Authorization: Bearer pk_live_abc123..." ``` ### Query Parameters | Parameter | Type | Default | Description | |-----------|------|---------|-------------| | `limit` | integer | 50 | Results per page (max 100) | | `offset` | integer | 0 | Pagination offset | ### Response (200 OK) ```json { "data": [ { "id": "550e8400-e29b-41d4-a716-446655440000", "prompt_id": "660e8400-e29b-41d4-a716-446655440001", "name": "Daily regression test", "schedule_type": "interval", "interval_minutes": 1440, "cron_expression": null, "timezone": "UTC", "is_enabled": true, "next_run_at": "2026-02-05T00:00:00.000Z", "last_run_at": "2026-02-04T00:00:00.000Z", "sla_thresholds": { "max_response_time_ms": 5000, "max_cost_per_run": 0.05 }, "notifications": { "on_success": false, "on_failure": true, "on_sla_breach": true, "webhook_callback_url": null }, "created_at": "2026-02-01T12:00:00.000Z", "updated_at": "2026-02-04T08:00:00.000Z" } ], "pagination": { "total": 3, "limit": 50, "offset": 0, "has_more": false }, "limits": { "current": 3, "max": 20, "tier": "hobby" } } ``` --- ## POST /v1/schedules Create a new scheduled test. ### Request Body | Parameter | Type | Required | Default | Description | |-----------|------|----------|---------|-------------| | `prompt_id` | string (UUID) | Yes | - | ID of the saved prompt to test | | `name` | string | Yes | - | Name for this schedule (1-100 chars) | | `schedule_type` | string | Yes | - | `"interval"`, `"cron"`, or `"webhook"` | | `interval_minutes` | integer | Conditional | - | Required if schedule_type is "interval" | | `cron_expression` | string | Conditional | - | Required if schedule_type is "cron" | | `timezone` | string | No | "UTC" | IANA timezone (e.g., "America/New_York") | | `max_response_time_ms` | integer | No | - | SLA threshold for response time | | `max_cost_per_run` | number | No | - | SLA threshold for cost (in USD) | | `notify_on_success` | boolean | No | false | Notify on successful runs | | `notify_on_failure` | boolean | No | true | Notify on failed runs | | `notify_on_sla_breach` | boolean | No | true | Notify on SLA breaches | | `webhook_callback_url` | string (URL) | No | - | URL to POST run results to | ### Example: Interval Schedule ```bash curl -X POST https://www.promptster.dev/v1/schedules \ -H "Authorization: Bearer pk_live_abc123..." \ -H "Content-Type: application/json" \ -d '{ "prompt_id": "660e8400-e29b-41d4-a716-446655440001", "name": "Hourly check", "schedule_type": "interval", "interval_minutes": 60, "max_response_time_ms": 3000, "max_cost_per_run": 0.01 }' ``` ### Example: Cron Schedule ```bash curl -X POST https://www.promptster.dev/v1/schedules \ -H "Authorization: Bearer pk_live_abc123..." \ -H "Content-Type: application/json" \ -d '{ "prompt_id": "660e8400-e29b-41d4-a716-446655440001", "name": "Daily at 9am EST", "schedule_type": "cron", "cron_expression": "0 9 * * *", "timezone": "America/New_York" }' ``` ### Example: Webhook Schedule ```bash curl -X POST https://www.promptster.dev/v1/schedules \ -H "Authorization: Bearer pk_live_abc123..." \ -H "Content-Type: application/json" \ -d '{ "prompt_id": "660e8400-e29b-41d4-a716-446655440001", "name": "CI/CD triggered", "schedule_type": "webhook", "webhook_callback_url": "https://your-server.com/promptster-results" }' ``` ### Response (201 Created) ```json { "id": "550e8400-e29b-41d4-a716-446655440000", "prompt_id": "660e8400-e29b-41d4-a716-446655440001", "name": "Hourly check", "schedule_type": "interval", "interval_minutes": 60, "cron_expression": null, "timezone": "UTC", "is_enabled": true, "next_run_at": "2026-02-04T13:00:00.000Z", "last_run_at": null, "sla_thresholds": { "max_response_time_ms": 3000, "max_cost_per_run": 0.01 }, "notifications": { "on_success": false, "on_failure": true, "on_sla_breach": true, "webhook_callback_url": null }, "created_at": "2026-02-04T12:00:00.000Z", "updated_at": "2026-02-04T12:00:00.000Z" } ``` --- ## GET /v1/schedules/:id Get details for a specific schedule including run statistics. ### Request ```bash curl https://www.promptster.dev/v1/schedules/550e8400-e29b-41d4-a716-446655440000 \ -H "Authorization: Bearer pk_live_abc123..." ``` ### Response (200 OK) ```json { "id": "550e8400-e29b-41d4-a716-446655440000", "prompt_id": "660e8400-e29b-41d4-a716-446655440001", "name": "Hourly check", "schedule_type": "interval", "interval_minutes": 60, "cron_expression": null, "timezone": "UTC", "is_enabled": true, "next_run_at": "2026-02-04T13:00:00.000Z", "last_run_at": "2026-02-04T12:00:00.000Z", "sla_thresholds": { "max_response_time_ms": 3000, "max_cost_per_run": 0.01 }, "notifications": { "on_success": false, "on_failure": true, "on_sla_breach": true, "webhook_callback_url": null }, "run_count": 42, "failure_count": 2, "created_at": "2026-02-01T12:00:00.000Z", "updated_at": "2026-02-04T12:00:00.000Z" } ``` --- ## PATCH /v1/schedules/:id Update an existing schedule. ### Request Body All fields are optional. Only include fields you want to change. | Parameter | Type | Description | |-----------|------|-------------| | `name` | string | New name for the schedule | | `schedule_type` | string | Change schedule type | | `interval_minutes` | integer \| null | Update interval (null to clear) | | `cron_expression` | string \| null | Update cron expression (null to clear) | | `timezone` | string | Update timezone | | `is_enabled` | boolean | Enable or disable the schedule | | `max_response_time_ms` | integer \| null | Update SLA threshold | | `max_cost_per_run` | number \| null | Update cost threshold (in USD) | | `notify_on_success` | boolean | Toggle success notifications | | `notify_on_failure` | boolean | Toggle failure notifications | | `notify_on_sla_breach` | boolean | Toggle SLA breach notifications | | `webhook_callback_url` | string \| null | Update or remove callback URL | ### Example: Disable a Schedule ```bash curl -X PATCH https://www.promptster.dev/v1/schedules/550e8400-e29b-41d4-a716-446655440000 \ -H "Authorization: Bearer pk_live_abc123..." \ -H "Content-Type: application/json" \ -d '{"is_enabled": false}' ``` ### Example: Update Interval ```bash curl -X PATCH https://www.promptster.dev/v1/schedules/550e8400-e29b-41d4-a716-446655440000 \ -H "Authorization: Bearer pk_live_abc123..." \ -H "Content-Type: application/json" \ -d '{"interval_minutes": 30}' ``` ### Response (200 OK) Returns the updated schedule object (same format as GET). --- ## DELETE /v1/schedules/:id Delete a schedule. This also removes all associated run history. ### Request ```bash curl -X DELETE https://www.promptster.dev/v1/schedules/550e8400-e29b-41d4-a716-446655440000 \ -H "Authorization: Bearer pk_live_abc123..." ``` ### Response (200 OK) ```json { "deleted": true, "id": "550e8400-e29b-41d4-a716-446655440000" } ``` --- ## POST /v1/schedules/:id/run Manually trigger a schedule execution. The test runs immediately regardless of `next_run_at`. ### Request ```bash curl -X POST https://www.promptster.dev/v1/schedules/550e8400-e29b-41d4-a716-446655440000/run \ -H "Authorization: Bearer pk_live_abc123..." ``` ### Response (200 OK) ```json { "run_id": "770e8400-e29b-41d4-a716-446655440002", "schedule_id": "550e8400-e29b-41d4-a716-446655440000", "status": "completed", "metrics": { "response_time_1_ms": 1847, "response_time_2_ms": 2103, "estimated_cost_1": 0.000105, "estimated_cost_2": 0.000089 }, "sla_breaches": { "response_time": false, "cost": false }, "error": null, "created_at": "2026-02-04T12:34:56.789Z" } ``` ### Response on Failure ```json { "run_id": "770e8400-e29b-41d4-a716-446655440002", "schedule_id": "550e8400-e29b-41d4-a716-446655440000", "status": "failed", "metrics": { "response_time_1_ms": null, "response_time_2_ms": null, "estimated_cost_1": null, "estimated_cost_2": null }, "sla_breaches": { "response_time": false, "cost": false }, "error": { "message": "API key not found for anthropic", "provider": null }, "created_at": "2026-02-04T12:34:56.789Z" } ``` --- ## Code Examples ### JavaScript / Node.js ```javascript const PROMPTSTER_KEY = 'pk_live_abc123...'; // Create a schedule async function createSchedule(promptId, name, intervalMinutes) { const response = await fetch('https://www.promptster.dev/v1/schedules', { method: 'POST', headers: { 'Authorization': `Bearer ${PROMPTSTER_KEY}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ prompt_id: promptId, name: name, schedule_type: 'interval', interval_minutes: intervalMinutes, max_response_time_ms: 5000, notify_on_failure: true }) }); return response.json(); } // List all schedules async function listSchedules() { const response = await fetch('https://www.promptster.dev/v1/schedules', { headers: { 'Authorization': `Bearer ${PROMPTSTER_KEY}` } }); const { data, limits } = await response.json(); console.log(`Using ${limits.current}/${limits.max} schedules`); return data; } // Manually trigger a run async function triggerRun(scheduleId) { const response = await fetch( `https://www.promptster.dev/v1/schedules/${scheduleId}/run`, { method: 'POST', headers: { 'Authorization': `Bearer ${PROMPTSTER_KEY}` } } ); const result = await response.json(); if (result.status === 'completed') { console.log(`Run completed in ${result.metrics.response_time_1_ms}ms`); } else { console.error(`Run failed: ${result.error?.message}`); } return result; } ``` ### Python ```python import requests PROMPTSTER_KEY = 'pk_live_abc123...' BASE_URL = 'https://www.promptster.dev/v1' HEADERS = {'Authorization': f'Bearer {PROMPTSTER_KEY}'} def create_schedule(prompt_id, name, interval_minutes): """Create a new scheduled test.""" response = requests.post( f'{BASE_URL}/schedules', headers=HEADERS, json={ 'prompt_id': prompt_id, 'name': name, 'schedule_type': 'interval', 'interval_minutes': interval_minutes, 'max_response_time_ms': 5000, 'notify_on_failure': True } ) return response.json() def list_schedules(): """List all schedules with usage info.""" response = requests.get(f'{BASE_URL}/schedules', headers=HEADERS) data = response.json() print(f"Using {data['limits']['current']}/{data['limits']['max']} schedules") return data['data'] def trigger_run(schedule_id): """Manually trigger a schedule execution.""" response = requests.post( f'{BASE_URL}/schedules/{schedule_id}/run', headers=HEADERS ) result = response.json() if result['status'] == 'completed': print(f"Run completed in {result['metrics']['response_time_1_ms']}ms") else: print(f"Run failed: {result.get('error', {}).get('message')}") return result # Example usage schedule = create_schedule( prompt_id='660e8400-e29b-41d4-a716-446655440001', name='Hourly regression', interval_minutes=60 ) print(f"Created schedule: {schedule['id']}") ``` ### cURL Complete Flow ```bash # 1. Create a schedule curl -X POST https://www.promptster.dev/v1/schedules \ -H "Authorization: Bearer pk_live_abc123..." \ -H "Content-Type: application/json" \ -d '{ "prompt_id": "660e8400-e29b-41d4-a716-446655440001", "name": "Daily check", "schedule_type": "interval", "interval_minutes": 1440 }' # 2. List schedules curl https://www.promptster.dev/v1/schedules \ -H "Authorization: Bearer pk_live_abc123..." # 3. Trigger a manual run curl -X POST https://www.promptster.dev/v1/schedules/SCHEDULE_ID/run \ -H "Authorization: Bearer pk_live_abc123..." # 4. Get run history curl https://www.promptster.dev/v1/schedules/SCHEDULE_ID/runs \ -H "Authorization: Bearer pk_live_abc123..." # 5. Disable the schedule curl -X PATCH https://www.promptster.dev/v1/schedules/SCHEDULE_ID \ -H "Authorization: Bearer pk_live_abc123..." \ -H "Content-Type: application/json" \ -d '{"is_enabled": false}' # 6. Delete the schedule curl -X DELETE https://www.promptster.dev/v1/schedules/SCHEDULE_ID \ -H "Authorization: Bearer pk_live_abc123..." ``` --- ## Schedule Types ### Interval Runs at a fixed interval (e.g., every hour, every day). ```json { "schedule_type": "interval", "interval_minutes": 60 } ``` Common intervals: - `15` - Every 15 minutes - `60` - Hourly - `360` - Every 6 hours - `1440` - Daily - `10080` - Weekly ### Cron Uses cron expressions for complex schedules. ```json { "schedule_type": "cron", "cron_expression": "0 9 * * 1-5", "timezone": "America/New_York" } ``` Examples: - `0 9 * * *` - Daily at 9:00 AM - `0 9 * * 1-5` - Weekdays at 9:00 AM - `0 */4 * * *` - Every 4 hours - `0 0 1 * *` - Monthly on the 1st ### Webhook Triggered on-demand via external webhook. Useful for CI/CD integration. ```json { "schedule_type": "webhook" } ``` Trigger the schedule by calling `/scheduler/webhook/:id` with the webhook secret: ```bash curl -X POST https://www.promptster.dev/scheduler/webhook/SCHEDULE_ID \ -H "X-Webhook-Signature: HMAC_SIGNATURE" \ -H "Content-Type: application/json" \ -d '{}' ``` --- ## SLA Thresholds Monitor performance with configurable thresholds: ### Response Time ```json { "max_response_time_ms": 5000 } ``` If any provider takes longer than 5000ms, the run is flagged as an SLA breach. ### Cost ```json { "max_cost_per_run": 0.05 } ``` If total cost exceeds $0.05, the run is flagged as an SLA breach. SLA breaches trigger notifications if `notify_on_sla_breach` is enabled. --- ## Error Responses ### 400 Bad Request ```json { "error": { "type": "validation_error", "message": "interval_minutes is required for interval schedule type" } } ``` ### 403 Forbidden - Limit Exceeded ```json { "error": { "type": "limit_exceeded", "message": "Schedule limit reached. You have 3/3 schedules for free tier.", "current": 3, "limit": 3, "tier": "free" } } ``` ### 404 Not Found ```json { "error": { "type": "not_found", "message": "Schedule not found" } } ``` ```json { "error": { "type": "not_found", "message": "Prompt not found or does not belong to you" } } ``` --- ## Next Steps - **[Schedule Runs](schedule-runs.md)** - View run history and metrics - **[Test Prompt](test-prompt.md)** - Test prompts before scheduling - **[Usage](usage.md)** - Monitor your API usage --- **Questions?** See the [API reference](../index.md) or email support@promptster.dev --- ## Section: Schedule Runs --- # GET /v1/schedules/:id/runs Retrieve the execution history for a scheduled test, including performance metrics, SLA breach status, and error details. --- ## Endpoint ``` GET /v1/schedules/:id/runs ``` --- ## Authentication ```bash Authorization: Bearer YOUR_PROMPTSTER_KEY ``` See [Authentication](../authentication.md) for details. --- ## Request ### Path Parameters | Parameter | Type | Description | |-----------|------|-------------| | `id` | string (UUID) | The schedule ID | ### Query Parameters | Parameter | Type | Default | Description | |-----------|------|---------|-------------| | `limit` | integer | 50 | Results per page (max 100) | | `offset` | integer | 0 | Pagination offset | | `status` | string | - | Filter by status: `pending`, `running`, `completed`, `failed`, `partial` | | `start_date` | string | - | Filter runs after this ISO 8601 timestamp | | `end_date` | string | - | Filter runs before this ISO 8601 timestamp | ### Example Request ```bash curl "https://www.promptster.dev/v1/schedules/550e8400-e29b-41d4-a716-446655440000/runs?limit=10&status=failed" \ -H "Authorization: Bearer pk_live_abc123..." ``` --- ## Response ### Success (200 OK) ```json { "data": [ { "id": "770e8400-e29b-41d4-a716-446655440002", "schedule_id": "550e8400-e29b-41d4-a716-446655440000", "prompt_id": "660e8400-e29b-41d4-a716-446655440001", "status": "completed", "trigger_type": "scheduled", "started_at": "2026-02-04T12:00:00.000Z", "completed_at": "2026-02-04T12:00:02.153Z", "metrics": { "response_time_1_ms": 1847, "response_time_2_ms": 2103, "estimated_cost_1": 0.000105, "estimated_cost_2": 0.000089 }, "sla_breaches": { "response_time": false, "cost": false }, "error": null, "created_at": "2026-02-04T12:00:00.000Z" }, { "id": "770e8400-e29b-41d4-a716-446655440003", "schedule_id": "550e8400-e29b-41d4-a716-446655440000", "prompt_id": "660e8400-e29b-41d4-a716-446655440001", "status": "failed", "trigger_type": "manual", "started_at": "2026-02-04T11:00:00.000Z", "completed_at": "2026-02-04T11:00:00.500Z", "metrics": { "response_time_1_ms": null, "response_time_2_ms": null, "estimated_cost_1": null, "estimated_cost_2": null }, "sla_breaches": { "response_time": false, "cost": false }, "error": { "message": "Invalid API key for anthropic", "provider": "anthropic" }, "created_at": "2026-02-04T11:00:00.000Z" } ], "pagination": { "total": 42, "limit": 10, "offset": 0, "has_more": true }, "filters": { "status": "failed", "start_date": null, "end_date": null } } ``` --- ## Run Object | Field | Type | Description | |-------|------|-------------| | `id` | string | Unique run identifier | | `schedule_id` | string | Parent schedule ID | | `prompt_id` | string | Prompt that was tested | | `status` | string | Run status (see below) | | `trigger_type` | string | How the run was triggered | | `started_at` | string | ISO 8601 timestamp when run started | | `completed_at` | string | ISO 8601 timestamp when run completed | | `metrics` | object | Performance metrics | | `sla_breaches` | object | SLA breach flags | | `error` | object \| null | Error details if failed | | `created_at` | string | ISO 8601 timestamp | ### Status Values | Status | Description | |--------|-------------| | `pending` | Run is queued but not started | | `running` | Run is currently executing | | `completed` | Run finished successfully | | `failed` | Run failed completely | | `partial` | Run partially completed (one provider succeeded, one failed) | ### Trigger Types | Type | Description | |------|-------------| | `scheduled` | Triggered by the schedule timer | | `manual` | Triggered via API (`POST /v1/schedules/:id/run`) | | `webhook` | Triggered via webhook endpoint | ### Metrics Object | Field | Type | Description | |-------|------|-------------| | `response_time_1_ms` | integer \| null | Response time for provider 1 | | `response_time_2_ms` | integer \| null | Response time for provider 2 (comparison only) | | `estimated_cost_1` | number \| null | Cost for provider 1 (USD) | | `estimated_cost_2` | number \| null | Cost for provider 2 (USD) | ### SLA Breaches Object | Field | Type | Description | |-------|------|-------------| | `response_time` | boolean | True if response time exceeded threshold | | `cost` | boolean | True if cost exceeded threshold | ### Error Object Present only when `status` is `failed` or `partial`: | Field | Type | Description | |-------|------|-------------| | `message` | string | Error description | | `provider` | string \| null | Provider that failed (if applicable) | --- ## Code Examples ### JavaScript / Node.js ```javascript const PROMPTSTER_KEY = 'pk_live_abc123...'; async function getRunHistory(scheduleId, options = {}) { const params = new URLSearchParams(); if (options.limit) params.set('limit', options.limit); if (options.offset) params.set('offset', options.offset); if (options.status) params.set('status', options.status); if (options.startDate) params.set('start_date', options.startDate); if (options.endDate) params.set('end_date', options.endDate); const url = `https://www.promptster.dev/v1/schedules/${scheduleId}/runs?${params}`; const response = await fetch(url, { headers: { 'Authorization': `Bearer ${PROMPTSTER_KEY}` } }); return response.json(); } // Get all runs const { data: runs, pagination } = await getRunHistory('550e8400-...'); console.log(`Total runs: ${pagination.total}`); // Get failed runs only const { data: failures } = await getRunHistory('550e8400-...', { status: 'failed' }); console.log(`Failed runs: ${failures.length}`); // Get runs from last 24 hours const yesterday = new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString(); const { data: recentRuns } = await getRunHistory('550e8400-...', { startDate: yesterday }); // Analyze SLA breaches const slaBreaches = runs.filter( run => run.sla_breaches.response_time || run.sla_breaches.cost ); console.log(`SLA breaches: ${slaBreaches.length}`); ``` ### Python ```python import requests from datetime import datetime, timedelta PROMPTSTER_KEY = 'pk_live_abc123...' BASE_URL = 'https://www.promptster.dev/v1' HEADERS = {'Authorization': f'Bearer {PROMPTSTER_KEY}'} def get_run_history(schedule_id, **kwargs): """Get run history with optional filters.""" params = {k: v for k, v in kwargs.items() if v is not None} response = requests.get( f'{BASE_URL}/schedules/{schedule_id}/runs', headers=HEADERS, params=params ) return response.json() # Get all runs result = get_run_history('550e8400-...') print(f"Total runs: {result['pagination']['total']}") # Get failed runs only failures = get_run_history('550e8400-...', status='failed') print(f"Failed runs: {len(failures['data'])}") # Get runs from last 24 hours yesterday = (datetime.now() - timedelta(days=1)).isoformat() recent = get_run_history('550e8400-...', start_date=yesterday) # Calculate success rate runs = result['data'] completed = sum(1 for r in runs if r['status'] == 'completed') success_rate = completed / len(runs) * 100 if runs else 0 print(f"Success rate: {success_rate:.1f}%") # Find SLA breaches breaches = [ r for r in runs if r['sla_breaches']['response_time'] or r['sla_breaches']['cost'] ] print(f"SLA breaches: {len(breaches)}") ``` ### cURL ```bash # Get recent runs curl "https://www.promptster.dev/v1/schedules/550e8400-e29b-41d4-a716-446655440000/runs?limit=10" \ -H "Authorization: Bearer pk_live_abc123..." # Get failed runs only curl "https://www.promptster.dev/v1/schedules/550e8400-e29b-41d4-a716-446655440000/runs?status=failed" \ -H "Authorization: Bearer pk_live_abc123..." # Get runs from a date range curl "https://www.promptster.dev/v1/schedules/550e8400-e29b-41d4-a716-446655440000/runs?start_date=2026-02-01T00:00:00Z&end_date=2026-02-04T00:00:00Z" \ -H "Authorization: Bearer pk_live_abc123..." # Paginate through results curl "https://www.promptster.dev/v1/schedules/550e8400-e29b-41d4-a716-446655440000/runs?limit=50&offset=50" \ -H "Authorization: Bearer pk_live_abc123..." ``` --- ## Use Cases ### Monitor Regression Testing ```javascript async function checkForRegressions(scheduleId) { const { data: runs } = await getRunHistory(scheduleId, { limit: 100 }); // Calculate average response time const completedRuns = runs.filter(r => r.status === 'completed'); const avgResponseTime = completedRuns.reduce( (sum, r) => sum + (r.metrics.response_time_1_ms || 0), 0 ) / completedRuns.length; // Check for recent slowdowns const recentRuns = completedRuns.slice(0, 10); const recentAvg = recentRuns.reduce( (sum, r) => sum + (r.metrics.response_time_1_ms || 0), 0 ) / recentRuns.length; if (recentAvg > avgResponseTime * 1.5) { console.warn(`Response time regression detected: ${recentAvg}ms vs ${avgResponseTime}ms average`); } } ``` ### Track Costs Over Time ```python def calculate_monthly_costs(schedule_id): """Calculate total costs for a schedule this month.""" from datetime import datetime # Get runs from start of month start_of_month = datetime.now().replace(day=1, hour=0, minute=0, second=0).isoformat() result = get_run_history(schedule_id, start_date=start_of_month, limit=100) total_cost = 0 for run in result['data']: cost1 = run['metrics'].get('estimated_cost_1') or 0 cost2 = run['metrics'].get('estimated_cost_2') or 0 total_cost += cost1 + cost2 print(f"Total cost this month: ${total_cost:.4f}") return total_cost ``` --- ## Error Responses ### 404 Not Found ```json { "error": { "type": "not_found", "message": "Schedule not found" } } ``` --- ## Next Steps - **[Schedules API](schedules.md)** - Create and manage schedules - **[Usage](usage.md)** - Monitor your API usage - **[History](history.md)** - View all API request history --- **Questions?** See the [API reference](../index.md) or email support@promptster.dev --- ## Section: Error Reference --- # Error Reference Complete guide to all API error types, status codes, and how to handle them. --- ## Error Response Format All errors follow a consistent JSON structure: ```json { "error": { "type": "error_type_snake_case", "message": "Human-readable error description", "field": "field_name", // Optional: for validation errors "retry_after": 3600, // Optional: for rate limit errors "provider": "openai" // Optional: for provider errors } } ``` --- ## Error Types by Status Code ### 400 Bad Request - Validation Errors Client sent invalid data. #### Invalid Provider ```json { "error": { "type": "validation_error", "message": "Invalid provider. Must be one of: openai, anthropic, google, deepseek, xai, groq, mistral, perplexity, together, cerebras, fireworks", "field": "provider" } } ``` **Solutions**: - Check provider spelling - Use lowercase: `"openai"` not `"OpenAI"` - See supported providers in [test endpoint docs](endpoints/test-prompt.md#supported-models) #### Invalid Model ```json { "error": { "type": "validation_error", "message": "Invalid model name for provider openai", "field": "model" } } ``` **Solutions**: - Verify model name matches provider - Check model is still available (some models are deprecated) - See supported models in [test endpoint docs](endpoints/test-prompt.md#supported-models) #### Prompt Too Long ```json { "error": { "type": "validation_error", "message": "Prompt must be between 1 and 10,000 characters", "field": "prompt" } } ``` **Solutions**: - Shorten your prompt - Split into multiple requests - Use summary or key points instead of full text #### Invalid max_tokens ```json { "error": { "type": "validation_error", "message": "max_tokens must be between 1 and 4096", "field": "max_tokens" } } ``` **Solutions**: - Use value between 1-4096 - Default is 2000 if omitted - Some models have lower limits #### Invalid temperature ```json { "error": { "type": "validation_error", "message": "temperature must be between 0.0 and 2.0", "field": "temperature" } } ``` **Solutions**: - Use value between 0.0-2.0 - Lower = more focused (try 0.3) - Higher = more creative (try 1.2) - Default is 0.7 if omitted #### Invalid Configurations Array ```json { "error": { "type": "validation_error", "message": "configurations must contain 2-5 items", "field": "configurations" } } ``` **Solutions**: - Include at least 2 configurations for comparison - Don't exceed 5 configurations - Use test endpoint for single provider #### Missing Required Header ```json { "error": { "type": "validation_error", "message": "Missing required header: X-ANTHROPIC-API-KEY", "field": "headers" } } ``` **Solutions**: - Include provider API key in headers - Use correct header name for provider: - OpenAI: `X-OPENAI-API-KEY` - Anthropic: `X-ANTHROPIC-API-KEY` - Google: `X-GOOGLE-API-KEY` - DeepSeek: `X-DEEPSEEK-API-KEY` - xAI: `X-XAI-API-KEY` - Groq: `X-GROQ-API-KEY` - Mistral: `X-MISTRAL-API-KEY` --- ### 401 Unauthorized - Authentication Errors API key authentication failed. #### Invalid API Key ```json { "error": { "type": "authentication_error", "message": "Invalid API key. Please check your Authorization header." } } ``` **Solutions**: - Verify Promptster API key is correct - Check format: `Authorization: Bearer pk_live_...` - Ensure key hasn't been revoked - Create new key at [/developer/api-keys](https://www.promptster.dev/developer/api-keys) #### Missing Authorization Header ```json { "error": { "type": "authentication_error", "message": "Missing Authorization header" } } ``` **Solutions**: - Include: `Authorization: Bearer YOUR_KEY` - Don't forget "Bearer " prefix - Check header name spelling --- ### 429 Too Many Requests - Rate Limit Errors Request quota exceeded. #### Daily Limit Exceeded ```json { "error": { "type": "rate_limit_exceeded", "message": "You have exceeded your monthly call quota of 500 calls.", "retry_after": 43200 } } ``` **Solutions**: - Wait until quota resets (check `retry_after` seconds) - Upgrade to higher tier for more quota - Use test keys for development (unlimited daily) #### Per-Minute Limit Exceeded ```json { "error": { "type": "rate_limit_exceeded", "message": "You have exceeded your per-minute rate limit of 5 requests.", "retry_after": 45 } } ``` **Solutions**: - Wait `retry_after` seconds before retrying - Add delay between requests - Upgrade to higher tier for faster rate limit - Use exponential backoff in your code **Rate Limits by Tier**: | Tier | Included Calls/Month | Rate Limit (per minute) | |------|---------------------|------------------------| | Free | 500 | 5 | | Builder ($19/mo) | 5,000 | 30 | | Scale ($49/mo) | 25,000 | 120 | --- ### 503 Service Unavailable - Provider Errors AI provider API is unavailable or returned an error. #### Provider Temporarily Unavailable ```json { "error": { "type": "provider_error", "message": "OpenAI API is currently unavailable. Please try again later.", "provider": "openai" } } ``` **Solutions**: - Try again in a few seconds/minutes - Check provider status page: - [OpenAI Status](https://status.openai.com/) - [Anthropic Status](https://status.anthropic.com/) - Use different provider temporarily #### Invalid Provider API Key ```json { "error": { "type": "provider_error", "message": "Anthropic API authentication failed. Please check your X-ANTHROPIC-API-KEY.", "provider": "anthropic" } } ``` **Solutions**: - Verify provider API key is correct - Regenerate key in provider's dashboard - Check key has proper permissions - Ensure you have credits/quota with provider #### Provider Rate Limit ```json { "error": { "type": "provider_error", "message": "OpenAI rate limit exceeded. Please wait and try again.", "provider": "openai" } } ``` **Solutions**: - Wait before retrying - This is the **provider's** rate limit, not Promptster's - Upgrade your provider plan - Use different provider --- ### 500 Internal Server Error Something went wrong on Promptster's side. ```json { "error": { "type": "internal_error", "message": "An unexpected error occurred. Please try again." } } ``` **Solutions**: - Try request again - If persists, contact support@promptster.dev - Check [status page](https://status.promptster.dev) (coming soon) --- ## Error Handling Strategies ### 1. Retry with Exponential Backoff For rate limits and temporary errors: ```javascript async function makeRequestWithRetry(url, options, maxRetries = 3) { for (let i = 0; i < maxRetries; i++) { try { const response = await fetch(url, options); if (response.ok) { return await response.json(); } const error = await response.json(); // Don't retry on validation errors (400) if (response.status === 400) { throw new Error(error.error.message); } // Retry on 429 (rate limit) or 503 (provider error) if (response.status === 429 || response.status === 503) { const retryAfter = error.error.retry_after || Math.pow(2, i) * 1000; console.log(`Retrying after ${retryAfter}ms (attempt ${i + 1}/${maxRetries})`); await sleep(retryAfter); continue; } // Don't retry on auth errors (401) if (response.status === 401) { throw new Error('Authentication failed: ' + error.error.message); } } catch (error) { if (i === maxRetries - 1) throw error; } } } function sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } ``` ### 2. Graceful Degradation Handle provider errors by trying alternatives: ```javascript async function testPromptWithFallback(prompt) { const providers = [ { provider: 'anthropic', model: 'claude-sonnet-4-20250514' }, { provider: 'openai', model: 'gpt-4' }, { provider: 'google', model: 'gemini-1.5-pro' } ]; for (const config of providers) { try { const response = await fetch('/v1/prompts/test', { method: 'POST', headers: { 'Authorization': 'Bearer pk_live_...', [`X-${config.provider.toUpperCase()}-API-KEY`]: getProviderKey(config.provider), 'Content-Type': 'application/json' }, body: JSON.stringify({ ...config, prompt }) }); if (response.ok) { return await response.json(); } // Try next provider on error console.warn(`${config.provider} failed, trying next provider...`); } catch (error) { console.error(`${config.provider} error:`, error); } } throw new Error('All providers failed'); } ``` ### 3. User-Friendly Error Messages Convert technical errors to user-friendly messages: ```javascript function getUserFriendlyError(error) { const errorMap = { 'rate_limit_exceeded': 'You\'ve used all your requests for today. Please upgrade or try again tomorrow.', 'authentication_error': 'Your API key is invalid. Please check your settings.', 'provider_error': 'The AI service is temporarily unavailable. Please try again in a moment.', 'validation_error': `Invalid input: ${error.message}`, 'internal_error': 'Something went wrong on our end. We\'re working on it!' }; return errorMap[error.type] || error.message; } // Usage: try { const result = await makeRequest(); } catch (error) { const message = getUserFriendlyError(error.error); showErrorToUser(message); } ``` ### 4. Monitoring and Alerts Track errors in production: ```javascript async function makeRequestWithMonitoring(url, options) { try { const response = await fetch(url, options); if (!response.ok) { const error = await response.json(); // Log to monitoring service logError({ type: error.error.type, message: error.error.message, status: response.status, endpoint: url }); // Alert on critical errors if (response.status === 401 || response.status === 500) { sendAlert('Critical API error: ' + error.error.type); } throw error; } return await response.json(); } catch (error) { console.error('API request failed:', error); throw error; } } ``` --- ## Rate Limit Headers Even successful responses include rate limit headers: ```http HTTP/1.1 200 OK X-RateLimit-Remaining-Calls: 4953 X-Credit-Balance: 0 X-Billing-Source: plan_included X-RateLimit-Tier: builder ``` Use these to prevent rate limit errors: ```javascript async function checkRateLimitBeforeRequest(url, options) { const response = await fetch(url, options); const remaining = parseInt(response.headers.get('X-RateLimit-Remaining')); const reset = parseInt(response.headers.get('X-RateLimit-Reset')); // Warn if running low if (remaining < 10) { console.warn(`⚠️ Only ${remaining} requests remaining!`); console.warn(`Resets at ${new Date(reset * 1000).toLocaleString()}`); } // Pause if very low if (remaining < 5) { console.log('Rate limit low, pausing...'); await sleep(60000); // Wait 1 minute } return response; } ``` --- ## Error Response Examples by Endpoint ### POST /v1/prompts/test Common errors: - βœ… `400` - Invalid provider/model - βœ… `401` - Invalid Promptster API key - βœ… `429` - Rate limit exceeded - βœ… `503` - Provider unavailable or invalid provider API key ### POST /v1/prompts/compare Common errors: - βœ… `400` - Invalid configurations array (must be 2-5 items) - βœ… `400` - Missing provider API key for one of the providers - βœ… `401` - Invalid Promptster API key - βœ… `429` - Rate limit exceeded - βœ… `503` - One or more providers unavailable (partial success possible) ### GET /v1/usage Common errors: - βœ… `401` - Invalid Promptster API key ### GET /v1/history Common errors: - βœ… `400` - Invalid pagination parameters (limit/offset) - βœ… `400` - Invalid date format - βœ… `401` - Invalid Promptster API key --- ## Best Practices ### 1. Always Check Response Status ```javascript // βœ… Good const response = await fetch(url); if (!response.ok) { const error = await response.json(); console.error('API error:', error.error.message); throw error; } // ❌ Bad - Assumes success const data = await fetch(url).then(r => r.json()); ``` ### 2. Handle Specific Error Types ```javascript const response = await fetch(url); const data = await response.json(); if (!response.ok) { switch (data.error.type) { case 'rate_limit_exceeded': await sleep(data.error.retry_after * 1000); return makeRequest(); // Retry case 'authentication_error': redirectToLogin(); break; case 'provider_error': tryAlternativeProvider(); break; default: showGenericError(); } } ``` ### 3. Validate Input Before Sending ```javascript function validatePromptRequest(data) { const errors = []; if (!data.provider || !['openai', 'anthropic', 'google', 'deepseek', 'xai', 'groq', 'mistral', 'perplexity', 'together', 'cerebras', 'fireworks'].includes(data.provider)) { errors.push('Invalid provider'); } if (!data.prompt || data.prompt.length > 10000) { errors.push('Prompt must be 1-10,000 characters'); } if (data.max_tokens && (data.max_tokens < 1 || data.max_tokens > 4096)) { errors.push('max_tokens must be 1-4096'); } if (errors.length > 0) { throw new Error(errors.join(', ')); } } // Usage: try { validatePromptRequest(requestData); const result = await makeRequest(requestData); } catch (error) { showError(error.message); } ``` ### 4. Log All Errors ```javascript function logAPIError(error, context) { console.error('API Error', { type: error.error?.type, message: error.error?.message, context, timestamp: new Date().toISOString() }); // Send to monitoring service if (window.Sentry) { Sentry.captureException(error); } } ``` --- ## Testing Error Handling Test your error handling with intentional errors: ```javascript // Test 400 - Invalid provider await fetch('/v1/prompts/test', { body: JSON.stringify({ provider: 'invalid' }) }); // Test 429 - Rate limit // Make 101 requests in quick succession with free tier // Test 503 - Invalid provider key await fetch('/v1/prompts/test', { headers: { 'X-OPENAI-API-KEY': 'invalid_key' } }); ``` --- ## Next Steps - **[Test Endpoint](endpoints/test-prompt.md)** - Try making requests - **[Authentication](authentication.md)** - Manage your API keys - **[Usage](endpoints/usage.md)** - Monitor rate limits --- **Questions?** Contact support@promptster.dev --- ## Section: Code Examples --- # Code Examples Complete code examples in multiple languages for common API operations. --- ## Table of Contents - [JavaScript / Node.js](#javascript--nodejs) - [Python](#python) - [cURL](#curl) - [PHP](#php) - [Common Patterns](#common-patterns) --- ## JavaScript / Node.js ### Basic Test Request ```javascript const PROMPTSTER_KEY = process.env.PROMPTSTER_KEY; const ANTHROPIC_KEY = process.env.ANTHROPIC_KEY; async function testPrompt(prompt) { const response = await fetch('https://www.promptster.dev/v1/prompts/test', { method: 'POST', headers: { 'Authorization': `Bearer ${PROMPTSTER_KEY}`, 'X-ANTHROPIC-API-KEY': ANTHROPIC_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ provider: 'anthropic', model: 'claude-sonnet-4-20250514', prompt: prompt, max_tokens: 500, temperature: 0.7 }) }); if (!response.ok) { const error = await response.json(); throw new Error(`API error: ${error.error.message}`); } const data = await response.json(); console.log('Response:', data.response); console.log('Cost:', `$${data.metadata.estimated_cost}`); console.log('Time:', `${data.metadata.response_time_ms}ms`); return data; } // Usage testPrompt('Explain quantum computing in simple terms'); ``` ### Compare Multiple Providers ```javascript async function compareProviders(prompt) { const response = await fetch('https://www.promptster.dev/v1/prompts/compare', { method: 'POST', headers: { 'Authorization': `Bearer ${process.env.PROMPTSTER_KEY}`, 'X-OPENAI-API-KEY': process.env.OPENAI_KEY, 'X-ANTHROPIC-API-KEY': process.env.ANTHROPIC_KEY, 'X-GOOGLE-API-KEY': process.env.GOOGLE_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ prompt: prompt, configurations: [ { provider: 'openai', model: 'gpt-4', temperature: 0.7 }, { provider: 'anthropic', model: 'claude-sonnet-4-20250514', temperature: 0.7 }, { provider: 'google', model: 'gemini-1.5-pro', temperature: 0.7 } ], max_tokens: 500 }) }); const data = await response.json(); console.log('Comparison Results:\n'); data.results.forEach(result => { console.log(`${result.provider}/${result.model}:`); console.log(` Response: ${result.response.substring(0, 100)}...`); console.log(` Cost: $${result.metadata.estimated_cost}`); console.log(` Time: ${result.metadata.response_time_ms}ms\n`); }); // Find best value (quality per dollar) const bestValue = data.results.reduce((best, r) => { const valueScore = r.metadata.output_tokens / r.metadata.estimated_cost; const bestScore = best.metadata.output_tokens / best.metadata.estimated_cost; return valueScore > bestScore ? r : best; }); console.log(`πŸ’° Best value: ${bestValue.provider}/${bestValue.model}`); return data; } // Usage compareProviders('What is machine learning?'); ``` ### Error Handling with Retry ```javascript async function makeRequestWithRetry(url, options, maxRetries = 3) { for (let attempt = 0; attempt < maxRetries; attempt++) { try { const response = await fetch(url, options); if (response.ok) { return await response.json(); } const error = await response.json(); // Don't retry on validation errors if (response.status === 400) { throw new Error(`Validation error: ${error.error.message}`); } // Retry on rate limit or provider errors if (response.status === 429 || response.status === 503) { const retryAfter = error.error.retry_after || Math.pow(2, attempt) * 1000; console.log(`Retry ${attempt + 1}/${maxRetries} after ${retryAfter}ms`); await new Promise(resolve => setTimeout(resolve, retryAfter)); continue; } // Don't retry on auth errors if (response.status === 401) { throw new Error('Authentication failed'); } } catch (error) { if (attempt === maxRetries - 1) { throw error; } } } } // Usage const result = await makeRequestWithRetry( 'https://www.promptster.dev/v1/prompts/test', { method: 'POST', headers: { /* ... */ }, body: JSON.stringify({ /* ... */ }) } ); ``` ### Check Usage Before Request ```javascript async function safeRequest(promptData) { // Check usage first const usageResponse = await fetch('https://www.promptster.dev/v1/usage', { headers: { 'Authorization': `Bearer ${process.env.PROMPTSTER_KEY}` } }); const usage = await usageResponse.json(); if (usage.usage.today.remaining === 0) { throw new Error(`Daily quota exceeded! Resets at ${usage.usage.today.reset_at}`); } if (usage.usage.today.remaining < 10) { console.warn(`⚠️ Only ${usage.usage.today.remaining} requests remaining today`); } // Make the request return testPrompt(promptData); } ``` --- ## Python ### Basic Test Request ```python import os import requests PROMPTSTER_KEY = os.environ['PROMPTSTER_KEY'] ANTHROPIC_KEY = os.environ['ANTHROPIC_KEY'] def test_prompt(prompt): response = requests.post( 'https://www.promptster.dev/v1/prompts/test', headers={ 'Authorization': f'Bearer {PROMPTSTER_KEY}', 'X-ANTHROPIC-API-KEY': ANTHROPIC_KEY, 'Content-Type': 'application/json' }, json={ 'provider': 'anthropic', 'model': 'claude-sonnet-4-20250514', 'prompt': prompt, 'max_tokens': 500, 'temperature': 0.7 } ) if not response.ok: error = response.json() raise Exception(f"API error: {error['error']['message']}") data = response.json() print(f"Response: {data['response']}") print(f"Cost: ${data['metadata']['estimated_cost']}") print(f"Time: {data['metadata']['response_time_ms']}ms") return data # Usage test_prompt('Explain quantum computing in simple terms') ``` ### Compare Multiple Providers ```python def compare_providers(prompt): response = requests.post( 'https://www.promptster.dev/v1/prompts/compare', headers={ 'Authorization': f'Bearer {os.environ["PROMPTSTER_KEY"]}', 'X-OPENAI-API-KEY': os.environ['OPENAI_KEY'], 'X-ANTHROPIC-API-KEY': os.environ['ANTHROPIC_KEY'], 'X-GOOGLE-API-KEY': os.environ['GOOGLE_KEY'], 'Content-Type': 'application/json' }, json={ 'prompt': prompt, 'configurations': [ {'provider': 'openai', 'model': 'gpt-4', 'temperature': 0.7}, {'provider': 'anthropic', 'model': 'claude-sonnet-4-20250514', 'temperature': 0.7}, {'provider': 'google', 'model': 'gemini-1.5-pro', 'temperature': 0.7} ], 'max_tokens': 500 } ) data = response.json() print('Comparison Results:\n') for result in data['results']: print(f"{result['provider']}/{result['model']}:") print(f" Response: {result['response'][:100]}...") print(f" Cost: ${result['metadata']['estimated_cost']}") print(f" Time: {result['metadata']['response_time_ms']}ms\n") # Find cheapest option cheapest = min(data['results'], key=lambda r: r['metadata']['estimated_cost']) print(f"πŸ’° Cheapest: {cheapest['provider']}/{cheapest['model']} (${cheapest['metadata']['estimated_cost']})") return data # Usage compare_providers('What is machine learning?') ``` ### Error Handling with Retry ```python import time def make_request_with_retry(url, headers, json_data, max_retries=3): for attempt in range(max_retries): try: response = requests.post(url, headers=headers, json=json_data) if response.ok: return response.json() error = response.json() # Don't retry on validation errors if response.status_code == 400: raise Exception(f"Validation error: {error['error']['message']}") # Retry on rate limit or provider errors if response.status_code in [429, 503]: retry_after = error['error'].get('retry_after', 2 ** attempt) print(f"Retry {attempt + 1}/{max_retries} after {retry_after}s") time.sleep(retry_after) continue # Don't retry on auth errors if response.status_code == 401: raise Exception('Authentication failed') except Exception as e: if attempt == max_retries - 1: raise e raise Exception('Max retries exceeded') # Usage result = make_request_with_retry( 'https://www.promptster.dev/v1/prompts/test', { 'Authorization': f'Bearer {PROMPTSTER_KEY}', 'X-ANTHROPIC-API-KEY': ANTHROPIC_KEY, 'Content-Type': 'application/json' }, { 'provider': 'anthropic', 'model': 'claude-sonnet-4-20250514', 'prompt': 'Hello world' } ) ``` ### View Request History ```python from datetime import datetime, timedelta def get_history(limit=50, start_date=None, end_date=None): params = {'limit': limit} if start_date: params['start_date'] = start_date if end_date: params['end_date'] = end_date response = requests.get( 'https://www.promptster.dev/v1/history', headers={'Authorization': f'Bearer {os.environ["PROMPTSTER_KEY"]}'}, params=params ) data = response.json() print(f"πŸ“Š Request History ({data['pagination']['total']} total)") print(f"Average response time: {data['statistics']['avg_response_time_ms']}ms") print(f"Success rate: {data['statistics']['success_rate'] * 100:.1f}%\n") for log in data['data']: status = 'βœ…' if log['status_code'] == 200 else '❌' timestamp = datetime.fromisoformat(log['created_at'].replace('Z', '+00:00')) print(f"{status} {log['endpoint']} [{log['status_code']}] - {log['response_time_ms']}ms") print(f" {timestamp.strftime('%Y-%m-%d %H:%M:%S')}\n") return data # Get today's requests today = datetime.now().replace(hour=0, minute=0, second=0, microsecond=0) get_history(start_date=today.isoformat() + 'Z') ``` --- ## cURL ### Basic Test Request ```bash curl -X POST https://www.promptster.dev/v1/prompts/test \ -H "Authorization: Bearer $PROMPTSTER_KEY" \ -H "X-ANTHROPIC-API-KEY: $ANTHROPIC_KEY" \ -H "Content-Type: application/json" \ -d '{ "provider": "anthropic", "model": "claude-sonnet-4-20250514", "prompt": "Explain quantum computing in simple terms", "max_tokens": 500, "temperature": 0.7 }' ``` ### Compare Multiple Providers ```bash curl -X POST https://www.promptster.dev/v1/prompts/compare \ -H "Authorization: Bearer $PROMPTSTER_KEY" \ -H "X-OPENAI-API-KEY: $OPENAI_KEY" \ -H "X-ANTHROPIC-API-KEY: $ANTHROPIC_KEY" \ -H "Content-Type: application/json" \ -d '{ "prompt": "What is machine learning?", "configurations": [ {"provider": "openai", "model": "gpt-4"}, {"provider": "anthropic", "model": "claude-sonnet-4-20250514"} ], "max_tokens": 500 }' ``` ### Check Usage ```bash curl -X GET https://www.promptster.dev/v1/usage \ -H "Authorization: Bearer $PROMPTSTER_KEY" ``` ### View History ```bash # Last 10 requests curl -X GET "https://www.promptster.dev/v1/history?limit=10" \ -H "Authorization: Bearer $PROMPTSTER_KEY" # Filter by endpoint curl -X GET "https://www.promptster.dev/v1/history?provider=/v1/prompts/test" \ -H "Authorization: Bearer $PROMPTSTER_KEY" # Today's requests TODAY=$(date -u +%Y-%m-%dT00:00:00Z) curl -X GET "https://www.promptster.dev/v1/history?start_date=$TODAY" \ -H "Authorization: Bearer $PROMPTSTER_KEY" ``` ### Test with xAI (Grok) ```bash curl -X POST https://www.promptster.dev/v1/prompts/test \ -H "Authorization: Bearer $PROMPTSTER_KEY" \ -H "X-XAI-API-KEY: $XAI_KEY" \ -H "Content-Type: application/json" \ -d '{ "provider": "xai", "model": "grok-3-fast", "prompt": "Explain quantum computing in simple terms", "max_tokens": 500, "temperature": 0.7 }' ``` ### Test with Groq ```bash curl -X POST https://www.promptster.dev/v1/prompts/test \ -H "Authorization: Bearer $PROMPTSTER_KEY" \ -H "X-GROQ-API-KEY: $GROQ_KEY" \ -H "Content-Type: application/json" \ -d '{ "provider": "groq", "model": "llama-3.3-70b-versatile", "prompt": "Explain quantum computing in simple terms", "max_tokens": 500, "temperature": 0.7 }' ``` ### Test with Mistral ```bash curl -X POST https://www.promptster.dev/v1/prompts/test \ -H "Authorization: Bearer $PROMPTSTER_KEY" \ -H "X-MISTRAL-API-KEY: $MISTRAL_KEY" \ -H "Content-Type: application/json" \ -d '{ "provider": "mistral", "model": "mistral-large-latest", "prompt": "Explain quantum computing in simple terms", "max_tokens": 500, "temperature": 0.7 }' ``` ### Pretty Print Response ```bash # Add | jq for formatted output curl -X POST https://www.promptster.dev/v1/prompts/test \ -H "Authorization: Bearer $PROMPTSTER_KEY" \ -H "X-ANTHROPIC-API-KEY: $ANTHROPIC_KEY" \ -H "Content-Type: application/json" \ -d '{"provider":"anthropic","model":"claude-sonnet-4-20250514","prompt":"Hello"}' \ | jq '.' ``` --- ## PHP ### Basic Test Request ```php 'anthropic', 'model' => 'claude-sonnet-4-20250514', 'prompt' => $prompt, 'max_tokens' => 500, 'temperature' => 0.7 ]; $ch = curl_init('https://www.promptster.dev/v1/prompts/test'); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data)); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_HTTPHEADER, [ "Authorization: Bearer $promptsterKey", "X-ANTHROPIC-API-KEY: $anthropicKey", "Content-Type: application/json" ]); $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); $result = json_decode($response, true); if ($httpCode !== 200) { throw new Exception("API error: " . $result['error']['message']); } echo "Response: " . $result['response'] . "\n"; echo "Cost: $" . $result['metadata']['estimated_cost'] . "\n"; echo "Time: " . $result['metadata']['response_time_ms'] . "ms\n"; return $result; } // Usage testPrompt('Explain quantum computing in simple terms'); ?> ``` ### Compare Multiple Providers ```php $prompt, 'configurations' => [ ['provider' => 'openai', 'model' => 'gpt-4'], ['provider' => 'anthropic', 'model' => 'claude-sonnet-4-20250514'] ], 'max_tokens' => 500 ]; $ch = curl_init('https://www.promptster.dev/v1/prompts/compare'); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data)); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_HTTPHEADER, [ "Authorization: Bearer $promptsterKey", "X-OPENAI-API-KEY: $openaiKey", "X-ANTHROPIC-API-KEY: $anthropicKey", "Content-Type: application/json" ]); $response = curl_exec($ch); curl_close($ch); $result = json_decode($response, true); echo "Comparison Results:\n\n"; foreach ($result['results'] as $r) { echo $r['provider'] . "/" . $r['model'] . ":\n"; echo " Cost: $" . $r['metadata']['estimated_cost'] . "\n"; echo " Time: " . $r['metadata']['response_time_ms'] . "ms\n\n"; } return $result; } ?> ``` --- ## Common Patterns ### Cost Optimization Find the cheapest model that meets your quality needs: ```javascript async function findCheapestModel(prompt, minQualityScore = 0.8) { const response = await fetch('/v1/prompts/compare', { // ... compare all models }); const data = await response.json(); // Sort by cost (ascending) const sorted = data.results.sort((a, b) => a.metadata.estimated_cost - b.metadata.estimated_cost ); // Return cheapest model above quality threshold // (You'd implement your own quality scoring) for (const result of sorted) { const quality = evaluateQuality(result.response); if (quality >= minQualityScore) { return result; } } return sorted[0]; // Return cheapest if none meet threshold } ``` ### Batch Processing Process multiple prompts with rate limit awareness: ```javascript async function batchProcess(prompts, batchSize = 10) { const results = []; for (let i = 0; i < prompts.length; i += batchSize) { const batch = prompts.slice(i, i + batchSize); // Process batch in parallel const batchResults = await Promise.all( batch.map(prompt => testPrompt(prompt)) ); results.push(...batchResults); // Pause between batches to avoid rate limits if (i + batchSize < prompts.length) { await new Promise(resolve => setTimeout(resolve, 6000)); // 10/min = 6s between batches } } return results; } ``` ### Cost Tracking Dashboard Track cumulative costs over time: ```javascript let totalCost = 0; const costHistory = []; async function trackCost(requestFunction) { const result = await requestFunction(); const cost = result.metadata.estimated_cost; totalCost += cost; costHistory.push({ timestamp: new Date(), cost: cost, cumulative: totalCost }); console.log(`Request cost: $${cost}`); console.log(`Total today: $${totalCost}`); return result; } // Usage await trackCost(() => testPrompt('Hello')); await trackCost(() => testPrompt('How are you?')); ``` --- ## Environment Variables ### .env File ```bash # Promptster API PROMPTSTER_KEY=pk_live_abc123... # Provider API Keys OPENAI_KEY=sk-proj-def456... ANTHROPIC_KEY=sk-ant-ghi789... GOOGLE_KEY=AIza... DEEPSEEK_KEY=... XAI_KEY=xai-... GROQ_KEY=gsk_... MISTRAL_KEY=... ``` ### Loading in Node.js ```javascript // Install: npm install dotenv require('dotenv').config(); const PROMPTSTER_KEY = process.env.PROMPTSTER_KEY; const ANTHROPIC_KEY = process.env.ANTHROPIC_KEY; ``` ### Loading in Python ```python # Install: pip install python-dotenv from dotenv import load_dotenv import os load_dotenv() PROMPTSTER_KEY = os.getenv('PROMPTSTER_KEY') ANTHROPIC_KEY = os.getenv('ANTHROPIC_KEY') ``` --- ## Next Steps - **[Test Endpoint](endpoints/test-prompt.md)** - API reference - **[Error Handling](errors.md)** - Handle errors properly - **[Authentication](authentication.md)** - Manage API keys --- **Need help?** Contact support@promptster.dev --- ## Section: MCP Integration --- # MCP Integration Connect AI coding assistants like **Claude Code**, **Cursor**, and **Windsurf** to Promptster via the Model Context Protocol (MCP). Test and compare prompts directly from your editor without switching to the browser. --- ## What is MCP? The [Model Context Protocol](https://modelcontextprotocol.io/) (MCP) is an open standard for connecting AI assistants to external tools and data sources. Promptster's MCP server exposes the same capabilities as the REST API β€” test prompts, compare providers, check usage, manage schedules β€” as tools that your AI coding assistant can call directly. **Why use MCP?** - Test and iterate on prompts without leaving your editor - Let your AI assistant compare providers for you and pick the best one - Monitor usage and costs inline while building - Trigger scheduled test runs from your development workflow --- ## Prerequisites 1. **Promptster API key** β€” Create one at [/developer/api-keys](https://www.promptster.dev/developer/api-keys). Both `pk_live_*` and `pk_test_*` keys work. 2. **Saved provider API keys** β€” Save your provider keys in [/api-keys](https://www.promptster.dev/api-keys). MCP tools automatically use your saved keys, so you don't need to pass them in every request. --- ## Setup ### Claude Code (CLI) ```bash claude mcp add --transport http promptster https://www.promptster.dev/mcp \ --header "Authorization: Bearer ${PROMPTSTER_API_KEY}" ``` Set the `PROMPTSTER_API_KEY` environment variable in your shell profile, or replace `${PROMPTSTER_API_KEY}` with your key directly. ### Claude Desktop Claude Desktop **does not support remote HTTP servers directly** in its config file. You need [`mcp-remote`](https://github.com/geelen/mcp-remote) as a stdio-to-HTTP bridge. Edit `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) or `%APPDATA%\Claude\claude_desktop_config.json` (Windows): ```json { "mcpServers": { "promptster": { "command": "npx", "args": [ "mcp-remote", "https://www.promptster.dev/mcp", "--header", "Authorization:${PROMPTSTER_AUTH}" ], "env": { "PROMPTSTER_AUTH": "Bearer pk_live_YOUR_KEY_HERE" } } } } ``` > **Note:** The `Bearer ` prefix is in the `env` value (not in `args`) to avoid a known bug in Claude Desktop and Cursor on Windows where spaces in args get mangled. After saving, fully quit and relaunch Claude Desktop. ### Cursor Create or edit `.cursor/mcp.json` in your project root: ```json { "mcpServers": { "promptster": { "url": "https://www.promptster.dev/mcp", "headers": { "Authorization": "Bearer pk_live_YOUR_KEY_HERE" } } } } ``` ### Windsurf Create or edit `.windsurf/mcp.json` in your project root: ```json { "mcpServers": { "promptster": { "url": "https://www.promptster.dev/mcp", "headers": { "Authorization": "Bearer pk_live_YOUR_KEY_HERE" } } } } ``` ### Generic `.mcp.json` (Project-Scoped) Works with any MCP-compatible client that supports remote HTTP. Add to your project root: ```json { "mcpServers": { "promptster": { "type": "http", "url": "https://www.promptster.dev/mcp", "headers": { "Authorization": "Bearer ${PROMPTSTER_API_KEY}" } } } } ``` > **Tip:** Use environment variable substitution (`${PROMPTSTER_API_KEY}`) to avoid committing keys to version control. --- ## Available Tools | Tool | Description | Billing | |------|-------------|---------| | `test_prompt` | Test a prompt against one AI provider | 1 API call | | `compare_prompts` | Compare a prompt across 2-5 providers simultaneously | N API calls | | `get_usage` | View usage stats, rate limits, and billing info | Free | | `get_credits` | Check credit balance and billing period | Free | | `get_history` | Browse request history with filtering | Free | | `list_schedules` | List all scheduled tests | Free | | `get_schedule` | Get schedule details with run/failure counts | Free | | `run_schedule` | Manually trigger a scheduled test | 1 API call | | `get_schedule_runs` | View execution history for a schedule | Free | | `export_data` | Export all your Promptster data (Builder+ only) | Free | --- ## Tool Examples ### test_prompt Test a single prompt against one provider. The tool automatically uses your saved API key for that provider. **Input:** ```json { "provider": "anthropic", "model": "claude-sonnet-4-5", "prompt": "Explain the difference between REST and GraphQL in 3 sentences." } ``` **Output:** ```json { "provider": "anthropic", "model": "claude-sonnet-4-5", "response": "REST uses fixed endpoints that return predetermined data structures, while GraphQL provides a single endpoint where clients specify exactly what data they need. REST relies on HTTP methods (GET, POST, PUT, DELETE) for different operations, whereas GraphQL uses queries and mutations through a type system. GraphQL reduces over-fetching and under-fetching of data, but REST is simpler to cache and has broader tooling support.", "metrics": { "response_time_ms": 1847, "input_tokens": 22, "output_tokens": 89, "total_tokens": 111, "tokens_per_second": 48.2, "estimated_cost_usd": 0.000543 } } ``` ### compare_prompts Compare the same prompt across multiple providers side by side. **Input:** ```json { "prompt": "Write a Python function to check if a string is a palindrome.", "configurations": [ { "provider": "openai", "model": "gpt-4o-mini" }, { "provider": "anthropic", "model": "claude-sonnet-4-5" }, { "provider": "google", "model": "gemini-2.0-flash" } ], "max_tokens": 500 } ``` **Output:** ```json { "prompt": "Write a Python function to check if a string is a palindrome.", "results": [ { "provider": "openai", "model": "gpt-4o-mini", "response": "def is_palindrome(s):\n s = s.lower().replace(' ', '')\n return s == s[::-1]", "metrics": { "response_time_ms": 892, "input_tokens": 18, "output_tokens": 34, "tokens_per_second": 38.1, "estimated_cost_usd": 0.000012 }, "success": true }, { "provider": "anthropic", "model": "claude-sonnet-4-5", "response": "def is_palindrome(s: str) -> bool:\n cleaned = ''.join(c.lower() for c in s if c.isalnum())\n return cleaned == cleaned[::-1]", "metrics": { "response_time_ms": 1203, "input_tokens": 18, "output_tokens": 52, "tokens_per_second": 43.2, "estimated_cost_usd": 0.000198 }, "success": true }, { "provider": "google", "model": "gemini-2.0-flash", "response": "def is_palindrome(text):\n text = text.lower()\n text = ''.join(char for char in text if char.isalnum())\n return text == text[::-1]", "metrics": { "response_time_ms": 654, "input_tokens": 18, "output_tokens": 44, "tokens_per_second": 67.3, "estimated_cost_usd": 0.000008 }, "success": true } ] } ``` ### get_usage Check your current usage, rate limits, and credit balance. **Input:** _(none required)_ **Output:** ```json { "tier": "builder", "limits": { "requests_per_day": 10000, "requests_per_minute": 30 }, "usage": { "today": 47, "this_month": 1823 }, "billing_period": { "included_calls": 5000, "used_calls": 1823, "remaining_calls": 3177, "overage_calls": 0, "period_end": "2026-03-01T00:00:00.000Z" }, "credits": { "balance": 500, "total_purchased": 1000 } } ``` --- ## Provider API Key Handling MCP tools that call AI providers (`test_prompt`, `compare_prompts`) resolve API keys in this order: 1. **Explicit `api_key` parameter** β€” If you pass an `api_key` in the tool input, it's used directly. 2. **Saved keys** β€” If no explicit key is provided, the tool looks up your saved encrypted key from the Promptster dashboard ([/api-keys](https://www.promptster.dev/api-keys)). 3. **Error** β€” If no key is found, you'll get an error message with a link to save your key. **Recommendation:** Save your provider keys in the Promptster dashboard. This way, MCP tools work automatically without you needing to pass keys in every request. --- ## Rate Limits & Billing MCP tools share the same rate limits and billing as the REST API: | Tier | Included Calls/Month | Rate Limit | |------|---------------------|------------| | Free | 500 | 5/min | | Builder ($19/mo) | 5,000 | 30/min | | Scale ($49/mo) | 25,000 | 120/min | Tools marked "Free" in the tools table (like `get_usage`, `list_schedules`) do not count against your API call quota. Only tools that call AI providers (`test_prompt`, `compare_prompts`, `run_schedule`) consume API calls. --- ## Troubleshooting ### "Authentication required" error Make sure your `Authorization: Bearer` header is set correctly. Verify your API key is active at [/developer/api-keys](https://www.promptster.dev/developer/api-keys). ### "Missing API key for [provider]" error Save your provider API key at [/api-keys](https://www.promptster.dev/api-keys), or pass it directly via the `api_key` parameter. ### Claude Desktop won't start / crashes on launch Claude Desktop does not support remote HTTP servers configured directly via `claude_desktop_config.json`. You must use the `mcp-remote` bridge (see [Claude Desktop setup](#claude-desktop) above). If you added a `"url"` + `"headers"` entry instead of the `"command": "npx"` format, remove it and use the `mcp-remote` configuration. ### Connection timeout The MCP endpoint uses Streamable HTTP (not SSE). Ensure your client is configured for HTTP transport, not stdio or SSE. The endpoint URL is `https://www.promptster.dev/mcp`. ### Tools not appearing in your editor 1. Verify the MCP configuration file is in the correct location for your editor 2. Fully quit and relaunch your editor (not just reload) 3. Check that the API key in your config is valid and not revoked 4. For Claude Desktop, ensure `npx` is available in your PATH (Node.js must be installed) ### Rate limit exceeded You've hit your per-minute rate limit. Wait and retry, or upgrade your plan for higher limits. Check your current usage with the `get_usage` tool. > **Important:** Always use `www.promptster.dev` (not `promptster.dev` without `www`). The bare domain redirects and strips the Authorization header. --- ## Technical Details - **Endpoint:** `https://www.promptster.dev/mcp` - **Transport:** Streamable HTTP (stateless, no SSE) - **Auth:** Bearer token with `pk_live_*` or `pk_test_*` API keys - **Protocol:** [Model Context Protocol](https://modelcontextprotocol.io/) via `mcp-handler` --- ## Next Steps - **[API Overview](index.md)** β€” Full REST API documentation - **[Authentication](authentication.md)** β€” API key management - **[Test Endpoint](endpoints/test-prompt.md)** β€” REST API equivalent of `test_prompt` - **[Compare Endpoint](endpoints/compare-prompts.md)** β€” REST API equivalent of `compare_prompts`