# InstaRecipe API Documentation This document describes the InstaRecipe API endpoints for the async queue-based recipe extraction system. ## Base URL All API endpoints are relative to your InstaRecipe instance: ``` https://your-instarecipe-instance.com/api ``` ## Authentication Currently, no authentication is required for API access. This may change in future versions. ## Content Type All requests should use `Content-Type: application/json` unless otherwise specified. ## Error Handling All endpoints return standardized error responses: ```json { "error": "Error type", "message": "Human-readable error message", "details": { /* Additional error context */ } } ``` HTTP status codes follow REST conventions: - `200` - Success - `201` - Created - `400` - Bad Request (invalid input) - `404` - Not Found - `409` - Conflict (e.g., cannot retry pending item) - `410` - Gone (deprecated endpoint) - `500` - Internal Server Error ## Queue Management Endpoints ### POST /api/queue Enqueue an Instagram URL for async processing. **Request:** ```json { "url": "https://instagram.com/p/abc123" } ``` **Supported URL Formats:** - Posts: `https://instagram.com/p/{post-id}` - Posts (www): `https://www.instagram.com/p/{post-id}` - Reels: `https://instagram.com/reel/{reel-id}` - IGTV: `https://instagram.com/tv/{video-id}` - With query parameters: `https://instagram.com/reel/{reel-id}?utm_source=share` **URL Requirements:** - Must use HTTPS protocol - Hostname must be `instagram.com` or `www.instagram.com` - Any Instagram path is accepted (posts, reels, IGTV, etc.) - Query parameters and hash fragments are allowed **Examples:** ```json // Post URL { "url": "https://instagram.com/p/ABC123" } // Reel URL with tracking { "url": "https://www.instagram.com/reel/DSevV5CDcNm/?utm_source=ig_web_copy_link" } // IGTV URL { "url": "https://instagram.com/tv/XYZ789" } ``` **Response (201 Created):** ```json { "id": "550e8400-e29b-41d4-a716-446655440000", "url": "https://instagram.com/reel/DSevV5CDcNm/?utm_source=ig_web_copy_link", "status": "pending", "phases": [ { "name": "extraction", "status": "pending", "progress": 0 }, { "name": "parsing", "status": "pending", "progress": 0 }, { "name": "uploading", "status": "pending", "progress": 0 } ], "createdAt": "2024-12-21T10:30:00Z", "updatedAt": "2024-12-21T10:30:00Z" } ``` **Errors:** - `400` - Invalid URL format (not a valid URL) - `400` - URL must use HTTPS protocol - `400` - URL must be from instagram.com domain - `400` - Missing or invalid URL parameter ### GET /api/queue List queue items with optional filtering, pagination, and sorting. **Query Parameters:** - `status` (optional): Filter by status (`pending`, `in_progress`, `success`, `error`, `unhealthy`) - `limit` (optional): Number of items to return (default: 50, max: 100) - `offset` (optional): Number of items to skip (default: 0) - `sort` (optional): Sort field (`createdAt`, `updatedAt`, `status`) (default: `createdAt`) - `order` (optional): Sort order (`asc`, `desc`) (default: `desc`) **Examples:** ```bash GET /api/queue # All items GET /api/queue?status=error # Failed items only GET /api/queue?limit=10&offset=20 # Pagination GET /api/queue?sort=status&order=asc # Sort by status ``` **Response (200 OK):** ```json { "items": [ { "id": "550e8400-e29b-41d4-a716-446655440000", "url": "https://instagram.com/p/abc123", "status": "success", "phases": [ { "name": "extraction", "status": "completed", "startedAt": "2024-12-21T10:30:01Z", "completedAt": "2024-12-21T10:30:15Z", "progress": 100 }, { "name": "parsing", "status": "completed", "startedAt": "2024-12-21T10:30:15Z", "completedAt": "2024-12-21T10:30:25Z", "progress": 100 }, { "name": "uploading", "status": "completed", "startedAt": "2024-12-21T10:30:25Z", "completedAt": "2024-12-21T10:30:30Z", "progress": 100 } ], "results": { "recipe": { "name": "Chocolate Chip Cookies", "description": "Delicious homemade cookies", "servings": 24, "ingredients": [ { "food": "flour", "amount": 2.25, "unit": "cups" } ], "steps": [ { "instruction": "Preheat oven to 375°F", "time": 5 } ], "keywords": ["cookies", "dessert", "chocolate"], "image": "https://instagram.com/image.jpg" }, "tandoorUrl": "https://tandoor.example.com/recipe/123", "extractedText": "Raw extracted text...", "thumbnail": "https://instagram.com/thumbnail.jpg" }, "createdAt": "2024-12-21T10:30:00Z", "updatedAt": "2024-12-21T10:30:30Z" } ], "total": 42, "hasMore": true } ``` ### GET /api/queue/{id} Get details for a specific queue item. **Path Parameters:** - `id`: Queue item UUID **Response (200 OK):** Returns the same queue item structure as in the list response. **Errors:** - `400` - Invalid UUID format - `404` - Queue item not found ### POST /api/queue/{id}/retry Retry a failed queue item. Only items with `error` or `unhealthy` status can be retried. **Path Parameters:** - `id`: Queue item UUID **Response (200 OK):** ```json { "success": true, "message": "Item queued for retry", "item": { "id": "550e8400-e29b-41d4-a716-446655440000", "status": "pending", "updatedAt": "2024-12-21T11:00:00Z" } } ``` **Errors:** - `400` - Invalid UUID format - `404` - Queue item not found - `409` - Cannot retry item with current status (only `error` and `unhealthy` can be retried) ## Real-time Updates ### GET /api/queue/stream Server-Sent Events (SSE) endpoint for real-time queue updates. **Query Parameters:** - `itemId` (optional): Filter updates for specific item - `status` (optional): Filter updates by status **Headers:** ``` Accept: text/event-stream Cache-Control: no-cache ``` **Response:** SSE stream with the following event types: #### connection Sent when connection is established: ``` event: connection data: {"message": "Connected to queue stream", "timestamp": "2024-12-21T10:30:00Z"} ``` #### queue-update Sent when queue item status changes: ``` event: queue-update data: { "itemId": "550e8400-e29b-41d4-a716-446655440000", "status": "in_progress", "timestamp": "2024-12-21T10:30:01Z", "progress": [ { "name": "extraction", "status": "in_progress", "startedAt": "2024-12-21T10:30:01Z", "progress": 45 } ] } ``` #### ping Keep-alive ping sent every 30 seconds: ``` event: ping data: {"timestamp": "2024-12-21T10:30:30Z"} ``` **Usage Examples:** **JavaScript:** ```javascript const eventSource = new EventSource('/api/queue/stream'); eventSource.addEventListener('connection', (event) => { console.log('Connected:', JSON.parse(event.data)); }); eventSource.addEventListener('queue-update', (event) => { const update = JSON.parse(event.data); console.log('Queue update:', update); updateUI(update); }); eventSource.addEventListener('ping', (event) => { console.log('Keep-alive ping'); }); eventSource.onerror = (error) => { console.error('SSE error:', error); // Reconnect logic here }; ``` **curl:** ```bash curl -N -H "Accept: text/event-stream" \ "https://your-instance.com/api/queue/stream?itemId=550e8400-e29b-41d4-a716-446655440000" ``` ## Push Notifications ### GET /api/notifications/vapid-key Get the VAPID public key required for push notification subscriptions. **Response (200 OK):** ```json { "publicKey": "BDummyPublicKeyForDevelopment...", "applicationServerKey": "BDummyPublicKeyForDevelopment..." } ``` ### POST /api/notifications/subscribe Subscribe to push notifications for queue processing updates. **Request:** ```json { "subscription": { "endpoint": "https://fcm.googleapis.com/fcm/send/...", "keys": { "p256dh": "BIBn3E_YUVpW5f6_Eq_GH...", "auth": "tBiH_Y1nPSuVh7TRMhcf..." } }, "clientId": "unique-client-identifier" } ``` **Response (200 OK):** ```json { "success": true, "message": "Successfully subscribed to push notifications", "subscriptionCount": 5 } ``` **Errors:** - `400` - Invalid subscription object or missing clientId ### DELETE /api/notifications/subscribe Unsubscribe from push notifications. **Request:** ```json { "clientId": "unique-client-identifier" } ``` **Response (200 OK):** ```json { "success": true, "message": "Successfully unsubscribed from push notifications", "subscriptionCount": 4 } ``` ## Legacy Endpoints (Deprecated) ### POST /api/extract ⚠️ DEPRECATED **Status:** `410 Gone` This synchronous extraction endpoint is deprecated. Use `POST /api/queue` instead. **Migration:** ```javascript // ❌ Old synchronous approach const response = await fetch('/api/extract', { method: 'POST', body: JSON.stringify({ url }) }); const result = await response.json(); // Wait 30-60 seconds // ✅ New async queue approach const response = await fetch('/api/queue', { method: 'POST', body: JSON.stringify({ url }) }); const queueItem = await response.json(); // Immediate response ``` ### POST /api/extract-stream ⚠️ DEPRECATED **Status:** `410 Gone` This SSE endpoint is deprecated. Use `POST /api/queue` + `GET /api/queue/stream` instead. **Migration:** ```javascript // ❌ Old approach const response = await fetch('/api/extract-stream', { method: 'POST', body: JSON.stringify({ url }) }); // ✅ New approach const queueResponse = await fetch('/api/queue', { method: 'POST', body: JSON.stringify({ url }) }); const item = await queueResponse.json(); const eventSource = new EventSource(`/api/queue/stream?itemId=${item.id}`); ``` ## Data Models ### QueueItem ```typescript interface QueueItem { id: string; // UUID v4 url: string; // Instagram URL status: 'pending' | 'in_progress' | 'success' | 'error' | 'unhealthy'; phases: Array<{ name: 'extraction' | 'parsing' | 'uploading'; status: 'pending' | 'in_progress' | 'completed' | 'error'; startedAt?: string; // ISO 8601 timestamp completedAt?: string; // ISO 8601 timestamp progress?: number; // 0-100 }>; results?: { recipe?: Recipe; // Structured recipe data tandoorUrl?: string; // Tandoor recipe URL extractedText?: string; // Raw extracted text thumbnail?: string; // Image URL }; error?: string; // Error message createdAt: string; // ISO 8601 timestamp updatedAt: string; // ISO 8601 timestamp } ``` ### Recipe ```typescript interface Recipe { name: string; description?: string; servings?: number; prepTime?: number; // Minutes cookTime?: number; // Minutes totalTime?: number; // Minutes ingredients: Array<{ food: string; amount?: number; unit?: string; }>; steps: Array<{ instruction: string; time?: number; // Minutes }>; keywords?: string[]; // Recipe tags image?: string; // Image URL nutrition?: { // Nutritional information calories?: number; protein?: number; carbs?: number; fat?: number; }; } ``` ## Rate Limiting Currently, no rate limiting is enforced, but this may change in future versions. Consider implementing client-side rate limiting to avoid overwhelming the service. ## WebSocket Alternative For applications that cannot use Server-Sent Events, consider polling the `GET /api/queue/{id}` endpoint every 5-10 seconds for status updates. ## Error Recovery When implementing clients, consider these error recovery strategies: 1. **Network Errors**: Retry with exponential backoff 2. **SSE Connection Drops**: Automatically reconnect after 5-10 seconds 3. **Queue Item Failures**: Present retry option to users 4. **Push Notification Failures**: Gracefully degrade to polling ## Examples ### Complete Processing Workflow ```javascript async function processInstagramUrl(url) { try { // 1. Enqueue URL const queueResponse = await fetch('/api/queue', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ url }) }); const queueItem = await queueResponse.json(); console.log('Enqueued:', queueItem.id); // 2. Listen for real-time updates const eventSource = new EventSource(`/api/queue/stream?itemId=${queueItem.id}`); return new Promise((resolve, reject) => { eventSource.addEventListener('queue-update', (event) => { const update = JSON.parse(event.data); if (update.status === 'success') { eventSource.close(); resolve(update.results); } else if (update.status === 'error') { eventSource.close(); reject(new Error(update.error)); } // Handle progress updates console.log('Progress:', update.progress); }); eventSource.onerror = (error) => { eventSource.close(); reject(error); }; }); } catch (error) { console.error('Processing failed:', error); throw error; } } // Usage processInstagramUrl('https://instagram.com/p/abc123') .then((results) => { console.log('Recipe extracted:', results.recipe); if (results.tandoorUrl) { console.log('Uploaded to Tandoor:', results.tandoorUrl); } }) .catch((error) => { console.error('Extraction failed:', error.message); }); ``` For more examples and integration guides, see the [Migration Documentation](/docs/MIGRATION.md).