{
  "openapi": "3.0.1",
  "info": {
    "title": "eBay Sold Listings Comps AI: Price Checker & Max Buy Price",
    "description": "First 25 comps free. eBay sold listings cleaned into exact-match comps, pay per clean comp - junk never charged by us: parts, lots, accessories and outliers out, AI-matched to your item, plus median and max buy price. Free demo on any plan; live search runs on your Apify account.",
    "version": "0.3",
    "x-build-id": "B7rt0Nd3VtS3Holng"
  },
  "servers": [
    {
      "url": "https://api.apify.com/v2"
    }
  ],
  "paths": {
    "/acts/rich_minds~ebay-sold-comps-ai/run-sync-get-dataset-items": {
      "post": {
        "operationId": "run-sync-get-dataset-items-rich_minds-ebay-sold-comps-ai",
        "x-openai-isConsequential": false,
        "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
        "tags": [
          "Run Actor"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/inputSchema"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Enter your Apify token here"
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/acts/rich_minds~ebay-sold-comps-ai/runs": {
      "post": {
        "operationId": "runs-sync-rich_minds-ebay-sold-comps-ai",
        "x-openai-isConsequential": false,
        "summary": "Executes an Actor and returns information about the initiated run in response.",
        "tags": [
          "Run Actor"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/inputSchema"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Enter your Apify token here"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/runsResponseSchema"
                }
              }
            }
          }
        }
      }
    },
    "/acts/rich_minds~ebay-sold-comps-ai/run-sync": {
      "post": {
        "operationId": "run-sync-rich_minds-ebay-sold-comps-ai",
        "x-openai-isConsequential": false,
        "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
        "tags": [
          "Run Actor"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/inputSchema"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Enter your Apify token here"
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "inputSchema": {
        "type": "object",
        "required": [
          "sourceMode"
        ],
        "properties": {
          "keywords": {
            "title": "eBay search keywords (1–6)",
            "type": "array",
            "description": "The item you price, one keyword per line, e.g. <code>nintendo switch oled</code> or <code>iphone 13 pro 128gb</code> — one price report each. The live search runs eBay Sold Listings Search on your Apify plan (no Store Actors on your plan? use a dataset). The free demo does not search it; typing a keyword starts the live search.",
            "items": {
              "type": "string"
            }
          },
          "sourceMode": {
            "title": "Where do the sold listings come from?",
            "enum": [
              "actor",
              "dataset",
              "list"
            ],
            "type": "string",
            "description": "<b>Search eBay live</b> for the keywords above (rows billed by eBay Sold Listings Search, capped by <b>Max source spend</b>), clean an existing <b>dataset</b>, or paste a <b>list</b> of sold rows. With nothing to search (<code>{}</code>) the free demo runs on 16 sample sold listings — nothing charged. <b>Bring your own data:</b> rows or a dataset ID, and you pay only for the analysis — the source Actor is never run.",
            "default": "actor"
          },
          "minScore": {
            "title": "Minimum match score (0–100)",
            "minimum": 0,
            "maximum": 100,
            "type": "integer",
            "description": "The field that decides what you pay: sales whose match to your item scores below this are rejected and <b>not charged</b>. 70 keeps exact matches; raise it to 85 for only sure ones. Rules cap at 95; 96–100 needs the AI's exact verdict.",
            "default": 70
          },
          "itemDescription": {
            "title": "Your exact item, in words (for the AI)",
            "type": "string",
            "description": "Describe the exact item — model, storage, colour, condition — e.g. <i>iPhone 13 Pro Max 128GB unlocked, working</i>. The AI match check compares every borderline sale with it. Leave empty to match on the keywords only."
          },
          "maxQualified": {
            "title": "Max comps to deliver",
            "minimum": 1,
            "maximum": 10000,
            "type": "integer",
            "description": "Hard cap on the comps pushed and on what <b>this Actor</b> charges: at most this many × $0.01 with AI on (× $0.005 off), e.g. 200 × $0.01 = $2.00. The price report always uses every clean comp. Best matches first.",
            "default": 200
          },
          "maxDiscoveryChargeUsd": {
            "title": "Max source spend (USD)",
            "minimum": 0.01,
            "maximum": 1000,
            "type": "number",
            "description": "Hard cap on what the eBay search may charge your account ($0.004 per sold row, so 1 keyword × 100 rows ≈ $0.40) — $0.50 in the form and for API calls that leave it out. Raise it for more keywords.",
            "default": 0.5
          },
          "enableAi": {
            "title": "AI match check",
            "type": "boolean",
            "description": "Checks every borderline sale against your exact item (verdict, confidence, model / storage / colour), lists the defects the listing admits and writes the report summary. <b>On = $0.01 per AI-checked comp + AI tokens; off = $0.005</b> (rule match only). Clear matches stay at the basic price.",
            "default": true
          },
          "notifyEmail": {
            "title": "E-mail me the price report (optional)",
            "type": "string",
            "description": "After every run with new comps or a price report, the digest (median, band, list and max-buy price per keyword, change vs last run, the new comps with links) is e-mailed here. Schedule the run weekly and it lands in your inbox. Never sent for the free demo."
          },
          "mustInclude": {
            "title": "Must include (every term)",
            "type": "array",
            "description": "Terms every matching title must contain, number-aware: <code>128gb</code> matches \"128 GB\". A sale missing one is capped at score 40 — rejected unless the AI confirms it.",
            "items": {
              "type": "string"
            }
          },
          "mustExclude": {
            "title": "Must exclude (any term)",
            "type": "array",
            "description": "A title with any of these terms is rejected, e.g. <code>max</code>, <code>mini</code>, <code>refurbished</code>.",
            "items": {
              "type": "string"
            }
          },
          "keepConditions": {
            "title": "Conditions to keep",
            "type": "array",
            "description": "Condition buckets that count as comps (from eBay's condition id and the local label). <b>For parts</b> and <b>unknown</b> are off by default.",
            "items": {
              "type": "string",
              "enum": [
                "new",
                "open_box",
                "refurbished",
                "used",
                "parts",
                "unknown"
              ],
              "enumTitles": [
                "New",
                "Open box / new other",
                "Refurbished",
                "Used",
                "For parts / not working",
                "Unknown"
              ]
            },
            "default": [
              "new",
              "open_box",
              "refurbished",
              "used"
            ]
          },
          "lotHandling": {
            "title": "Lots and bundles",
            "enum": [
              "exclude",
              "normalize",
              "include"
            ],
            "type": "string",
            "description": "\"Lot of 3\", \"3x\", \"bundle\", \"Konvolut\": <b>exclude</b> them, <b>normalize</b> (price ÷ lot size — a lot without a number is excluded) or <b>include</b> as sold.",
            "default": "exclude"
          },
          "bestOfferHandling": {
            "title": "Best Offer accepted sales",
            "enum": [
              "excludeFromStats",
              "include",
              "drop"
            ],
            "type": "string",
            "description": "eBay shows the asking price, not the accepted offer, on these. <b>Keep out of the statistics</b> (delivered with <code>priceIsAsking: true</code>), <b>include</b> them anyway, or <b>drop</b> them.",
            "default": "excludeFromStats"
          },
          "outlierIqrK": {
            "title": "Outlier fence (IQR × k)",
            "minimum": 0,
            "maximum": 10,
            "type": "number",
            "description": "Sales outside Q1 − k·IQR … Q3 + k·IQR of their keyword and condition (5+ sales) are rejected free. 1.5 is Tukey's fence; 3 keeps more; 0 turns it off.",
            "default": 1.5
          },
          "targetFlags": {
            "title": "Only comps with these flags (any of)",
            "type": "array",
            "description": "Only deliver comps with at least one of these flags, e.g. <code>below_p25</code> for cheap sales or <code>auction</code> (<code>belowP25</code> works too). The price report still uses every clean comp. An unknown name stops the run with the valid ones.",
            "items": {
              "type": "string"
            }
          },
          "suppressionList": {
            "title": "Exclude list",
            "type": "array",
            "description": "eBay item ids, URLs or seller usernames to never output — skipped before any processing and never charged.",
            "items": {
              "type": "string"
            }
          },
          "ebaySite": {
            "title": "eBay marketplace",
            "enum": [
              "ebay.com",
              "ebay.co.uk",
              "ebay.de",
              "ebay.fr",
              "ebay.it",
              "ebay.es",
              "ebay.ca",
              "ebay.com.au"
            ],
            "type": "string",
            "description": "The eBay site the live search runs on. One currency per report — sales in another currency are rejected free.",
            "default": "ebay.com"
          },
          "daysToScrape": {
            "title": "Sold in the last N days",
            "minimum": 1,
            "maximum": 90,
            "type": "integer",
            "description": "The sold window (1–90 days) — also the window of the sell-through rate (sales per day) in the report.",
            "default": 30
          },
          "maxSoldPerKeyword": {
            "title": "Max sold listings per keyword",
            "minimum": 1,
            "maximum": 1000,
            "type": "integer",
            "description": "How many sold rows the source loads per keyword (its <code>count</code>, $0.004 each on your account). 100 gives a solid median; the spend cap above still applies.",
            "default": 100
          },
          "categoryId": {
            "title": "eBay category id",
            "type": "string",
            "description": "Only sales in this eBay category, e.g. <code>139971</code> (video game consoles). Leave empty for all."
          },
          "minPrice": {
            "title": "Min sold price",
            "minimum": 0,
            "type": "number",
            "description": "Only sales at or above this price (site currency) — the source skips the rest, so you pay for fewer junk rows."
          },
          "maxPrice": {
            "title": "Max sold price",
            "minimum": 0,
            "type": "number",
            "description": "Only sales at or below this price (site currency)."
          },
          "feeRatePct": {
            "title": "Your selling fee (%)",
            "minimum": 0,
            "maximum": 50,
            "type": "number",
            "description": "Your eBay final-value-fee rate for the max-buy-price math: <code>maxBuyPrice = suggestedListPrice × (1 − fee) − shippingCost − targetProfit</code>, all in the site's currency.",
            "default": 13.25
          },
          "shippingCost": {
            "title": "Your shipping cost per sale",
            "minimum": 0,
            "type": "number",
            "description": "What shipping one unit costs you, in the eBay site's currency (USD on ebay.com, GBP on ebay.co.uk, EUR on ebay.de …); subtracted in the max buy price. The 0.1 name <code>shippingCostUsd</code> still works.",
            "default": 0
          },
          "targetProfit": {
            "title": "Target profit per flip",
            "minimum": 0,
            "type": "number",
            "description": "The profit you want per unit, in the eBay site's currency; subtracted in the max buy price. The 0.1 name <code>targetProfitUsd</code> still works.",
            "default": 0
          },
          "llmProvider": {
            "title": "AI model access",
            "enum": [
              "apify",
              "byok"
            ],
            "type": "string",
            "description": "<b>Apify (no keys)</b> — the AI runs through Apify's built-in OpenRouter proxy; tokens are billed to your Apify account at OpenRouter's rates. <b>My own key</b> — use your OpenAI / Anthropic / Gemini / Groq key instead. A free-tier key is rate-limited: expect 1–3 AI checks per run (the borderline comps first, one at a time after a rate limit), the rest keep their rule score; a paid Groq / Gemini key (billing on) assesses every comp.",
            "default": "apify"
          },
          "llmModel": {
            "title": "AI model",
            "type": "string",
            "description": "Leave empty for the default (<code>anthropic/claude-haiku-4.5</code> with Apify's model access, <code>google:gemini-3.5-flash-lite</code> with your own key). Apify mode takes an OpenRouter slug such as <code>openai/gpt-4.1-mini</code>; own-key mode takes <code>provider:model</code>, e.g. <code>anthropic:claude-haiku-4-5-20251001</code>."
          },
          "llmApiKey": {
            "title": "Your API key (own-key mode only)",
            "type": "string",
            "description": "Required when <b>AI model access</b> is <i>My own API key</i>. Stored encrypted by Apify, never logged."
          },
          "aiCandidateMultiplier": {
            "title": "AI candidate pool (× max comps)",
            "minimum": 1,
            "maximum": 5,
            "type": "integer",
            "description": "The AI checks the borderline sales only — rule score 40–90, or a clear match in used / open-box condition (for its defects) — best rule score first, up to this multiple of <b>Max comps to deliver</b>.",
            "default": 2
          },
          "slackWebhookUrl": {
            "title": "Post the price report to Slack (optional)",
            "type": "string",
            "description": "A Slack incoming-webhook URL (<code>https://hooks.slack.com/services/…</code>). After every run the digest (median and change per keyword, the new comps with links) is posted to that channel as a readable message. Never sent for the free demo."
          },
          "discordWebhookUrl": {
            "title": "Post the price report to Discord (optional)",
            "type": "string",
            "description": "A Discord channel webhook URL (<code>https://discord.com/api/webhooks/…</code>, channel settings → Integrations → Webhooks). After every run the digest (median, list and max-buy price per keyword, the new comps with links) is posted to that channel — made for reseller groups. Stored encrypted; never sent for the free demo."
          },
          "alertOnly": {
            "title": "Deal alerts only",
            "type": "boolean",
            "description": "On a schedule: send the e-mail / Slack / Discord digest <b>only</b> when a new sale lands at or under your max buy price, or a keyword's median moved by at least <b>Median move to alert on</b> since the last run — a “buy now” ping instead of a weekly report. The comps and the price report are delivered either way.",
            "default": false
          },
          "alertMedianMovePct": {
            "title": "Median move to alert on (%)",
            "minimum": 0,
            "maximum": 100,
            "type": "number",
            "description": "With <b>Deal alerts only</b>: alert when a keyword's median moved by at least this many percent (up or down) vs the last run.",
            "default": 10
          },
          "webhookUrl": {
            "title": "Webhook URL (optional)",
            "type": "string",
            "description": "New comps are POSTed here as JSON (Zapier, Make, n8n, your database), then one <code>comps.report</code> per keyword. For Google Sheets / Slack you can also use Apify's Integrations tab. Stored encrypted — hook URLs carry their secret in the path."
          },
          "webhookHeaders": {
            "title": "Webhook headers (optional)",
            "type": "object",
            "description": "Extra HTTP headers for the webhook, e.g. <code>{\"Authorization\": \"Bearer …\"}</code>. Stored encrypted by Apify, never logged."
          },
          "webhookBatchSize": {
            "title": "Webhook batch size",
            "minimum": 1,
            "maximum": 500,
            "type": "integer",
            "description": "1 = one POST per comp the moment it is ready. Higher = one POST per N comps.",
            "default": 1
          },
          "dedupeAcrossRuns": {
            "title": "Never charge the same sale twice",
            "type": "boolean",
            "description": "Remembers every comp you were charged for (eBay site + item id, in a key-value store on your account): a weekly run delivers only new sales, while the price report still uses every clean sale in the window.",
            "default": true
          },
          "dedupeStoreName": {
            "title": "Dedupe store name",
            "type": "string",
            "description": "Key-value store used for cross-run memory. Left at the default, the memory is kept per search (source, keywords, eBay site): rewording <code>itemDescription</code> keeps it. Pricing for several clients on one account, type one name per client.",
            "default": "ebay-sold-comps-ai-seen"
          },
          "datasetId": {
            "title": "Dataset",
            "type": "string",
            "description": "Only for <b>dataset</b> mode: a dataset of eBay sold listings (from eBay Sold Listings Search or another eBay sold scraper — <code>price</code>, <code>soldDate</code>, <code>link</code>, <code>id</code> work too)."
          },
          "itemsList": {
            "title": "Sold rows to clean",
            "type": "array",
            "description": "Only for <b>list</b> mode. JSON array of sold listings with at least <code>title</code> and <code>soldPrice</code>; <code>itemId</code>, <code>url</code>, <code>condition</code>, <code>endedAt</code>, <code>keyword</code> and the other README list-mode fields switch on the rest of the output."
          },
          "discoveryActorId": {
            "title": "Source Actor (advanced)",
            "type": "string",
            "description": "Actor used in <b>actor</b> mode. Any Actor whose output has the fields of eBay Sold Listings Search works.",
            "default": "caffein.dev/ebay-sold-listings"
          },
          "discoveryInput": {
            "title": "Raw source input (advanced)",
            "type": "object",
            "description": "Only for <b>actor</b> mode. Extra input merged into the eBay Sold Listings Search call, e.g. <code>{\"itemCondition\": \"used\"}</code>; the fields above win. The live search runs that Store Actor on your Apify plan — if your plan cannot run Store Actors, use <b>dataset</b> mode."
          },
          "maxToProcess": {
            "title": "Max new sales to process",
            "minimum": 1,
            "maximum": 20000,
            "type": "integer",
            "description": "Upper bound on the new sold rows cleaned and matched in one run (controls run time). Default = 3 × max comps."
          },
          "discoveryTimeoutSecs": {
            "title": "Source run timeout, seconds (API only)",
            "minimum": 60,
            "maximum": 86400,
            "type": "integer",
            "description": "API only — hidden on the form. How long the eBay search may run before the sold rows it scraped so far are used. Raise it when the status says the source stopped early.",
            "default": 3600
          }
        }
      },
      "runsResponseSchema": {
        "type": "object",
        "properties": {
          "data": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "actId": {
                "type": "string"
              },
              "userId": {
                "type": "string"
              },
              "startedAt": {
                "type": "string",
                "format": "date-time",
                "example": "2025-01-08T00:00:00.000Z"
              },
              "finishedAt": {
                "type": "string",
                "format": "date-time",
                "example": "2025-01-08T00:00:00.000Z"
              },
              "status": {
                "type": "string",
                "example": "READY"
              },
              "meta": {
                "type": "object",
                "properties": {
                  "origin": {
                    "type": "string",
                    "example": "API"
                  },
                  "userAgent": {
                    "type": "string"
                  }
                }
              },
              "stats": {
                "type": "object",
                "properties": {
                  "inputBodyLen": {
                    "type": "integer",
                    "example": 2000
                  },
                  "rebootCount": {
                    "type": "integer",
                    "example": 0
                  },
                  "restartCount": {
                    "type": "integer",
                    "example": 0
                  },
                  "resurrectCount": {
                    "type": "integer",
                    "example": 0
                  },
                  "computeUnits": {
                    "type": "integer",
                    "example": 0
                  }
                }
              },
              "options": {
                "type": "object",
                "properties": {
                  "build": {
                    "type": "string",
                    "example": "latest"
                  },
                  "timeoutSecs": {
                    "type": "integer",
                    "example": 300
                  },
                  "memoryMbytes": {
                    "type": "integer",
                    "example": 1024
                  },
                  "diskMbytes": {
                    "type": "integer",
                    "example": 2048
                  }
                }
              },
              "buildId": {
                "type": "string"
              },
              "defaultKeyValueStoreId": {
                "type": "string"
              },
              "defaultDatasetId": {
                "type": "string"
              },
              "defaultRequestQueueId": {
                "type": "string"
              },
              "buildNumber": {
                "type": "string",
                "example": "1.0.0"
              },
              "containerUrl": {
                "type": "string"
              },
              "usage": {
                "type": "object",
                "properties": {
                  "ACTOR_COMPUTE_UNITS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_WRITES": {
                    "type": "integer",
                    "example": 1
                  },
                  "KEY_VALUE_STORE_LISTS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_INTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_EXTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_SERPS": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_UNBLOCKER_UNITS": {
                    "type": "integer",
                    "example": 0
                  }
                }
              },
              "usageTotalUsd": {
                "type": "number",
                "example": 0.00005
              },
              "usageUsd": {
                "type": "object",
                "properties": {
                  "ACTOR_COMPUTE_UNITS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_WRITES": {
                    "type": "number",
                    "example": 0.00005
                  },
                  "KEY_VALUE_STORE_LISTS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_INTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_EXTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_SERPS": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_UNBLOCKER_UNITS": {
                    "type": "integer",
                    "example": 0
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}