{"openapi":"3.1.0","info":{"title":"PriceTrack API","version":"1.0.0","description":"The source of truth for SaaS pricing. Historical pricing, change alerts, and a REST API for every tracked SaaS product.\n\n## Authentication\nAll `/v1/*` endpoints require `Authorization: Bearer pt_<your-key>`. Create a key in the [developer dashboard](/dashboard/developer/keys).\n\n## Rate limits\n| Tier | Per minute | Per day |\n|---|---|---|\n| Free | 10 | 100 |\n| Pro | 100 | 10,000 |\n| Enterprise | 1,000 | 100,000 |\n\nEvery response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` for the minute window. When the daily cap is finite, you also get `X-RateLimit-Daily-Limit` and `X-RateLimit-Daily-Remaining`. A 429 from either includes `Retry-After`; the daily one is seconds-until-midnight-UTC.","contact":{"name":"PriceTrack","url":"https://pricetrack.dev"},"license":{"name":"Commercial","url":"https://pricetrack.dev/terms"}},"servers":[{"url":"https://api.pricetrack.dev","description":"Production"},{"url":"http://localhost:3001","description":"Local dev"}],"security":[{"bearerAuth":[]}],"tags":[{"name":"Products","description":"Browse and fetch product pricing."},{"name":"Categories","description":"Product categorization."},{"name":"Compare","description":"Side-by-side comparison."},{"name":"Alerts","description":"Notifications on price changes."},{"name":"Webhooks","description":"HTTP callbacks for events."},{"name":"Trends","description":"Market-level pricing analytics."},{"name":"Search","description":"Unified autocomplete search."},{"name":"Account","description":"Caller's tier, limits, and usage."}],"paths":{"/v1/me":{"get":{"tags":["Account"],"summary":"Caller's tier, limits, and today's usage","description":"Returns the API key's tier, per-minute/per-day limits, today's usage (broken down by endpoint), and month-to-date totals. Cheap call — two indexed reads, no joins. Useful for integrations that want to surface remaining quota in their own UI without scraping the X-RateLimit headers.","responses":{"200":{"description":"Account snapshot","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"key":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","nullable":true},"prefix":{"type":"string","nullable":true},"createdAt":{"type":"string","format":"date-time","nullable":true},"lastUsedAt":{"type":"string","format":"date-time","nullable":true}}},"tier":{"type":"string","enum":["free","pro","enterprise"]},"limits":{"type":"object","properties":{"requestsPerMinute":{"type":"integer"},"requestsPerDay":{"type":"integer"},"historyDepthDays":{"type":"integer"},"maxWebhooks":{"type":"integer","nullable":true},"maxAlerts":{"type":"integer","nullable":true,"description":"null = unlimited (enterprise tier)."}}},"usage":{"type":"object","properties":{"date":{"type":"string","format":"date"},"usedToday":{"type":"integer"},"remainingToday":{"type":"integer"},"usedThisMonth":{"type":"integer"},"byEndpoint":{"type":"array","items":{"type":"object","properties":{"endpoint":{"type":"string"},"count":{"type":"integer"}}}}}},"account":{"type":"object","description":"Account-scoped counts — how many of the tier's alert/webhook slots are in use.","properties":{"activeAlerts":{"type":"integer"},"activeWebhooks":{"type":"integer"},"savedProducts":{"type":"integer"},"alertSlotsRemaining":{"type":"integer","nullable":true,"description":"null = unlimited slots (enterprise tier)."},"webhookSlotsRemaining":{"type":"integer","nullable":true}}}}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/v1/products":{"get":{"tags":["Products"],"summary":"List products","description":"Paginated, optionally filtered by full-text query or category.","parameters":[{"name":"q","in":"query","schema":{"type":"string"},"description":"Search term"},{"name":"category","in":"query","schema":{"type":"string"},"description":"Category slug"},{"name":"page","in":"query","schema":{"type":"integer","minimum":1,"default":1}},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":100,"default":20}}],"responses":{"200":{"description":"Product list","content":{"application/json":{"schema":{"type":"object","required":["data","pagination"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Product"}},"pagination":{"$ref":"#/components/schemas/Pagination"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/products/{slug}":{"get":{"tags":["Products"],"summary":"Get one product","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Product detail","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Product"}}}}}},"404":{"$ref":"#/components/responses/NotFound"}}}},"/v1/products/{slug}/history":{"get":{"tags":["Products"],"summary":"Price history","description":"Complete price-change log for a product, newest first. On Free tier history is limited to the last 6 months.","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}},{"name":"from","in":"query","schema":{"type":"string","format":"date"}},{"name":"to","in":"query","schema":{"type":"string","format":"date"}},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":500,"default":100}}],"responses":{"200":{"description":"Price history entries","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/PriceChange"}}}}}}}}}},"/v1/products/{slug}/history.csv":{"get":{"tags":["Products"],"summary":"Price history as CSV","description":"Pro and Enterprise tiers only. Returns a comma-separated file with the same fields as the JSON history endpoint.","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}},{"name":"from","in":"query","schema":{"type":"string","format":"date"}},{"name":"to","in":"query","schema":{"type":"string","format":"date"}},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":50000,"default":5000}}],"responses":{"200":{"description":"CSV file","content":{"text/csv":{"schema":{"type":"string"},"example":"detected_at,plan,billing_period,change_type,old_price,new_price,change_percentage,source\n2026-03-14T00:00:00Z,Plus,monthly,increase,10,12,20,scrape"}}},"402":{"$ref":"#/components/responses/UpgradeRequired"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/v1/categories":{"get":{"tags":["Categories"],"summary":"List categories","responses":{"200":{"description":"All categories, sorted by name","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Category"}}}}}}}}}},"/v1/categories/{slug}/products":{"get":{"tags":["Categories"],"summary":"Products in a category","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}},{"name":"page","in":"query","schema":{"type":"integer","default":1}},{"name":"limit","in":"query","schema":{"type":"integer","default":20,"maximum":100}}],"responses":{"200":{"description":"Products belonging to the category","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Product"}},"category":{"$ref":"#/components/schemas/Category"}}}}}}}}},"/v1/compare":{"get":{"tags":["Compare"],"summary":"Compare 2–4 products","parameters":[{"name":"slugs","in":"query","required":true,"schema":{"type":"string"},"description":"Comma-separated product slugs (2–4).","example":"notion,linear,asana"}],"responses":{"200":{"description":"Normalized side-by-side comparison","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"products":{"type":"array","items":{"$ref":"#/components/schemas/Product"}}}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"}}}},"/v1/alerts":{"get":{"tags":["Alerts"],"summary":"List your alerts","responses":{"200":{"description":"Alerts + tier limits","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Alert"}},"limits":{"type":"object","properties":{"used":{"type":"integer"},"max":{"type":"integer"}}}}}}}}}},"post":{"tags":["Alerts"],"summary":"Create an alert","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateAlertBody"}}}},"responses":{"201":{"description":"Alert created","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","format":"uuid"}}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"402":{"$ref":"#/components/responses/LimitReached"}}}},"/v1/alerts/{id}":{"delete":{"tags":["Alerts"],"summary":"Delete an alert","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Deleted","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"deleted":{"type":"boolean"}}}}}}}},"404":{"$ref":"#/components/responses/NotFound"}}}},"/v1/webhooks":{"get":{"tags":["Webhooks"],"summary":"List your webhook endpoints","responses":{"200":{"description":"Webhooks + tier limits","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Webhook"}},"limits":{"type":"object","properties":{"used":{"type":"integer"},"max":{"type":"integer"}}}}}}}}}},"post":{"tags":["Webhooks"],"summary":"Register a webhook","description":"The `secret` is returned **once** in the response — store it, we don't. Incoming deliveries are signed as `x-pricetrack-signature: sha256=<hmac(secret, timestamp + '.' + body)>`.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateWebhookBody"}}}},"responses":{"201":{"description":"Webhook created; `secret` returned once.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"url":{"type":"string"},"secret":{"type":"string","description":"HMAC signing secret. Shown once only."},"events":{"type":"array","items":{"$ref":"#/components/schemas/WebhookEventName"}}}}}}}}},"402":{"$ref":"#/components/responses/LimitReached"}}}},"/v1/webhooks/{id}":{"delete":{"tags":["Webhooks"],"summary":"Delete a webhook endpoint","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Deleted","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"deleted":{"type":"boolean"}}}}}}}}}}},"/v1/search":{"get":{"tags":["Search"],"summary":"Unified search across products, categories, and alternatives","parameters":[{"name":"q","in":"query","required":true,"schema":{"type":"string","minLength":1}},{"name":"limit","in":"query","schema":{"type":"integer","default":10,"maximum":25}}],"responses":{"200":{"description":"Search hits","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"hits":{"type":"array","items":{"type":"object","required":["type","slug","name"],"properties":{"type":{"type":"string","enum":["product","category","alternatives"]},"slug":{"type":"string"},"name":{"type":"string"},"description":{"type":"string","nullable":true},"logoUrl":{"type":"string","nullable":true}}}}}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/trends":{"get":{"tags":["Trends"],"summary":"Market-level pricing statistics","parameters":[{"name":"category","in":"query","schema":{"type":"string"},"description":"Filter by category slug"}],"responses":{"200":{"description":"Per-category trend stats","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/TrendStat"}}}}}}}}}},"/v1/changes":{"get":{"tags":["Changes"],"summary":"Recent price changes across the catalog","description":"Paginated feed of detected price changes with the same filters as /changes on the website. Mirrors what the public RSS/JSON feeds expose, plus ApiKey auth and rate limiting.","parameters":[{"name":"direction","in":"query","schema":{"type":"string","enum":["all","increase","decrease","new","removed"]},"description":"Filter by change direction. Default `all`."},{"name":"window","in":"query","schema":{"type":"string","enum":["24h","7d","30d","90d","all"]},"description":"Time window since detectedAt. Default `30d`."},{"name":"category","in":"query","schema":{"type":"string"},"description":"Filter by category slug."},{"name":"page","in":"query","schema":{"type":"integer","minimum":1,"default":1}},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":100,"default":30}}],"responses":{"200":{"description":"Paginated list of price-change events","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ChangeEvent"}},"pagination":{"type":"object","properties":{"page":{"type":"integer"},"limit":{"type":"integer"},"total":{"type":"integer"},"totalPages":{"type":"integer"}}},"filters":{"type":"object","properties":{"direction":{"type":"string"},"window":{"type":"string"},"category":{"type":"string","nullable":true}}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"}}}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"pt_<64 hex>","description":"Create a key at /dashboard/developer/keys. Format: `pt_` followed by 64 hex characters."}},"schemas":{"Product":{"type":"object","required":["slug","name","verified","plans"],"properties":{"slug":{"type":"string","example":"notion"},"name":{"type":"string","example":"Notion"},"description":{"type":"string","nullable":true},"websiteUrl":{"type":"string","nullable":true,"format":"uri"},"pricingUrl":{"type":"string","nullable":true,"format":"uri"},"logoUrl":{"type":"string","nullable":true,"format":"uri"},"verified":{"type":"boolean"},"category":{"type":"object","nullable":true,"properties":{"slug":{"type":"string"},"name":{"type":"string"}}},"plans":{"type":"array","items":{"$ref":"#/components/schemas/Plan"}}}},"Plan":{"type":"object","required":["name","billingPeriod","currency","isFree","isCustom"],"properties":{"name":{"type":"string","example":"Business"},"billingPeriod":{"type":"string","enum":["monthly","yearly","one_time","usage_based","custom"]},"priceAmount":{"type":"number","nullable":true},"currency":{"type":"string","example":"USD"},"priceDisplay":{"type":"string","nullable":true},"isFree":{"type":"boolean"},"isCustom":{"type":"boolean"},"features":{"type":"array","items":{"$ref":"#/components/schemas/Feature"}}}},"Feature":{"type":"object","properties":{"name":{"type":"string"},"value":{"type":"string","nullable":true},"category":{"type":"string","nullable":true}}},"PriceChange":{"type":"object","properties":{"plan":{"type":"object","properties":{"name":{"type":"string"},"billingPeriod":{"type":"string"}}},"oldPrice":{"type":"object","properties":{"amount":{"type":"number","nullable":true},"currency":{"type":"string"}}},"newPrice":{"type":"object","properties":{"amount":{"type":"number","nullable":true},"currency":{"type":"string"}}},"changeType":{"type":"string","enum":["increase","decrease","new_plan","removed_plan","restructure"]},"changePercentage":{"type":"number","nullable":true},"detectedAt":{"type":"string","format":"date-time"},"source":{"type":"string","enum":["scrape","manual","api"]}}},"Category":{"type":"object","properties":{"slug":{"type":"string"},"name":{"type":"string"},"description":{"type":"string","nullable":true}}},"Pagination":{"type":"object","required":["page","limit","total","totalPages"],"properties":{"page":{"type":"integer"},"limit":{"type":"integer"},"total":{"type":"integer"},"totalPages":{"type":"integer"}}},"Alert":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"product":{"type":"object","properties":{"slug":{"type":"string"},"name":{"type":"string"}}},"alertType":{"type":"string","enum":["any_change","increase","decrease","threshold","price_above","price_below"]},"thresholdPercentage":{"type":"integer","nullable":true},"targetPrice":{"type":"number","nullable":true,"description":"Dollar threshold for price_above / price_below alerts. Fires on the crossing, not while the price remains on that side."},"customLabel":{"type":"string","nullable":true,"maxLength":120,"description":"Short note shown in the dashboard and included in matching email subjects and webhook payloads."},"deliveryMethod":{"type":"string","enum":["email","webhook","both"]},"webhookUrl":{"type":"string","nullable":true},"isActive":{"type":"boolean"},"lastTriggeredAt":{"type":"string","format":"date-time","nullable":true},"createdAt":{"type":"string","format":"date-time"}}},"CreateAlertBody":{"type":"object","required":["productSlug","alertType"],"properties":{"productSlug":{"type":"string"},"alertType":{"type":"string","enum":["any_change","increase","decrease","threshold","price_above","price_below"]},"thresholdPercentage":{"type":"integer","minimum":1,"maximum":100},"targetPrice":{"type":"number","minimum":0},"customLabel":{"type":"string","maxLength":120},"deliveryMethod":{"type":"string","enum":["email","webhook","both"],"default":"email"},"webhookUrl":{"type":"string","format":"uri"}}},"Webhook":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"url":{"type":"string","format":"uri"},"events":{"type":"array","items":{"$ref":"#/components/schemas/WebhookEventName"}},"isActive":{"type":"boolean"},"lastDeliveredAt":{"type":"string","format":"date-time","nullable":true},"createdAt":{"type":"string","format":"date-time"}}},"CreateWebhookBody":{"type":"object","required":["url"],"properties":{"url":{"type":"string","format":"uri"},"events":{"type":"array","items":{"$ref":"#/components/schemas/WebhookEventName"},"default":["price.changed"]}}},"WebhookEventName":{"type":"string","enum":["price.changed","plan.added","plan.removed"]},"TrendStat":{"type":"object","properties":{"category":{"type":"object","properties":{"slug":{"type":"string"},"name":{"type":"string"}}},"products":{"type":"integer"},"plans":{"type":"integer"},"pricing":{"type":"object","properties":{"average":{"type":"number"},"median":{"type":"number"},"min":{"type":"number"},"max":{"type":"number"},"freePlans":{"type":"integer"}}},"changes":{"type":"object","properties":{"total":{"type":"integer"},"increases":{"type":"integer"},"decreases":{"type":"integer"}}}}},"ChangeEvent":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"product":{"type":"object","properties":{"slug":{"type":"string"},"name":{"type":"string"}}},"category":{"type":"object","nullable":true,"properties":{"slug":{"type":"string"},"name":{"type":"string"}}},"plan":{"type":"string","nullable":true},"currency":{"type":"string","description":"ISO 4217 currency code, default USD."},"oldPrice":{"type":"number","nullable":true},"newPrice":{"type":"number","nullable":true},"changeType":{"type":"string","enum":["increase","decrease","new_plan","removed_plan","restructure"]},"changePercentage":{"type":"number","nullable":true},"detectedAt":{"type":"string","format":"date-time"}}},"Error":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}},"responses":{"Unauthorized":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"BadRequest":{"description":"Invalid parameters","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"LimitReached":{"description":"Tier limit reached — upgrade to continue","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpgradeRequired":{"description":"Feature requires a higher tier","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Rate limit exceeded","headers":{"Retry-After":{"schema":{"type":"integer"},"description":"Seconds to wait"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}