{"openapi":"3.1.0","info":{"title":"Pricesaurus Watch API","version":"1.0.0","description":"Watch a product URL and get notified when the price drops. This is the canonical API for the app, the web, MCP tools, and SDKs.\n\nVersioning: the URL path is the version (`\/api\/v1`). Breaking changes ship a new path. Deprecated operations get `Deprecation` and `Sunset` response headers plus a note here at least 90 days before removal.","contact":{"name":"Pricesaurus","email":"alfonso@pricesaurus.com","url":"https:\/\/pricesaurus.com\/developers"}},"servers":[{"url":"\/api\/v1","description":"Version 1"}],"security":[{"bearerAuth":[]}],"tags":[{"name":"extract"},{"name":"products"},{"name":"alerts"}],"paths":{"\/me":{"get":{"tags":["account"],"operationId":"getMe","summary":"Current plan, product count, webhook status","responses":{"200":{"description":"Account","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/AccountResponse"}}}},"401":{"$ref":"#\/components\/responses\/Unauthorized"},"403":{"$ref":"#\/components\/responses\/Error"},"400":{"$ref":"#\/components\/responses\/Error"},"404":{"$ref":"#\/components\/responses\/NotFound"},"422":{"$ref":"#\/components\/responses\/Error"},"429":{"$ref":"#\/components\/responses\/Error"},"500":{"$ref":"#\/components\/responses\/Error"}}}},"\/alerts\/{id}":{"patch":{"tags":["alerts"],"operationId":"updateAlert","summary":"Rename or disable an alert","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"content":{"application\/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"is_active":{"type":"boolean"}}}}}},"responses":{"200":{"description":"Alert","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/AlertResponse"}}}},"401":{"$ref":"#\/components\/responses\/Unauthorized"},"403":{"$ref":"#\/components\/responses\/Error"},"400":{"$ref":"#\/components\/responses\/Error"},"404":{"$ref":"#\/components\/responses\/NotFound"},"422":{"$ref":"#\/components\/responses\/Error"},"429":{"$ref":"#\/components\/responses\/Error"},"500":{"$ref":"#\/components\/responses\/Error"}}},"delete":{"tags":["alerts"],"operationId":"deleteAlert","summary":"Delete a price alert","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"204":{"description":"Deleted"},"401":{"$ref":"#\/components\/responses\/Unauthorized"},"404":{"$ref":"#\/components\/responses\/Error"},"400":{"$ref":"#\/components\/responses\/Error"},"422":{"$ref":"#\/components\/responses\/Error"},"429":{"$ref":"#\/components\/responses\/Error"},"500":{"$ref":"#\/components\/responses\/Error"}}}},"\/webhook\/test":{"post":{"tags":["webhooks"],"operationId":"testWebhook","summary":"Queue a webhook.test event to the saved URL","responses":{"200":{"description":"Queued","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/WebhookTestResponse"}}}},"401":{"$ref":"#\/components\/responses\/Unauthorized"},"403":{"$ref":"#\/components\/responses\/Error"},"422":{"$ref":"#\/components\/responses\/Error"},"400":{"$ref":"#\/components\/responses\/Error"},"404":{"$ref":"#\/components\/responses\/NotFound"},"429":{"$ref":"#\/components\/responses\/Error"},"500":{"$ref":"#\/components\/responses\/Error"}}}},"\/extract":{"post":{"tags":["extract"],"operationId":"extractProduct","summary":"Read a product page without saving it","description":"Fetches the live title, price, currency, and availability for a product URL. Does not create a watch. Prefer this before POST \/products when you only need a snapshot.","parameters":[{"$ref":"#\/components\/parameters\/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/ExtractRequest"}}}},"responses":{"200":{"description":"Snapshot","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/SnapshotResponse"}}}},"401":{"$ref":"#\/components\/responses\/Unauthorized"},"422":{"$ref":"#\/components\/responses\/Error"},"503":{"$ref":"#\/components\/responses\/Error"},"400":{"$ref":"#\/components\/responses\/Error"},"404":{"$ref":"#\/components\/responses\/NotFound"},"429":{"$ref":"#\/components\/responses\/Error"},"500":{"$ref":"#\/components\/responses\/Error"}}}},"\/products":{"get":{"tags":["products"],"operationId":"listProducts","summary":"List watched products","description":"Newest first. Prefer `starting_after` (cursor) over `page` when paging as an agent.","parameters":[{"name":"per_page","in":"query","schema":{"type":"integer","minimum":1,"maximum":100}},{"name":"page","in":"query","schema":{"type":"integer","minimum":1}},{"name":"starting_after","in":"query","schema":{"type":"string","format":"uuid"},"description":"Cursor: UUID of the last product from the previous page."}],"responses":{"200":{"description":"Page of products","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/ProductListResponse"}}}},"401":{"$ref":"#\/components\/responses\/Unauthorized"},"400":{"$ref":"#\/components\/responses\/Error"},"404":{"$ref":"#\/components\/responses\/NotFound"},"422":{"$ref":"#\/components\/responses\/Error"},"429":{"$ref":"#\/components\/responses\/Error"},"500":{"$ref":"#\/components\/responses\/Error"}}},"post":{"tags":["products"],"operationId":"watchProduct","summary":"Start watching a product URL","description":"Creates a watched product from a store URL. The user never picks a store. Plus and Max only.","parameters":[{"$ref":"#\/components\/parameters\/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/WatchRequest"}}}},"responses":{"201":{"description":"Created","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/ProductResponse"}}}},"401":{"$ref":"#\/components\/responses\/Unauthorized"},"403":{"$ref":"#\/components\/responses\/Error"},"422":{"$ref":"#\/components\/responses\/Error"},"503":{"$ref":"#\/components\/responses\/Error"},"400":{"$ref":"#\/components\/responses\/Error"},"404":{"$ref":"#\/components\/responses\/NotFound"},"429":{"$ref":"#\/components\/responses\/Error"},"500":{"$ref":"#\/components\/responses\/Error"}}}},"\/products\/batch":{"post":{"tags":["products"],"operationId":"watchProductBatch","summary":"Watch many product URLs in one request","description":"Accepts up to 20 URLs. Successful watches are in data. Per-URL failures are in meta.errors. Does not stop the batch on a single bad URL.","parameters":[{"$ref":"#\/components\/parameters\/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application\/json":{"schema":{"type":"object","required":["urls"],"properties":{"urls":{"type":"array","minItems":1,"maxItems":20,"items":{"type":"string","format":"uri"}}}}}}},"responses":{"200":{"description":"Batch result","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/BatchResponse"}}}},"401":{"$ref":"#\/components\/responses\/Unauthorized"},"422":{"$ref":"#\/components\/responses\/Error"},"400":{"$ref":"#\/components\/responses\/Error"},"404":{"$ref":"#\/components\/responses\/NotFound"},"429":{"$ref":"#\/components\/responses\/Error"},"500":{"$ref":"#\/components\/responses\/Error"}}}},"\/products\/{id}":{"get":{"tags":["products"],"operationId":"getProduct","summary":"Read one watched product","description":"Current price, status, and permissions for a watched product.","parameters":[{"$ref":"#\/components\/parameters\/ProductId"}],"responses":{"200":{"content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/ProductResponse"}}}},"401":{"$ref":"#\/components\/responses\/Unauthorized"},"404":{"$ref":"#\/components\/responses\/Error"},"400":{"$ref":"#\/components\/responses\/Error"},"422":{"$ref":"#\/components\/responses\/Error"},"429":{"$ref":"#\/components\/responses\/Error"},"500":{"$ref":"#\/components\/responses\/Error"}}},"patch":{"tags":["products"],"operationId":"updateProduct","summary":"Rename or pause a watched product","description":"Patch name or is_active. Re-enabling counts against the plan product cap.","parameters":[{"$ref":"#\/components\/parameters\/ProductId"},{"$ref":"#\/components\/parameters\/IdempotencyKey"}],"requestBody":{"content":{"application\/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"is_active":{"type":"boolean"}}}}}},"responses":{"200":{"content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/ProductResponse"}}}},"401":{"$ref":"#\/components\/responses\/Unauthorized"},"403":{"$ref":"#\/components\/responses\/Error"},"404":{"$ref":"#\/components\/responses\/Error"},"400":{"$ref":"#\/components\/responses\/Error"},"422":{"$ref":"#\/components\/responses\/Error"},"429":{"$ref":"#\/components\/responses\/Error"},"500":{"$ref":"#\/components\/responses\/Error"}}},"delete":{"tags":["products"],"operationId":"deleteProduct","summary":"Stop watching a product","description":"Deletes the watch, its price history, and its alerts.","parameters":[{"$ref":"#\/components\/parameters\/ProductId"}],"responses":{"204":{"description":"Deleted"},"401":{"$ref":"#\/components\/responses\/Unauthorized"},"404":{"$ref":"#\/components\/responses\/Error"},"400":{"$ref":"#\/components\/responses\/Error"},"422":{"$ref":"#\/components\/responses\/Error"},"429":{"$ref":"#\/components\/responses\/Error"},"500":{"$ref":"#\/components\/responses\/Error"}}}},"\/products\/{id}\/checks":{"post":{"tags":["products"],"operationId":"checkProduct","summary":"Queue a price check","description":"Accepts the check and runs it on the queue. Poll GET \/products\/{id} until checked_at moves. Returns 202 Accepted with a Location header.","parameters":[{"$ref":"#\/components\/parameters\/ProductId"},{"$ref":"#\/components\/parameters\/IdempotencyKey"}],"responses":{"202":{"$ref":"#\/components\/responses\/AcceptedJob"},"401":{"$ref":"#\/components\/responses\/Unauthorized"},"404":{"$ref":"#\/components\/responses\/Error"},"422":{"$ref":"#\/components\/responses\/Error"},"503":{"$ref":"#\/components\/responses\/Error"},"400":{"$ref":"#\/components\/responses\/Error"},"429":{"$ref":"#\/components\/responses\/Error"},"500":{"$ref":"#\/components\/responses\/Error"}}}},"\/products\/{id}\/prices":{"get":{"tags":["products"],"operationId":"listPrices","summary":"Price history for a watched product","description":"Newest first. Offset pagination via page and per_page.","parameters":[{"$ref":"#\/components\/parameters\/ProductId"}],"responses":{"200":{"description":"Price history","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/PriceListResponse"}}}},"401":{"$ref":"#\/components\/responses\/Unauthorized"},"404":{"$ref":"#\/components\/responses\/Error"},"400":{"$ref":"#\/components\/responses\/Error"},"422":{"$ref":"#\/components\/responses\/Error"},"429":{"$ref":"#\/components\/responses\/Error"},"500":{"$ref":"#\/components\/responses\/Error"}}}},"\/products\/{id}\/alerts":{"get":{"tags":["alerts"],"operationId":"listAlerts","summary":"List alerts on a watched product","description":"All alerts for that product, newest first.","parameters":[{"$ref":"#\/components\/parameters\/ProductId"}],"responses":{"200":{"description":"Alerts","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/AlertListResponse"}}}},"401":{"$ref":"#\/components\/responses\/Unauthorized"},"404":{"$ref":"#\/components\/responses\/Error"},"400":{"$ref":"#\/components\/responses\/Error"},"422":{"$ref":"#\/components\/responses\/Error"},"429":{"$ref":"#\/components\/responses\/Error"},"500":{"$ref":"#\/components\/responses\/Error"}}},"post":{"tags":["alerts"],"operationId":"createAlert","summary":"Create a price alert","description":"condition drops (any fall), below, or above. below and above need threshold.","parameters":[{"$ref":"#\/components\/parameters\/ProductId"},{"$ref":"#\/components\/parameters\/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/AlertRequest"}}}},"responses":{"201":{"description":"Created","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/AlertResponse"}}}},"401":{"$ref":"#\/components\/responses\/Unauthorized"},"422":{"$ref":"#\/components\/responses\/Error"},"400":{"$ref":"#\/components\/responses\/Error"},"404":{"$ref":"#\/components\/responses\/NotFound"},"429":{"$ref":"#\/components\/responses\/Error"},"500":{"$ref":"#\/components\/responses\/Error"}}}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"Sanctum personal access token. See \/auth.md."}},"parameters":{"ProductId":{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},"IdempotencyKey":{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string","maxLength":255},"description":"Replay-safe key for POST, PUT, and PATCH. The same key on the same path returns the first successful response for 24 hours."}},"schemas":{"ExtractRequest":{"type":"object","required":["url"],"properties":{"url":{"type":"string","format":"uri"},"country":{"type":"string","minLength":2,"maxLength":2},"currency":{"type":"string","minLength":3,"maxLength":3}}},"WatchRequest":{"type":"object","required":["url"],"properties":{"url":{"type":"string","format":"uri"},"name":{"type":"string"},"country":{"type":"string"},"currency":{"type":"string"}}},"AlertRequest":{"type":"object","required":["condition"],"properties":{"condition":{"type":"string","enum":["drops","below","above"]},"threshold":{"type":"number"},"name":{"type":"string"}}},"Snapshot":{"type":"object","properties":{"url":{"type":"string"},"canonical_url":{"type":"string"},"name":{"type":["string","null"]},"price":{"type":["number","null"]},"currency":{"type":["string","null"]},"availability":{"type":"string","enum":["in_stock","out_of_stock","unknown"]},"image_url":{"type":["string","null"]},"store_name":{"type":["string","null"]},"driver":{"type":"string"},"contract":{"type":["string","null"]},"credits_used":{"type":["integer","null"]}}},"Product":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"url":{"type":"string"},"canonical_url":{"type":"string"},"name":{"type":"string"},"price":{"type":["number","null"]},"currency":{"type":["string","null"]},"status":{"type":"string","enum":["watching","pending","failing"]},"is_failing":{"type":"boolean"},"is_active":{"type":"boolean"},"crawler":{"type":["string","null"]},"can":{"type":"object","properties":{"add_alert":{"type":"boolean"},"update":{"type":"boolean"},"delete":{"type":"boolean"}}}}},"Error":{"type":"object","required":["code","message"],"additionalProperties":false,"properties":{"code":{"type":"string","description":"Stable machine code. Common values: unauthenticated, not_found, plan_required, no_price, unavailable, invalid.","examples":["unauthenticated","not_found","plan_required","no_price","unavailable","invalid"]},"message":{"type":"string"},"retryable":{"type":"boolean"},"driver":{"type":["string","null"]}},"examples":[{"code":"unauthorized","message":"Missing or invalid Bearer token.","retryable":false},{"code":"not_found","message":"Product not found.","retryable":false},{"code":"validation_failed","message":"The url field is required.","retryable":false}]},"SnapshotResponse":{"type":"object","properties":{"data":{"$ref":"#\/components\/schemas\/Snapshot"},"meta":{"type":"object"}}},"ProductResponse":{"type":"object","properties":{"data":{"$ref":"#\/components\/schemas\/Product"}}},"ProductListResponse":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#\/components\/schemas\/Product"}},"meta":{"type":"object","properties":{"page":{"type":"integer"},"per_page":{"type":"integer"},"total":{"type":"integer"},"has_more":{"type":"boolean"},"next_starting_after":{"type":["string","null"],"format":"uuid"}}}}},"JobResponse":{"type":"object","required":["data","job_id","status"],"properties":{"job_id":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["queued","complete","failed"]},"data":{"type":"object","required":["id","job_id","status","poll"],"properties":{"id":{"type":"string","format":"uuid"},"job_id":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["queued","complete","failed"]},"poll":{"type":"string","format":"uri"}}}}},"BatchResponse":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#\/components\/schemas\/Product"}},"meta":{"type":"object","properties":{"errors":{"type":"array","items":{"type":"object"}}}}}},"AccountResponse":{"type":"object","properties":{"data":{"type":"object","properties":{"plan":{"type":"string"},"products":{"type":"integer"},"webhook_url":{"type":["string","null"]}}}}},"Alert":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":["string","null"]},"condition":{"type":"string","enum":["drops","below","above"]},"threshold":{"type":["number","null"]},"is_active":{"type":"boolean"}}},"AlertResponse":{"type":"object","properties":{"data":{"$ref":"#\/components\/schemas\/Alert"}}},"AlertListResponse":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#\/components\/schemas\/Alert"}}}},"PricePoint":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"amount":{"type":"number"},"currency":{"type":"string"},"checked_at":{"type":"string","format":"date-time"}}},"PriceListResponse":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#\/components\/schemas\/PricePoint"}}}},"WebhookTestResponse":{"type":"object","properties":{"data":{"type":"object","properties":{"queued":{"type":"boolean"}}}}}},"responses":{"Unauthorized":{"description":"Missing or invalid Bearer token","headers":{"WWW-Authenticate":{"schema":{"type":"string"}},"RateLimit-Limit":{"schema":{"type":"string"}},"RateLimit-Remaining":{"schema":{"type":"string"}}},"content":{"application\/json":{"schema":{"type":"object","properties":{"error":{"$ref":"#\/components\/schemas\/Error"}}}}}},"Error":{"description":"The request failed. Read error.code.","content":{"application\/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"$ref":"#\/components\/schemas\/Error"}}}}}},"NotFound":{"description":"The product, alert, or path does not exist","content":{"application\/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"$ref":"#\/components\/schemas\/Error"}}},"example":{"error":{"code":"not_found","message":"Product not found.","retryable":false}}}}},"AcceptedJob":{"description":"Queued. Poll the Location header or the product resource.","headers":{"Location":{"schema":{"type":"string","format":"uri"}}},"content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/JobResponse"}}}}}}}