{"openapi":"3.0.3","info":{"title":"ShopSavvy Data API","version":"1.3.0","description":"Product details, current prices, price history, deals, and expert reviews for millions of products across tens of thousands of retailers. Look up by barcode, UPC/EAN, ASIN, model number, MPN, URL, or product name. JSON or CSV responses, with official SDKs in 10 languages.\n\nAuthentication: send your API key as `Authorization: Bearer ss_live_...`. Every response reports `credits_used`, `credits_remaining` and `rate_limit_remaining` in `meta`; each account can make 1,000 requests per rolling hour. Errors never cost credits. Each operation's `x-credit-cost` states what it charges.\n\nDocumentation: https://shopsavvy.com/data/documentation · Errors: https://shopsavvy.com/data/documentation/errors · Changelog: https://shopsavvy.com/data/changelog","contact":{"email":"support@shopsavvy.com","url":"https://shopsavvy.com/data"},"termsOfService":"https://shopsavvy.com/terms-of-use"},"externalDocs":{"description":"ShopSavvy Data API documentation","url":"https://shopsavvy.com/data/documentation"},"servers":[{"url":"https://api.shopsavvy.com/v1","description":"Production"}],"security":[{"BearerAuth":[]}],"components":{"securitySchemes":{"BearerAuth":{"type":"http","scheme":"bearer","description":"API key from shopsavvy.com/data/api-keys (format `ss_live_` + 32 characters). An `api_key` query parameter is also accepted."}},"responses":{"BadRequest":{"description":"`ERR_INVALID_PARAMS` (a parameter is malformed or over a limit, e.g. more than 100 identifiers or a history window over 366 days) or `ERR_IDENTIFIER_REQUIRED` (no identifiers in `ids`). No credits charged.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"Unauthorized":{"description":"`ERR_AUTH_MISSING`, `ERR_AUTH_INVALID` or `ERR_AUTH_REVOKED`: no key, a malformed key, or a revoked key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"Forbidden":{"description":"`ERR_SUBSCRIPTION_REQUIRED` (no Data API plan) or `ERR_SUBSCRIPTION_INACTIVE` (plan canceled or unpaid past the grace window).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"ProductNotFound":{"description":"`ERR_PRODUCT_NOT_FOUND`: none of the identifiers matched a product. The message says what was tried. No credits charged.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"NotFound":{"description":"`ERR_ENDPOINT_NOT_FOUND`: the batch job or webhook does not exist on this account.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"TooManyRequests":{"description":"`ERR_RATE_LIMIT` (more than 1,000 requests in the rolling hour; wait `Retry-After` seconds) or `ERR_CREDITS_EXHAUSTED` (the month's credits are used up; no `Retry-After`).","headers":{"Retry-After":{"schema":{"type":"integer"},"description":"Seconds to wait. Sent with ERR_RATE_LIMIT only."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"InternalError":{"description":"`ERR_INTERNAL`: an unexpected failure. Any credits recorded for the request are reversed. Retry with backoff and quote `meta.request_id` to support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"schemas":{"Meta":{"type":"object","description":"Present on every response, errors included.","properties":{"request_id":{"type":"string","description":"Quote this when contacting support."},"credits_used":{"type":"integer","description":"Credits this request cost. Always 0 on an error."},"credits_remaining":{"type":"integer","description":"Credits left in the billing period after this request."},"rate_limit_remaining":{"type":"integer","description":"Requests left in the rolling hour (also in the X-RateLimit-Remaining header)."}}},"ErrorResponse":{"type":"object","properties":{"success":{"type":"boolean","example":false},"error":{"type":"string","description":"Human-readable message, kept for older clients. Read error_detail in new code."},"error_detail":{"type":"object","properties":{"code":{"type":"string","enum":["ERR_AUTH_MISSING","ERR_AUTH_INVALID","ERR_AUTH_REVOKED","ERR_SUBSCRIPTION_REQUIRED","ERR_SUBSCRIPTION_INACTIVE","ERR_CREDITS_EXHAUSTED","ERR_RATE_LIMIT","ERR_INVALID_PARAMS","ERR_IDENTIFIER_REQUIRED","ERR_PRODUCT_NOT_FOUND","ERR_ENDPOINT_NOT_FOUND","ERR_METHOD_NOT_ALLOWED","ERR_INTERNAL"],"description":"Stable machine-readable code. Branch on this, not on the HTTP status or message."},"message":{"type":"string"},"action":{"type":"string","description":"One-line fix."},"docs_url":{"type":"string","description":"Link to this code on https://shopsavvy.com/data/documentation/errors."}}},"meta":{"$ref":"#/components/schemas/Meta"}}},"Score":{"type":"object","description":"Expert quality scores on a 0-1 scale. `aspects` is keyed by free-form aspect names drawn from the product's professional reviews (e.g. value, features, reliability, quality) — the set of aspects varies per product. Only populated fields are included.","properties":{"overall":{"type":"number","minimum":0,"maximum":1},"customer":{"type":"number","minimum":0,"maximum":1},"professional":{"type":"number","minimum":0,"maximum":1},"aspects":{"type":"object","additionalProperties":{"type":"number","minimum":0,"maximum":1}}}},"Match":{"type":"object","description":"How the identifier you sent matched this product.","properties":{"identifier":{"type":"string","description":"The identifier exactly as sent."},"by":{"type":"string","enum":["barcode","shopsavvy_id","asin","url","model","mpn","name"],"description":"Which field matched. Everything except `name` is an exact match; `name` is a full-text best guess."},"candidates":{"type":"integer","description":"How many products matched on that field. More than 1 means the identifier is shared and the best-evidence product was returned."}}},"Product":{"type":"object","description":"A product record. Fields with no value are omitted.","properties":{"title":{"type":"string"},"shopsavvy":{"type":"string","description":"ShopSavvy product id (a path such as `products/3ONn300xybP3y66ibqc1`). Accepted as an identifier by every endpoint."},"brand":{"type":"string"},"category":{"type":"string"},"images":{"type":"array","items":{"type":"string"}},"barcode":{"type":"string"},"amazon":{"type":"string","description":"Amazon ASIN."},"model":{"type":"string"},"mpn":{"type":"string"},"color":{"type":"string"},"title_short":{"type":"string"},"slug":{"type":"string"},"description":{"type":"string"},"categories":{"type":"array","items":{"type":"string"}},"attributes":{"type":"object","additionalProperties":{"type":"string"}},"rating":{"type":"object","description":"Review-count-weighted average across retailers.","properties":{"value":{"type":"number","description":"Out of 5."},"count":{"type":"integer"}}},"score":{"$ref":"#/components/schemas/Score"},"keywords":{"type":"array","items":{"type":"string"}},"identifiers":{"type":"object","additionalProperties":{},"description":"Every identifier known for the product, keyed by source (e.g. `amazon`, `bestbuy`, `walmart`, `barcode`)."},"match":{"$ref":"#/components/schemas/Match"}}},"Offer":{"type":"object","description":"One listing of the product at one retailer or marketplace seller.","properties":{"id":{"type":"string"},"retailer":{"type":"string"},"price":{"type":"number"},"currency":{"type":"string","description":"ISO currency code. Always 'USD' today — offer endpoints currently serve USD-denominated offers only."},"availability":{"type":"string","enum":["in","out"],"description":"Omitted when unknown."},"condition":{"type":"string","nullable":true,"description":"`new`, `used` or `refurbished`."},"condition_grade":{"type":"string","nullable":true,"description":"Finer grade alongside condition, e.g. 'open-box', 'refurbished-excellent', 'used-like-new'. null when the source did not grade it."},"URL":{"type":"string"},"seller":{"type":"string","nullable":true,"description":"Marketplace seller name; absent or null for first-party offers."},"is_marketplace":{"type":"boolean","nullable":true,"description":"true = sold by a third-party marketplace seller (see seller), false = sold by the retailer itself, null = the source did not say."},"timestamp":{"type":"string","format":"date-time","description":"When ShopSavvy last saw this offer."},"history":{"type":"array","items":{"$ref":"#/components/schemas/HistoryPoint"},"description":"Always empty on GET /products/offers; filled by GET /products/offers/history."}}},"HistoryPoint":{"type":"object","properties":{"price":{"type":"number"},"currency":{"type":"string","nullable":true},"availability":{"type":"string","enum":["in","out"],"description":"Omitted when unknown."},"timestamp":{"type":"string","format":"date-time"}}},"ProductWithOffers":{"allOf":[{"$ref":"#/components/schemas/Product"},{"type":"object","properties":{"offers":{"type":"array","items":{"$ref":"#/components/schemas/Offer"}}}}]},"ScheduledProduct":{"allOf":[{"$ref":"#/components/schemas/Product"},{"type":"object","properties":{"schedule":{"type":"string","enum":["hourly","daily","weekly"]},"retailer":{"type":"string","description":"Present when the schedule is limited to one retailer."}}}]},"Pagination":{"type":"object","properties":{"total":{"type":"integer"},"limit":{"type":"integer"},"offset":{"type":"integer"},"returned":{"type":"integer"}}},"BatchResult":{"type":"object","properties":{"identifier":{"type":"string"},"status":{"type":"string","enum":["found","not_found"]},"product":{"allOf":[{"$ref":"#/components/schemas/Product"}],"nullable":true},"offers":{"type":"array","items":{"$ref":"#/components/schemas/Offer"},"description":"When `include` has `offers`."},"review":{"allOf":[{"$ref":"#/components/schemas/TLDRReview"}],"nullable":true,"description":"When `include` has `reviews`."}}},"BatchSummary":{"type":"object","properties":{"total":{"type":"integer"},"found":{"type":"integer"},"not_found":{"type":"integer"}}},"BatchJob":{"type":"object","properties":{"id":{"type":"string"},"status":{"type":"string","enum":["pending","processing","completed","failed"]},"total":{"type":"integer"},"processed":{"type":"integer"},"poll_url":{"type":"string","example":"/v1/batch/batch_abc123def456"},"estimated_seconds":{"type":"integer","description":"On creation only."}}},"Usage":{"type":"object","properties":{"current_period":{"type":"object","properties":{"start_date":{"type":"string","format":"date"},"end_date":{"type":"string","format":"date"},"credits_used":{"type":"integer"},"credits_limit":{"type":"integer","description":"Monthly allowance, including referral bonus credits."},"credits_remaining":{"type":"integer"},"requests_made":{"type":"integer"}}},"usage_percentage":{"type":"number"},"subscription_required":{"type":"boolean","description":"true when no active Data API plan is attached; credits_limit is then 0."},"subscription_status":{"type":"string","nullable":true,"enum":["active","trialing","past_due",null]}}},"Deal":{"type":"object","properties":{"path":{"type":"string"},"title":{"type":"string"},"subtitle":{"type":"string"},"description":{"type":"string"},"emoji":{"type":"string"},"grade":{"type":"object","properties":{"letter":{"type":"string"},"suffix":{"type":"string"},"value":{"type":"number"},"justification":{"type":"string"}}},"pricing":{"type":"object","properties":{"current":{"type":"number"},"original":{"type":"number"},"currency":{"type":"string"}}},"retailer":{"type":"object","properties":{"name":{"type":"string"}}},"url":{"type":"string"},"votes":{"type":"object","properties":{"upvotes":{"type":"integer"},"downvotes":{"type":"integer"},"score":{"type":"integer"}}},"comment_count":{"type":"integer"},"created_at":{"type":"string"}}},"TLDRReview":{"type":"object","properties":{"slug":{"type":"string"},"headline":{"type":"string"},"pros":{"type":"array","items":{"type":"string"}},"cons":{"type":"array","items":{"type":"string"}},"bottom_line":{"type":"string"},"scores":{"$ref":"#/components/schemas/Score"}}},"Webhook":{"type":"object","properties":{"id":{"type":"string"},"url":{"type":"string"},"events":{"type":"array","items":{"type":"string","enum":["price_drop","availability_change","schedule_completion"]}},"is_active":{"type":"boolean"},"secret":{"type":"string","description":"Only returned on creation. Use it to verify X-ShopSavvy-Signature."},"created_at":{"type":"string"},"last_triggered_at":{"type":"string","nullable":true,"description":"In GET /webhooks."},"failure_count":{"type":"integer","description":"In GET /webhooks."},"consecutive_failures":{"type":"integer","description":"In GET /webhooks. The webhook is disabled after 50."}}}}},"paths":{"/products":{"get":{"operationId":"getProducts","summary":"Look up product details","description":"Returns the product record for each identifier you send: title, brand, category, images, identifiers, rating and scores. Identifiers of different types can be mixed in one request. Each product carries a `match` object showing which identifier produced it and how.","x-credit-cost":"1 credit per product found. Identifiers that match nothing cost nothing.","parameters":[{"name":"ids","in":"query","required":true,"schema":{"type":"string"},"description":"Up to 100 comma-separated product identifiers of any type: UPC, EAN, ISBN, GTIN, Amazon ASIN, model number, MPN, product URL at any retailer (URL-encoded), product name, or ShopSavvy product ID. Identifiers that match nothing are left out of `data`; use each product's `match.identifier` to line results up with your request.","example":"611247373064,B07GV2S1GS"},{"name":"format","in":"query","schema":{"type":"string","enum":["json","csv"],"default":"json"},"description":"`csv` returns a flattened CSV file instead of JSON."}],"responses":{"200":{"description":"Products found (JSON), or a CSV file with `format=csv`.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"data":{"type":"array","items":{"$ref":"#/components/schemas/Product"}},"meta":{"$ref":"#/components/schemas/Meta"}}}},"text/csv":{"schema":{"type":"string"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/ProductNotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/InternalError"}},"tags":["Products"]}},"/products/search":{"get":{"operationId":"searchProducts","summary":"Search products by keyword","description":"Full-text search over the product catalog, best match first, with pagination. Use it when you have a name or description rather than an identifier, then pass a result's `shopsavvy`, `barcode` or `amazon` value to the offers and history endpoints.","x-credit-cost":"1 credit per product returned.","parameters":[{"name":"q","in":"query","required":true,"schema":{"type":"string"},"description":"Search text.","example":"sony headphones"},{"name":"limit","in":"query","schema":{"type":"integer","default":20,"minimum":1,"maximum":100}},{"name":"offset","in":"query","schema":{"type":"integer","default":0,"minimum":0}},{"name":"format","in":"query","schema":{"type":"string","enum":["json","csv"],"default":"json"},"description":"`csv` returns a flattened CSV file instead of JSON."}],"responses":{"200":{"description":"Matching products and pagination.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"data":{"type":"array","items":{"$ref":"#/components/schemas/Product"}},"pagination":{"$ref":"#/components/schemas/Pagination"},"meta":{"$ref":"#/components/schemas/Meta"}}}},"text/csv":{"schema":{"type":"string"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/InternalError"}},"tags":["Products"]}},"/products/offers":{"get":{"operationId":"getOffers","summary":"Get current prices from all retailers","description":"Returns each product with an `offers` array: one entry per retailer listing, with price, availability, condition, marketplace seller and when ShopSavvy last saw it. Pass `retailer` to get one retailer only.\n\nReturns USD-denominated offers only. Offers mined in other currencies (GBP, EUR, CAD, AUD and ~40 more) exist but are not served by this endpoint and there is no parameter to request them, so an empty `offers` array can mean either \"no offers on file\" or \"no USD offers on file\". Offers our product-match validation flagged as invalid, and offers from unknown/untrusted-source retailers, are excluded.","x-credit-cost":"3 credits per product with offers (2 with `retailer`). Products with no offers, and identifiers that match nothing, cost nothing.","parameters":[{"name":"ids","in":"query","required":true,"schema":{"type":"string"},"description":"Up to 100 comma-separated product identifiers of any type: UPC, EAN, ISBN, GTIN, Amazon ASIN, model number, MPN, product URL at any retailer (URL-encoded), product name, or ShopSavvy product ID. Identifiers that match nothing are left out of `data`; use each product's `match.identifier` to line results up with your request.","example":"611247373064,B07GV2S1GS"},{"name":"retailer","in":"query","schema":{"type":"string"},"description":"Only this retailer, as a domain such as `bestbuy.com` or `amazon.com`.","example":"bestbuy.com"},{"name":"format","in":"query","schema":{"type":"string","enum":["json","csv"],"default":"json"},"description":"`csv` returns a flattened CSV file instead of JSON."}],"responses":{"200":{"description":"Products with their current offers.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"data":{"type":"array","items":{"$ref":"#/components/schemas/ProductWithOffers"}},"meta":{"$ref":"#/components/schemas/Meta"}}}},"text/csv":{"schema":{"type":"string"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/ProductNotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/InternalError"}},"tags":["Pricing"]}},"/products/offers/history":{"get":{"operationId":"getOfferHistory","summary":"Get historical price data","description":"Returns each product's offers with a `history` array of dated price and availability points between `start` and `end`. Points are quality-filtered to the same series the ShopSavvy price chart renders, and eBay listings never carry history. The window may span at most 366 days; request longer histories in successive windows.\n\nReturns USD-denominated offers and USD history points only; there is no currency parameter (see /products/offers).","x-credit-cost":"The offers cost per product (3, or 2 with `retailer`) plus 1 credit per day of the window, the per-day part charged once per request. Nothing is charged when no offers are found.","parameters":[{"name":"ids","in":"query","required":true,"schema":{"type":"string"},"description":"Up to 100 comma-separated product identifiers of any type: UPC, EAN, ISBN, GTIN, Amazon ASIN, model number, MPN, product URL at any retailer (URL-encoded), product name, or ShopSavvy product ID. Identifiers that match nothing are left out of `data`; use each product's `match.identifier` to line results up with your request.","example":"611247373064,B07GV2S1GS"},{"name":"start","in":"query","required":true,"schema":{"type":"string","format":"date"},"description":"YYYY-MM-DD. `start_date` is accepted as an alias.","example":"2026-09-01"},{"name":"end","in":"query","required":true,"schema":{"type":"string","format":"date"},"description":"YYYY-MM-DD, at most 366 days after `start`. `end_date` is accepted as an alias.","example":"2026-09-30"},{"name":"retailer","in":"query","schema":{"type":"string"},"description":"Only this retailer, as a domain such as `bestbuy.com` or `amazon.com`.","example":"bestbuy.com"},{"name":"format","in":"query","schema":{"type":"string","enum":["json","csv"],"default":"json"},"description":"`csv` returns a flattened CSV file instead of JSON."}],"responses":{"200":{"description":"Products with offers and their history points.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"data":{"type":"array","items":{"$ref":"#/components/schemas/ProductWithOffers"}},"meta":{"$ref":"#/components/schemas/Meta"}}}},"text/csv":{"schema":{"type":"string"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/ProductNotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/InternalError"}},"tags":["Pricing"]}},"/products/reviews":{"get":{"operationId":"getProductReview","summary":"Get TLDR review for a product","description":"Returns ShopSavvy's expert review summary for one product: headline, pros, cons, bottom line and 0-1 quality scores. `review` is null when the product has no TLDR review.","x-credit-cost":"1 credit when a review exists, 0 when `review` is null.","parameters":[{"name":"id","in":"query","required":true,"schema":{"type":"string"},"description":"One product identifier (barcode, ASIN, URL, model number or ShopSavvy id).","example":"B09XS7JWHH"},{"name":"format","in":"query","schema":{"type":"string","enum":["json","csv"],"default":"json"},"description":"`csv` returns a flattened CSV file instead of JSON."}],"responses":{"200":{"description":"The product and its review.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"product":{"type":"object","properties":{"path":{"type":"string"},"title":{"type":"string"}}},"review":{"allOf":[{"$ref":"#/components/schemas/TLDRReview"}],"nullable":true},"meta":{"$ref":"#/components/schemas/Meta"}}}},"text/csv":{"schema":{"type":"string"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/ProductNotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/InternalError"}},"tags":["Reviews"]}},"/products/batch":{"post":{"operationId":"batchProducts","summary":"Batch product lookup","description":"Looks up 1-100 identifiers in one request, optionally with offers and reviews. Up to 20 identifiers answer synchronously (200 with `results`); more than 20 create an asynchronous job (202 with `batch`), which you poll at `GET /batch/{batchId}`.","x-credit-cost":"1 credit per product found, plus 3 per product that has offers when `include` has `offers`, plus 1 per product that has a review when `include` has `reviews`.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["identifiers"],"properties":{"identifiers":{"type":"array","items":{"type":"string"},"minItems":1,"maxItems":100},"include":{"type":"array","items":{"type":"string","enum":["offers","reviews"]}}}}}}},"responses":{"200":{"description":"Synchronous results (20 identifiers or fewer).","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"results":{"type":"array","items":{"$ref":"#/components/schemas/BatchResult"}},"summary":{"$ref":"#/components/schemas/BatchSummary"},"meta":{"$ref":"#/components/schemas/Meta"}}}}}},"202":{"description":"Asynchronous job created (more than 20 identifiers).","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"batch":{"$ref":"#/components/schemas/BatchJob"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/InternalError"}},"tags":["Batch"]}},"/batch/{batchId}":{"get":{"operationId":"getBatch","summary":"Poll batch job status","description":"Returns the job's progress until `batch.status` is `completed`, then the full `results` and `summary`. A failed job has `batch.status` `failed` and an `error` message.","x-credit-cost":"0 credits (the batch was charged when its products were looked up).","parameters":[{"name":"batchId","in":"path","required":true,"schema":{"type":"string"},"description":"The `batch.id` from `POST /products/batch`."}],"responses":{"200":{"description":"Job status, and results once completed.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"batch":{"$ref":"#/components/schemas/BatchJob"},"results":{"type":"array","items":{"$ref":"#/components/schemas/BatchResult"}},"summary":{"$ref":"#/components/schemas/BatchSummary"},"error":{"type":"string"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/InternalError"}},"tags":["Batch"]}},"/products/scheduled":{"get":{"operationId":"listScheduledProducts","summary":"List scheduled products","description":"Returns every product you have scheduled for refreshes, with its `schedule` and, when limited to one retailer, its `retailer`. Read their latest prices with `GET /products/offers`.","x-credit-cost":"0 credits.","responses":{"200":{"description":"Scheduled products.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"data":{"type":"array","items":{"$ref":"#/components/schemas/ScheduledProduct"}},"meta":{"$ref":"#/components/schemas/Meta"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/InternalError"}},"tags":["Scheduling"]},"put":{"operationId":"scheduleProducts","summary":"Schedule product monitoring","description":"Schedules products to be re-checked hourly, daily or weekly at the retailers ShopSavvy can re-check through an official retailer API (Amazon for products with an ASIN, Best Buy for products with a Best Buy SKU). Offers at other retailers keep updating as ShopSavvy sees them. Scheduling a product again updates its schedule in place. Read the results with `GET /products/offers`.","x-credit-cost":"1 credit per product scheduled. Refreshes are not charged.","parameters":[{"name":"ids","in":"query","required":true,"schema":{"type":"string"},"description":"Up to 100 comma-separated product identifiers of any type: UPC, EAN, ISBN, GTIN, Amazon ASIN, model number, MPN, product URL at any retailer (URL-encoded), product name, or ShopSavvy product ID. Identifiers that match nothing are left out of `data`; use each product's `match.identifier` to line results up with your request.","example":"611247373064,B07GV2S1GS"},{"name":"schedule","in":"query","required":true,"schema":{"type":"string","enum":["hourly","daily","weekly"]}},{"name":"retailer","in":"query","schema":{"type":"string"},"description":"Only this retailer, as a domain such as `bestbuy.com` or `amazon.com`.","example":"bestbuy.com"}],"responses":{"200":{"description":"The products just scheduled.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"data":{"type":"array","items":{"$ref":"#/components/schemas/ScheduledProduct"}},"meta":{"$ref":"#/components/schemas/Meta"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/ProductNotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/InternalError"}},"tags":["Scheduling"]},"delete":{"operationId":"unscheduleProducts","summary":"Remove products from the schedule","description":"Stops scheduled refreshes for the given products. Their existing offers stay available.","x-credit-cost":"0 credits.","parameters":[{"name":"ids","in":"query","required":true,"schema":{"type":"string"},"description":"Up to 100 comma-separated product identifiers of any type: UPC, EAN, ISBN, GTIN, Amazon ASIN, model number, MPN, product URL at any retailer (URL-encoded), product name, or ShopSavvy product ID. Identifiers that match nothing are left out of `data`; use each product's `match.identifier` to line results up with your request.","example":"611247373064,B07GV2S1GS"}],"responses":{"200":{"description":"Removal confirmation.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"meta":{"$ref":"#/components/schemas/Meta"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/InternalError"}},"tags":["Scheduling"]}},"/deals":{"get":{"operationId":"listDeals","summary":"Browse shopping deals","description":"Returns current deals with expert grades (A+ through F), community votes, retailer and pricing, sorted hot/new/top and filterable by category, retailer, price and grade. Page through the feed with `limit` and `offset`.","x-credit-cost":"1 credit per 10 deals returned (minimum 1).","parameters":[{"name":"sort","in":"query","schema":{"type":"string","enum":["hot","new","top-hour","top-day","top-week"],"default":"hot"}},{"name":"limit","in":"query","schema":{"type":"integer","default":25,"minimum":1,"maximum":100}},{"name":"offset","in":"query","schema":{"type":"integer","default":0}},{"name":"category","in":"query","schema":{"type":"string"}},{"name":"retailer","in":"query","schema":{"type":"string"}},{"name":"grade","in":"query","schema":{"type":"string","enum":["A","B","C","D"]}},{"name":"min_price","in":"query","schema":{"type":"number"}},{"name":"max_price","in":"query","schema":{"type":"number"}},{"name":"format","in":"query","schema":{"type":"string","enum":["json","csv"],"default":"json"},"description":"`csv` returns a flattened CSV file instead of JSON."}],"responses":{"200":{"description":"Deals feed","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"deals":{"type":"array","items":{"$ref":"#/components/schemas/Deal"}},"pagination":{"type":"object"},"meta":{"$ref":"#/components/schemas/Meta"}}}},"text/csv":{"schema":{"type":"string"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/InternalError"}},"tags":["Deals"]}},"/webhooks":{"get":{"operationId":"listWebhooks","summary":"List webhooks","description":"Returns your registered webhooks with their events, whether they are active, when they last fired and their failure counts. The signing secret is not included; it is shown once, on creation.","x-credit-cost":"0 credits.","responses":{"200":{"description":"Your webhooks.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"webhooks":{"type":"array","items":{"$ref":"#/components/schemas/Webhook"}},"meta":{"$ref":"#/components/schemas/Meta"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"500":{"$ref":"#/components/responses/InternalError"}},"tags":["Webhooks"]},"post":{"operationId":"createWebhook","summary":"Create a webhook","description":"Registers an HTTPS URL to receive signed POSTs for price drops, availability changes and completed scheduled refreshes. The response includes the signing `secret`, shown once. Up to 5 webhooks per account.","x-credit-cost":"0 credits.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["url","events"],"properties":{"url":{"type":"string"},"events":{"type":"array","items":{"type":"string","enum":["price_drop","availability_change","schedule_completion"]}}}}}}},"responses":{"201":{"description":"Webhook created (includes secret).","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"webhook":{"$ref":"#/components/schemas/Webhook"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"500":{"$ref":"#/components/responses/InternalError"}},"tags":["Webhooks"]}},"/webhooks/{webhookId}":{"put":{"operationId":"updateWebhook","summary":"Update a webhook","description":"Changes a webhook's URL, event list or `is_active` flag without deleting it, so its signing secret is kept. Omitted fields are left unchanged; re-enabling resets the failure counter.","x-credit-cost":"0 credits.","parameters":[{"name":"webhookId","in":"path","required":true,"schema":{"type":"string"},"description":"The webhook id returned by `POST /webhooks` or `GET /webhooks`."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string"},"events":{"type":"array","items":{"type":"string","enum":["price_drop","availability_change","schedule_completion"]}},"is_active":{"type":"boolean"}}}}}},"responses":{"200":{"description":"Updated webhook.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"webhook":{"$ref":"#/components/schemas/Webhook"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}},"tags":["Webhooks"]},"delete":{"operationId":"deleteWebhook","summary":"Delete a webhook","description":"Permanently removes a webhook and its delivery history. To pause deliveries and keep the signing secret, set `is_active` to false with `PUT /webhooks/{webhookId}` instead.","x-credit-cost":"0 credits.","parameters":[{"name":"webhookId","in":"path","required":true,"schema":{"type":"string"},"description":"The webhook id returned by `POST /webhooks` or `GET /webhooks`."}],"responses":{"200":{"description":"Deleted.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"deleted":{"type":"boolean"},"meta":{"$ref":"#/components/schemas/Meta"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}},"tags":["Webhooks"]}},"/webhooks/{webhookId}/test":{"post":{"operationId":"testWebhook","summary":"Send a test webhook","description":"Sends a signed test event to the webhook's URL right away and reports whether your server accepted it. Use it to check your endpoint and signature verification before real events arrive.","x-credit-cost":"0 credits.","parameters":[{"name":"webhookId","in":"path","required":true,"schema":{"type":"string"},"description":"The webhook id returned by `POST /webhooks` or `GET /webhooks`."}],"responses":{"200":{"description":"Delivery result.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"test_result":{"type":"object","properties":{"delivered":{"type":"boolean"},"response_code":{"type":"integer","nullable":true},"error":{"type":"string","nullable":true}}},"meta":{"$ref":"#/components/schemas/Meta"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}},"tags":["Webhooks"]}},"/usage":{"get":{"operationId":"getUsage","summary":"Get API usage statistics","description":"Returns credits used and remaining, requests made and subscription status for the current billing period. The same remaining-credit count is in `meta.credits_remaining` on every response.","x-credit-cost":"0 credits.","responses":{"200":{"description":"Usage for the current billing period.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"data":{"$ref":"#/components/schemas/Usage"},"meta":{"$ref":"#/components/schemas/Meta"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/InternalError"}},"tags":["Analytics"]}}},"tags":[{"name":"Products","description":"Product details and search"},{"name":"Pricing","description":"Offers and price history"},{"name":"Deals","description":"Shopping deals feed"},{"name":"Reviews","description":"Expert product reviews"},{"name":"Batch","description":"Batch product lookups"},{"name":"Scheduling","description":"Product monitoring"},{"name":"Webhooks","description":"Event notifications"},{"name":"Analytics","description":"Usage statistics"}]}