# GOAT Scraper - Sneaker Asks, Last Sale, Sizes & Prices (`abotapi/goat-sneaker-scraper`) Actor

Scrape GOAT (goat.com), the sneaker and streetwear resale marketplace. Search or paste product, brand, collection and category URLs. 45+ fields per product: lowest ask, Instant Ship price, last sale and stock status for every size, retail, SKU, colorway, release date, images and story.

- **URL**: https://apify.com/abotapi/goat-sneaker-scraper.md
- **Developed by:** [Abot API](https://apify.com/abotapi) (community)
- **Categories:** E-commerce
- **Features:** Limited permissions
- **Stats:** 4 total users, 2 monthly users, 100.0% runs succeeded, 1 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 1,000 product results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## GOAT Scraper: Sneaker Asks, Last Sale, Sizes & Prices

GOAT Scraper turns GOAT, the sneaker and streetwear resale marketplace, into structured product data. Search by keyword, browse the catalogue with filters alone, or paste GOAT product, search, brand, collection and category links. Every product comes back with its full identity, and with Fetch full market data on, the live per size market table: lowest ask, Instant Ship price, last sold price and stock status for every size in the market you choose. Export the dataset as JSON, CSV or Excel, or pull it straight into your app through the API.

### Why This Scraper?

- **Four ways to reach the catalogue.** Search by keyword, browse by filters alone, or paste GOAT product, search, brand, collection and category links. A pasted browse link is paged like a search; a pasted product link always returns that single product in full.
- **Full product identity every time.** Brand, SKU, colorway, designer, silhouette, sizing, release date, retail price, images and the product story, whichever mode found it.
- **The live per size market table on demand.** Turn on Fetch full market data for the lowest ask, Instant Ship price, last sold price and stock status for every size, and pay the extra fee only when you use it.
- **A real ship-to market, not a currency label.** GOAT prices and stocks sizes differently per country, so switching Market genuinely changes which sizes and prices come back, not just how they are labelled.
- **Server side filters.** Brand, category, activity, gender, condition, size, price and release year all narrow GOAT's own search, so a filtered run reads fewer pages instead of throwing rows away afterwards.
- **Built for monitoring.** Incremental mode returns only new and changed products on recurring runs, and price, ask or stock moves are reported as the signal they are on a resale marketplace, not filtered out as noise.
- **Fails loudly instead of going quiet.** A run the site refuses stops with a clear message, so "nothing matched this search" is never confused with "GOAT could not be reached".

### Use Cases

- **Resale investors and flippers:** compare lowest ask, last sale and Instant Ship price across sizes to spot underpriced pairs.
- **Sneaker release trackers:** monitor new drops and price movement on hyped releases as soon as they land on GOAT.
- **Marketplace and price comparison tools:** feed product identity, retail price and live market data into your own app or tracker.
- **Reselling and inventory businesses:** check stock status and per size pricing across a catalogue before buying or listing.
- **Market research and data science:** build a history of ask and sale prices per size for sneaker market analysis.

### Data You Get

> Sample shape: values are illustrative placeholders, not from a live record.

| Field | Example |
| --- | --- |
| `productId`, `slug`, `url` | GOAT's product id, its URL slug and the product page link |
| `name`, `nickname` | Full product name and its short nickname |
| `brand`, `sku`, `colorway`, `color` | Brand, style code, colorway text and base colour |
| `designer`, `silhouette`, `midsole`, `upperMaterial` | Design attributes, when GOAT publishes them |
| `productCategory`, `productType`, `activities` | Shoes, clothing or accessories, product type, and activity tags |
| `gender`, `genders` | Primary audience and every audience it is cut for |
| `sizeRange`, `sizeUnit`, `sizeBrand` | Every size the product exists in, and the sizing system |
| `releaseDate`, `releaseYear`, `releaseMonth`, `season` | Release data |
| `retailPrice` | Original retail price, USD |
| `lowestAsk`, `instantShipLowestAsk` | Lowest ask and Instant Ship price, USD |
| `lowestAskSource` | `"search_match"` (the one variant matched) or `"all_sizes"` (true minimum, with Fetch full market data on) |
| `matchedSize`, `matchedCondition`, `matchedBoxCondition` | The variant GOAT's own search matched |
| `isUnderRetail`, `hasStock`, `status` | Whether it sits under retail, whether there is stock, listing status |
| `minimumOffer`, `maximumOffer` | The offer bounds GOAT enforces |
| `story`, `storyHtml` | The product write-up, plain and as HTML |
| `imageUrl`, `gridImageUrl`, `originalImageUrl` | Product images |
| `collectionSlugs` | GOAT collections the product is featured in |
| `market`, `currency`, `localPricing` | The market you asked for, and the retail price and ask in that market's own currency |
| `siteName`, `scrapedAt` | Provenance |
| `sizes[]` | With Fetch full market data on: one row per size, with condition, lowest ask, Instant Ship price, last sale and stock status |
| `lastSale`, `lastSaleLow`, `lastSaleHigh`, `totalSizeCount` | With Fetch full market data on: recent sale price roll up and market depth |
| `changeType`, `changedFields`, `firstSeenAt`, `lastSeenAt` | Incremental mode only: how the product changed since the last run |
| `priceChanges`, `variantChanges` | Compact updates only: old and new value of each moved price or stock field |
| `otherChanges` | Compact updates with change scope all only: old and new value of every other moved top level field, null when none |

With Fetch full market data on, a row also carries the full image gallery (`imageUrls`), every size option GOAT lists, classification flags (`taxonomy`, `isOfferable`, `isResellable`) and per condition listing counts. `detailCarriedForward` appears, and is `true`, only when a market fetch failed and the row was repaired from the previous run instead of left blank.

### How to Use

1. Pick a **mode**: `search` (by keyword) or `url` (paste GOAT links).
2. For search mode, fill in **Search terms**, or leave it empty and set filters alone to browse the whole catalogue. For URL mode, paste product, search, brand, collection or category links (leave the field untouched and url mode scrapes one example of each shape: a search page, a product page, a brand page and the sneakers category); the same filters apply to every pasted browse link.
3. Turn on **Fetch full market data** if you need the per size ask, Instant Ship price, last sale and stock table, and set **Max items** to control run size and cost.
4. Click **Start**, then download the dataset as JSON, CSV or Excel, or read it through the API.

**Keyword search:**

```json
{
  "mode": "search",
  "searchTerms": ["air jordan 1", "nike dunk low"],
  "maxItems": 50
}
```

**Browse the catalogue with filters alone (no search terms):**

```json
{
  "mode": "search",
  "searchTerms": [],
  "brands": ["Nike"],
  "activity": "basketball",
  "maxPriceUsd": 150,
  "releaseYearFrom": 2024,
  "maxItems": 200
}
```

**Paste GOAT links, with full market data:**

```json
{
  "mode": "url",
  "urls": [
    "https://www.goat.com/brands/new-balance",
    "https://www.goat.com/sneakers"
  ],
  "fetchDetails": true,
  "maxItems": 100
}
```

**A different ship-to market, Instant Ship stock only:**

```json
{
  "mode": "search",
  "searchTerms": ["yeezy 350"],
  "market": "GB",
  "instantShipOnly": true,
  "maxItems": 30
}
```

#### Run it from your code

Python:

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("abotapi/goat-sneaker-scraper").call(run_input={"mode": "search", "searchTerms": ["air jordan 1"], "maxItems": 20})
for product in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(product["name"], product["lowestAsk"])
```

JavaScript:

```javascript
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });
const run = await client.actor('abotapi/goat-sneaker-scraper').call({ mode: 'search', searchTerms: ['air jordan 1'], maxItems: 20 });
const { items } = await client.dataset(run.defaultDatasetId).listItems();
```

Or connect it to Make, Zapier, n8n, Google Sheets or webhooks from the **Integrations** tab.

#### Max items and GOAT's own limits

GOAT's own search returns at most 1000 products for any single search term, brand, collection or category link; the run logs it when this ceiling truncates a scope. **Max items** is the cap on top of that: raise it, or set it to `0`, to collect everything up to GOAT's own ceiling. **Max pages** is a separate safety bound on how many result pages one search term or link can walk; leave it at `0` to let Max items decide instead.

#### Resume and recurring updates

- **Resume** (`resumeFromRunId`) continues one interrupted run: paste its run or dataset id and the actor skips every product it already collected there, so you don't pay twice.
- **Incremental mode** (`incrementalMode`) is for scheduled runs over the same search, filters and market. Each product is classified `NEW`, `UPDATED` (with `changedFields`, since a price, ask or stock move is the signal on a resale marketplace, not noise), `UNCHANGED` (suppressed and not billed unless `emitUnchanged` is on), `REAPPEARED` or `EXPIRED` (only after a run that scanned the whole tracked scope, and only with `emitExpired`). `stateKey` names or shares the stored state, so two differently configured searches never mix. With incremental mode off, output is exactly as before.

**What is compared.** By default the whole product record: name, brand, colorway, release data, retail price, lowest ask, instant ship ask, last sale, stock flags and every size row with its ask and stock status. Excluded: the scrape timestamp and the carried-forward marker, which change on every run, plus any field you list in `ignoreFieldsForChanges`. The excluded fields stay in the output.

**Ignore fields (`ignoreFieldsForChanges`).** Field names to leave out of change detection, for example `color`. A dotted name reaches inside every size row while its ask and stock stay tracked, for example `sizes.sizeValue`. Unchanged is decided by re-checking the stored record against your current list, so editing the list does not return a wave of false updates. With no State key, a changed list starts a new automatic baseline (the first run returns everything as `NEW`); name a State key to keep one baseline.

**Price and stock monitoring (all optional, off by default).**

- `changeScope` = `priceAndStock`: only a move of the lowest ask, instant ship ask, retail price, under-retail flag, in-stock flag or last sale, or of a size row's ask, instant ship ask or stock status, returns `UPDATED`. Size rows are matched by size, shoe condition and box condition, in any order. Description, images and release data never do.
- `compactUpdates`: `UPDATED` and `EXPIRED` rows are short (ids, url, name, current ask, retail price and stock flags) with `priceChanges` and `variantChanges` showing old and new values; with the change scope set to all, `otherChanges` also holds the old and new value of any other moved top level field (null when none). `NEW` rows stay complete. The Fetch full market data event is charged only for a row that actually carries the market table, so a compact row does not trigger it.
- `expireAfterMissedScans` (default 1): with `emitExpired`, a product is marked `EXPIRED` only after it was missing from that many complete scans in a row. `EXPIRED` means no longer listed in this search; a listing drop and a delisting cannot be told apart without opening each product, so read availability from the stock fields.

#### Output views

The dataset has three tabs: **Overview** (identity, lowest ask, retail price and stock), **Detail (per-size market)** (the per size table when Fetch full market data is on) and **Changes (incremental)** (change type, changed fields, price and variant changes).

### Send results into your apps (MCP connectors)

Optionally pipe the scraped results into the apps you already use, via Model Context Protocol (MCP) connectors. This is an extra delivery step **after** the scrape: the Apify dataset is never changed and nothing extra is billed.

**What gets written to the connector:** a condensed, human-readable **summary** of each product, not the full JSON. The **complete record always stays in the Apify dataset**.

1. Authorize a connector once under **Apify, Settings, Integrations** (Notion, Linear, Airtable, or Apify).
2. Select it in the **"Pipe results into your apps"** input field (`mcpConnectors`). If the picker is empty, you have not authorized a connector yet.
3. For **Notion**, also set `notionParentPageUrl` to the page where items should be created.
4. `maxNotifyListings` caps how many items are written to each connector per run (default 50).

The connection is mediated by Apify's MCP proxy, so this actor never sees your third-party credentials. Leave the field empty to skip.

### Input Parameters

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `mode` | string | `search` | `search` (GOAT keyword search) or `url` (process pasted GOAT links). |
| `searchTerms` | array | `["air jordan 1"]` | Terms to search GOAT for (search mode). Leave empty with a filter set to browse the whole catalogue. |
| `urls` | array | one example of each shape (search, product, brand, category) | GOAT product, search, brand, collection or category links (url mode). With the field untouched, url mode scrapes one example of each shape. Add `?page=3` to a browse link to start walking from that page. |
| `brands` | array | (none) | Only these brands, OR-ed together. Applies in both modes. |
| `productCategory` | string | any | `shoes`, `clothing`, `accessories`, `bags` or `collectibles`. Applies in both modes. |
| `activity` | string | any | `lifestyle`, `running`, `basketball` or `skateboarding`. Applies in both modes. |
| `gender` | string | any | `men`, `women`, `youth` or `infant`. Applies in both modes. |
| `condition` | string | any | `new_no_defects`, `new_with_defects` or `used`. Also narrows the per size market table when Fetch full market data is on. |
| `sizeUs` | string | (none) | Only this US size, for example `10` or `9.5`. |
| `minPriceUsd` | integer | (none) | Only products whose lowest ask is at least this many USD. |
| `maxPriceUsd` | integer | (none) | Only products whose lowest ask is at most this many USD. |
| `releaseYearFrom` | integer | (none) | Only products released in this year or later. |
| `releaseYearTo` | integer | (none) | Only products released in this year or earlier. |
| `inStockOnly` | boolean | `false` | Only products that currently have stock on GOAT. |
| `instantShipOnly` | boolean | `false` | Only products with an Instant Ship price. |
| `underRetailOnly` | boolean | `false` | Only products whose lowest ask is below retail price. |
| `market` | string | `US` | Ship-to market: `US`, `GB`, `CA`, `AU`, `DE` or `JP`. Changes which sizes and prices come back; top level money fields stay USD. |
| `sortResultsBy` | string | `relevance` | Order of the rows this run collected (not the whole catalogue): `relevance`, `price_asc`, `price_desc`, `release_newest` or `release_oldest`. Use the price filters to target the cheapest or dearest listings across the whole catalogue. |
| `fetchDetails` | boolean | `false` | Fetch each product's live per size market table (lowest ask, Instant Ship price, last sold price, stock status), full image gallery, offer bounds and for sale counts. Charged once per product on top of the per result fee. |
| `maxItems` | integer | `20` | Stop after this many products across all search terms and links. `0` = unlimited (up to GOAT's own ceiling). |
| `maxPages` | integer | `0` | Safety bound on result pages walked per search term or link. `0` = no separate limit; Max items decides instead. |
| `resumeFromRunId` | string | (none) | Continue one interrupted run: paste its run id or dataset id. |
| `incrementalMode` | boolean | `false` | Return only new and changed products on scheduled runs over the same search. |
| `stateKey` | string | (none) | Name or share an incremental-mode state. Left empty, a key is derived automatically from the mode, search terms, links, filters, market and the Fetch full market data setting. |
| `emitUnchanged` | boolean | `false` | Also return (and bill) products unchanged since the last run. |
| `emitExpired` | boolean | `false` | Also return (and bill) products no longer found, after a complete scan of the tracked search. |
| `changeScope` | string | `all` | Incremental mode: `all` or `priceAndStock` (only price, ask and stock moves return `UPDATED`). |
| `compactUpdates` | boolean | `false` | Incremental mode: short `UPDATED` and `EXPIRED` rows with `priceChanges` and `variantChanges`. |
| `expireAfterMissedScans` | integer | `1` | With `emitExpired`: complete scans in a row a product must be missing before it is `EXPIRED`. |
| `ignoreFieldsForChanges` | array | `[]` | Incremental mode: field names (or dotted names such as `sizes.sizeValue`) excluded from change detection. |
| `mcpConnectors` | array | `[]` | Optional MCP connectors that receive a short summary of each product after the run. |
| `notionParentPageUrl` | string | no default (empty) | Notion parent page URL or id; needed only for the Notion connector. |
| `maxNotifyListings` | integer | `50` | Cap on items written to each connector per run. The dataset is unaffected. |
| `proxy` | object | Apify Proxy | Connection settings. |

### Output Example

> Sample shape: values are illustrative placeholders, not from a live record.

```json
{
  "productId": 90000001,
  "slug": "sample-brand-classic-runner-ab1234-100",
  "url": "https://www.goat.com/sneakers/sample-brand-classic-runner-ab1234-100",
  "name": "Sample Brand Classic Runner",
  "nickname": "Classic Runner",
  "brand": "Sample Brand",
  "sku": "AB1234-100",
  "colorway": "White/Black",
  "color": "White",
  "productCategory": "shoes",
  "productType": "sneakers",
  "gender": "men",
  "sizeRange": ["7", "8", "9", "10", "11"],
  "releaseDate": "2024-03-01T00:00:00.000Z",
  "releaseYear": 2024,
  "retailPrice": 120.0,
  "lowestAsk": 95.0,
  "instantShipLowestAsk": 110.0,
  "lowestAskSource": "all_sizes",
  "isUnderRetail": true,
  "hasStock": true,
  "status": "active",
  "imageUrl": "https://image.goat.com/sample-1.png",
  "market": "US",
  "currency": "USD",
  "localPricing": { "currency": "USD", "retailPrice": 120.0, "lowestAsk": 95.0 },
  "sizes": [
    {
      "size": "9",
      "shoeCondition": "new_no_defects",
      "boxCondition": "good_condition",
      "lowestAsk": 100.0,
      "instantShipLowestAsk": 115.0,
      "lastSale": 98.0,
      "stockStatus": "single_in_stock"
    },
    {
      "size": "10",
      "shoeCondition": "new_no_defects",
      "boxCondition": "good_condition",
      "lowestAsk": 95.0,
      "instantShipLowestAsk": 110.0,
      "lastSale": 102.0,
      "stockStatus": "multiple_in_stock"
    }
  ],
  "lowestAskSize": "10",
  "lastSale": 100.0,
  "lastSaleLow": 98.0,
  "lastSaleHigh": 102.0,
  "totalSizeCount": 2,
  "siteName": "GOAT",
  "scrapedAt": "2026-01-01T00:00:00Z"
}
```

### Plan Requirement

GOAT's product pages sit behind an anti-bot check that frequently challenges standard and datacenter connections, while its catalogue search is open on the connection you select. Catalogue results therefore load on the default Proxy setting, and per size market reads move to Apify Residential (which uses your own residential proxy traffic), then to a backup connection, when a connection is challenged repeatedly. Residential access is not included in Apify free plans; on those plans the actor continues on a backup connection and some market pages may still be unavailable. If a run is affected, the log says so:

- **Selected connection unavailable:** "The selected connection is unavailable to this run. Apify free plans do not include residential proxies. Continuing on a backup connection; some pages may still be unavailable."
- **Served by the backup:** "Served over the backup connection (the default connection was unavailable)."
- **Proxy refused (run fails):** "The proxy connection was refused for this account, so no page could be read. Your Apify plan does not include proxy access; a paid plan is required."
- **No results (run fails):** "No results: residential proxies are unavailable on this run (Apify free plans do not include them), and the backup connection could not load the requested GOAT pages."
- **Pages failed to load (run fails):** "Could not load any results page (the requests failed), so nothing was collected. This is a connection problem, not an empty search. Please try again later."

Billing: each product returned is billed as a **dataset item**. With Fetch full market data on, a **detail-enrichment** event is also billed, one per delivered product that carries the per size market table. It is charged as each enriched product is delivered (together with the batch of rows that carries it), never before the row is in your dataset; compact, unchanged, suppressed and failed rows are never charged it, and a catalogue only run never triggers it. The **Pricing** tab shows the current rates. Use **Max items** to cap cost.

### FAQ

#### How much does it cost?

You pay per product returned (dataset item), plus one detail-enrichment event per delivered product when Fetch full market data is switched on. The **Pricing** tab shows the current rates. Use **Max items** to cap the cost of any run.

#### Is it legal to scrape GOAT?

This actor collects only publicly available marketplace data. You are responsible for how you use it: follow GOAT's terms and the laws that apply to you, and get legal advice if you plan commercial redistribution. Product images and write ups may be subject to third party rights.

#### Can I get only new or changed products on a schedule?

Yes. Schedule the actor from the **Schedules** tab and turn on **Incremental mode**. Each run then returns only new and changed products, and unchanged ones are not billed. A price, ask or stock move is reported as an update, since that is the signal on a resale marketplace.

#### Why did a run return fewer products than I expected?

GOAT's own search returns at most 1000 products for any single search term, brand, collection or category link; a very broad scope is truncated there and the run says so in the log. Narrow it with the brand, category, size, price or release year filters to reach the rest, or check whether **Max items** stopped the run first.

#### Why did my run fail instead of returning an empty dataset?

If GOAT refuses every request, the run stops with a clear message so "nothing matched this search" is never confused with "nothing could be read". Run it again in a few minutes, or switch the Proxy setting to Apify Residential.

#### Can I automate this with my own tools?

Yes. Call it from the Apify API or client libraries, schedule it, or connect it to Make, Zapier, n8n, Google Sheets or webhooks from the **Integrations** tab.

### 🔗 Want more fashion data?

Pair this actor with these related scrapers from the same team:

<table>
<tr><td>👗 <a href="https://apify.com/abotapi/kream-scraper"><b>KREAM Scraper</b></a><br>Scrape KREAM (kream.co.kr) sneaker and fashion resale data by keyword search, filters...</td><td>👗 <a href="https://apify.com/abotapi/klekt-com-scraper"><b>KLEKT Sneaker &amp; Apparel Resale Scraper</b></a><br>Scrape sneaker and streetwear listings from KLEKT (klekt.com). Browse the catalog with...</td></tr>
<tr><td>👗 <a href="https://apify.com/abotapi/snkrdunk-com-scraper"><b>SNKRDUNK Scraper</b></a><br>Scrape SNKRDUNK (snkrdunk.com), Japan's sneaker and streetwear resale marketplace: JP and...</td><td>👗 <a href="https://apify.com/abotapi/stockx-market-data-scraper"><b>StockX Scraper</b></a><br>Scrape StockX market data by keyword, category or URL. Every row carries lowest ask...</td></tr>
<tr><td>🏷️ <a href="https://apify.com/abotapi/thredup-scraper"><b>ThredUp Scraper</b></a><br>Scrape ThredUp resale listings by keyword, department, brand, size, condition, price or...</td><td>👗 <a href="https://apify.com/abotapi/kohls-com-scraper"><b>Kohl's Scraper</b></a><br>Scrape Kohl's (kohls.com) products by keyword search or category browsing. Extract...</td></tr>
</table>

👉 [Browse all abotapi scrapers](https://apify.com/abotapi)

### 💬 Support

- 🐞 **Found a bug or a missing field?** Open a ticket on the [Issues tab](https://apify.com/abotapi/goat-sneaker-scraper/issues/open). We usually reply within hours.
- 🛠️ **Need another site, extra fields or a private build?** Email <contact@abotapi.com>.
- ⭐ **Enjoying it?** A quick review on the actor page helps other users find it.

# Actor input Schema

## `mode` (type: `string`):

How to start the scrape. 'search' runs GOAT keyword searches. 'url' processes the GOAT URLs you paste: a product URL is fetched as a single product, and a search, brand, collection or category URL is paged like a search. Only where the scope comes from differs: the filters below apply in BOTH modes.

## `searchTerms` (type: `array`):

One or more things to search GOAT for, for example 'air jordan 1', 'nike dunk low', 'yeezy 350'. Each term is scraped independently. Search mode only: in URL mode the scope is read out of each pasted GOAT URL instead. If you leave this empty and set at least one filter, the actor browses the whole catalogue narrowed by those filters.

## `urls` (type: `array`):

Paste GOAT product URLs (https://www.goat.com/sneakers/<slug>), search URLs (https://www.goat.com/search?query=...), brand pages (https://www.goat.com/brands/nike), collection pages (https://www.goat.com/collections/<slug>) or category pages (https://www.goat.com/sneakers). Add ?page=3 to any browse URL to start walking from that page instead of the first. The filters below are NOT ignored here: every pasted search, brand, collection or category URL is re-run through GOAT's search with them applied. A pasted product URL is the one exception, since it returns that single product in full.

## `brands` (type: `array`):

Only return products from these brands, for example 'Nike', 'Air Jordan', 'adidas', 'New Balance'. Several brands are combined with OR. Leave empty for every brand. Applies in BOTH search mode and URL mode. A pasted brand page URL overrides this filter for that URL only.

## `productCategory` (type: `string`):

Only return products in this category. Applies in BOTH search mode and URL mode. A pasted category page URL overrides this filter for that URL only.

## `activity` (type: `string`):

Only return products GOAT files under this activity. Applies in BOTH search mode and URL mode.

## `gender` (type: `string`):

Only return products cut for this audience. Applies in BOTH search mode and URL mode.

## `condition` (type: `string`):

Only return listings in this condition. This also narrows the per size market table when Fetch full market data is on, so the lowest ask and last sale you get back are for this condition only. Applies in BOTH search mode and URL mode.

## `sizeUs` (type: `string`):

Only return products listed in this US size, for example '10' or '9.5'. Leave empty for every size. Applies in BOTH search mode and URL mode.

## `minPriceUsd` (type: `integer`):

Only return products whose lowest ask is at least this many US dollars. Applies in BOTH search mode and URL mode.

## `maxPriceUsd` (type: `integer`):

Only return products whose lowest ask is at most this many US dollars. Applies in BOTH search mode and URL mode.

## `releaseYearFrom` (type: `integer`):

Only return products released in this year or later. Applies in BOTH search mode and URL mode.

## `releaseYearTo` (type: `integer`):

Only return products released in this year or earlier. Applies in BOTH search mode and URL mode.

## `inStockOnly` (type: `boolean`):

Only return products that currently have stock on GOAT. Applies in BOTH search mode and URL mode.

## `instantShipOnly` (type: `boolean`):

Only return products with an Instant Ship price, meaning stock GOAT already holds and can send immediately. Applies in BOTH search mode and URL mode.

## `underRetailOnly` (type: `boolean`):

Only return products whose lowest ask is below the original retail price. Applies in BOTH search mode and URL mode.

## `market` (type: `string`):

The market the prices apply to. GOAT offers different sizes at different prices per country, so this genuinely changes the asks, the last sale prices and which sizes come back. Top level money fields are always USD so rows stay comparable; the market's own currency is carried in localPricing. Applies in BOTH search mode and URL mode.

## `sortResultsBy` (type: `string`):

Order of the rows this run returns. GOAT's public search does not offer server side ordering, so this orders the products this run collected, not the whole catalogue. To target the cheapest or most expensive listings across the catalogue, use the price filters above instead. Applies in BOTH search mode and URL mode.

## `fetchDetails` (type: `boolean`):

Fetch each product's live per size market table: lowest ask, Instant Ship price, LAST SOLD price and stock status for every size, plus the full image gallery, the offer bounds and the for sale counts. This also replaces the single sampled ask with the true lowest ask across all sizes (the row then reports lowestAskSource = all_sizes). Slower, and charged once per product. Leave off for a fast catalogue crawl: identity, retail price, release date, images, story and a sampled ask are included either way.

## `maxItems` (type: `integer`):

The single cap on this run: stop after collecting this many products across all search terms and URLs. Set 0 for unlimited.

## `maxPages` (type: `integer`):

Safety bound on how many result pages to walk per search term or URL. Leave empty (0) for no page limit: the run stops at Max items, not here. Does not impose a separate cap below Max items. GOAT itself returns at most 1000 products per search, so a very broad scope needs the filters above rather than more pages.

## `proxy` (type: `object`):

Leave the default for the cheapest run. If market reads are challenged repeatedly the actor moves them to Apify Residential by itself (billed to your residential proxy traffic); you can also choose Apify Residential here from the start.

## `resumeFromRunId` (type: `string`):

Paste a previous run ID or dataset ID to continue a large crawl without returning or charging for products already collected there. Use this after an interrupted or capped run. For recurring monitoring of the same search, use Incremental mode below instead.

## `incrementalMode` (type: `boolean`):

Turn this on for daily or weekly recurring monitoring of the same search. The first run returns all matching products as NEW. Later runs normally return only NEW, UPDATED and REAPPEARED products. Compared: the whole product record (name, brand, colorway, release data, retail price, lowest ask, instant ship ask, last sale, stock flags and every size row with its ask and stock status). Excluded: the scrape timestamp and the carried-forward marker, which change on every run, plus any field you list in Ignore fields. A price, ask or stock move IS reported as UPDATED: on a resale marketplace it is the signal, not noise. To be told only about price and stock moves, set What counts as a change to Price and stock only. Turn on Emit unchanged or Emit expired only when you also want those rows returned (and billed). State is kept separately per search, URL, filter and market setup; use State key to name or deliberately share a monitoring campaign. To continue one specific interrupted run instead, use Resume from a previous run above.

## `stateKey` (type: `string`):

Optional. Name this monitoring campaign to keep its state stable, or to deliberately share state across differently configured runs. Leave empty to let the actor derive a key automatically from the search terms, URLs, filters and market, so two different searches never mix state.

## `emitUnchanged` (type: `boolean`):

Off by default. Turn on to also return products that have not changed since the last run, marked UNCHANGED. This returns, and bills, extra rows you already have, so leave it off unless you specifically want a full snapshot every run.

## `emitExpired` (type: `boolean`):

Off by default. Turn on to also return products that were present in a previous run but are no longer found, marked EXPIRED. Only produced once a run has fully scanned the tracked search: not when Max items capped it, not when Resume was used, not when the run was blocked, and not when GOAT's 1000 product search limit truncated the scope. This returns, and bills, extra synthetic rows, so leave it off unless you need expiry tracking.

## `changeScope` (type: `string`):

All fields (default): any change to the product record, except the scrape timestamp, returns an UPDATED row. Price and stock only: only a move of the lowest ask, instant ship ask, retail price, under-retail flag, in-stock flag or last sale, or of a size row's ask, instant ship ask or stock status (rows are matched by size, shoe condition and box condition, in any order) returns an UPDATED row; changes to descriptions, images, release data or other details never do. With no State key, changing this option starts a new automatic baseline (the first run returns everything as NEW); name a State key to keep one baseline.

## `compactUpdates` (type: `boolean`):

Off by default. When on, UPDATED (and EXPIRED) rows are short: ids, url, name, current ask, retail price and stock flags, plus priceChanges and variantChanges showing the old and new value of each moved field per size row. With the change scope set to all, otherChanges also lists the old and new value of any other moved top level field (null when none). NEW rows always carry the complete record. The Fetch full market data fee is not charged for a compact row, because the full market table is not in it.

## `expireAfterMissedScans` (type: `integer`):

Only used with Emit expired products. A product is marked EXPIRED only after it has been missing from this many complete scans in a row (default 1). EXPIRED means the product is no longer listed in this search; a listing drop and a delisting cannot be told apart without opening each product, so use the stock fields for availability. A product that comes back resets the count.

## `ignoreFieldsForChanges` (type: `array`):

Optional. Field names to exclude from UPDATED change detection (they stay in the output). The scrape timestamp is always excluded. Use a dotted name to ignore a field inside every size row while still tracking its ask and stock, for example sizes.sizeValue. Ignoring sizes itself would hide size-level price and stock moves. Changing this list with no State key starts a new automatic baseline.

## `mcpConnectors` (type: `array`):

Optionally send the scraped results into the apps you already use, via Model Context Protocol (MCP) connectors. Authorize a connector once under Apify, Settings, Integrations, then select it here. The connector receives a condensed, human-readable summary per item (title + key fields), not the full JSON; the complete record stays in the dataset. Leave empty to skip. Supported: Notion (https://mcp.notion.com/mcp), Linear (https://mcp.linear.app/sse), Airtable (https://mcp.airtable.com/mcp), Apify (https://mcp.apify.com).

## `notionParentPageUrl` (type: `string`):

URL (or id) of the Notion page under which item pages are created. Required to enable the Notion export; ignored by other connectors.

## `maxNotifyListings` (type: `integer`):

Cap on items written to each connector per run. Does not affect the dataset.

## Actor input object example

```json
{
  "mode": "search",
  "searchTerms": [
    "air jordan 1"
  ],
  "urls": [
    "https://www.goat.com/search?query=air+jordan+1",
    "https://www.goat.com/sneakers/air-jordan-1-mid-chicago-554724-173",
    "https://www.goat.com/brands/nike",
    "https://www.goat.com/sneakers"
  ],
  "productCategory": "",
  "activity": "",
  "gender": "",
  "condition": "",
  "inStockOnly": false,
  "instantShipOnly": false,
  "underRetailOnly": false,
  "market": "US",
  "sortResultsBy": "relevance",
  "fetchDetails": true,
  "maxItems": 20,
  "maxPages": 0,
  "proxy": {
    "useApifyProxy": true
  },
  "incrementalMode": false,
  "emitUnchanged": false,
  "emitExpired": false,
  "changeScope": "all",
  "compactUpdates": false,
  "expireAfterMissedScans": 1,
  "ignoreFieldsForChanges": [],
  "mcpConnectors": [],
  "maxNotifyListings": 50
}
```

# Actor output Schema

## `overview` (type: `string`):

No description

## `detail` (type: `string`):

No description

## `changes` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "mode": "search",
    "searchTerms": [
        "air jordan 1"
    ],
    "urls": [
        "https://www.goat.com/search?query=air+jordan+1",
        "https://www.goat.com/sneakers/air-jordan-1-mid-chicago-554724-173",
        "https://www.goat.com/brands/nike",
        "https://www.goat.com/sneakers"
    ],
    "market": "US",
    "fetchDetails": true,
    "maxItems": 20,
    "proxy": {
        "useApifyProxy": true
    },
    "incrementalMode": false,
    "emitUnchanged": false,
    "emitExpired": false,
    "compactUpdates": false,
    "expireAfterMissedScans": 1,
    "ignoreFieldsForChanges": [],
    "mcpConnectors": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("abotapi/goat-sneaker-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "mode": "search",
    "searchTerms": ["air jordan 1"],
    "urls": [
        "https://www.goat.com/search?query=air+jordan+1",
        "https://www.goat.com/sneakers/air-jordan-1-mid-chicago-554724-173",
        "https://www.goat.com/brands/nike",
        "https://www.goat.com/sneakers",
    ],
    "market": "US",
    "fetchDetails": True,
    "maxItems": 20,
    "proxy": { "useApifyProxy": True },
    "incrementalMode": False,
    "emitUnchanged": False,
    "emitExpired": False,
    "compactUpdates": False,
    "expireAfterMissedScans": 1,
    "ignoreFieldsForChanges": [],
    "mcpConnectors": [],
}

# Run the Actor and wait for it to finish
run = client.actor("abotapi/goat-sneaker-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "mode": "search",
  "searchTerms": [
    "air jordan 1"
  ],
  "urls": [
    "https://www.goat.com/search?query=air+jordan+1",
    "https://www.goat.com/sneakers/air-jordan-1-mid-chicago-554724-173",
    "https://www.goat.com/brands/nike",
    "https://www.goat.com/sneakers"
  ],
  "market": "US",
  "fetchDetails": true,
  "maxItems": 20,
  "proxy": {
    "useApifyProxy": true
  },
  "incrementalMode": false,
  "emitUnchanged": false,
  "emitExpired": false,
  "compactUpdates": false,
  "expireAfterMissedScans": 1,
  "ignoreFieldsForChanges": [],
  "mcpConnectors": []
}' |
apify call abotapi/goat-sneaker-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,abotapi/goat-sneaker-scraper"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/7DyUKduUKOcl95PdS/builds/1scIQt1DggpgeN9qY/openapi.json
