{
  "openapi": "3.0.1",
  "info": {
    "title": "Fresha Scraper - Salons, Spas, Services & Reviews",
    "description": "Scrape Fresha salon and spa listings: business profiles, service menus with prices and durations, opening hours, team members, photos, ratings and client reviews. Search by keyword and location or paste venue and professional links. Resume, incremental updates and MCP export included.",
    "version": "0.1",
    "x-build-id": "Qnx6qg1aTLqoFj8U0"
  },
  "servers": [
    {
      "url": "https://api.apify.com/v2"
    }
  ],
  "paths": {
    "/acts/abotapi~fresha-salon-spa-scraper/run-sync-get-dataset-items": {
      "post": {
        "operationId": "run-sync-get-dataset-items-abotapi-fresha-salon-spa-scraper",
        "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/abotapi~fresha-salon-spa-scraper/runs": {
      "post": {
        "operationId": "runs-sync-abotapi-fresha-salon-spa-scraper",
        "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/abotapi~fresha-salon-spa-scraper/run-sync": {
      "post": {
        "operationId": "run-sync-abotapi-fresha-salon-spa-scraper",
        "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": [
          "mode"
        ],
        "properties": {
          "mode": {
            "title": "Mode",
            "enum": [
              "SEARCH",
              "LISTING_URLS"
            ],
            "type": "string",
            "description": "SEARCH scrapes Fresha listings for a keyword and location. LISTING_URLS scrapes the exact venue pages (fresha.com/a/...) and professional profiles (fresha.com/p/...) you paste.",
            "default": "SEARCH"
          },
          "searchQuery": {
            "title": "Search keyword",
            "type": "string",
            "description": "Service or business keyword, e.g. hair, massage, nails, barber, facial, tattoo. Used in SEARCH mode.",
            "default": "hair"
          },
          "searchLocation": {
            "title": "Location",
            "type": "string",
            "description": "City or area name, e.g. Sydney, London, New York. Resolved automatically to map coordinates. Leave blank when latitude/longitude or a venue URL list is provided.",
            "default": "Sydney"
          },
          "latitude": {
            "title": "Latitude",
            "type": "string",
            "description": "Optional map latitude. When set together with longitude it overrides the resolved Location."
          },
          "longitude": {
            "title": "Longitude",
            "type": "string",
            "description": "Optional map longitude. When set together with latitude it overrides the resolved Location."
          },
          "maxPages": {
            "title": "Max pages per search",
            "type": "integer",
            "description": "How many result pages (20 listings each) to walk per search. Leave blank or 0 for unlimited until Max items is reached."
          },
          "sort": {
            "title": "Sort results by",
            "enum": [
              "RECOMMENDED",
              "RATING",
              "DISTANCE"
            ],
            "type": "string",
            "description": "Ordering of search results. RATING and DISTANCE are narrowed assertions: results come back in that order.",
            "default": "RECOMMENDED"
          },
          "minPrice": {
            "title": "Min price",
            "type": "integer",
            "description": "Optional minimum service price filter in the local currency of the search area."
          },
          "maxPrice": {
            "title": "Max price",
            "type": "integer",
            "description": "Optional maximum service price filter in the local currency of the search area."
          },
          "hasDeals": {
            "title": "Only venues with deals",
            "type": "boolean",
            "description": "Return only venues currently running deals or offers.",
            "default": false
          },
          "hasGroupAppointments": {
            "title": "Only group appointments",
            "type": "boolean",
            "description": "Return only venues offering group appointments or classes.",
            "default": false
          },
          "freshaVerifiedOnly": {
            "title": "Only Fresha-verified venues",
            "type": "boolean",
            "description": "Return only venues carrying the Fresha Verified badge.",
            "default": false
          },
          "availabilityDate": {
            "title": "Available on date",
            "type": "string",
            "description": "Only venues with bookable availability on this date (YYYY-MM-DD). Used in SEARCH mode."
          },
          "listingUrls": {
            "title": "Venue & professional URLs",
            "type": "array",
            "description": "Fresha URLs to scrape in LISTING_URLS mode. Supported shapes, which can be mixed freely: venue pages (fresha.com/a/...), professional profiles (fresha.com/p/...), SEO landing pages (fresha.com/lp/..., harvested for the venue links they contain), localized venue links in the short locale form (e.g. fresha.com/de/a/...), and venue links carrying tracking parameters (?utm_source=..., fbclid=... — matched the same as clean venue links). Book-now links (fresha.com/book-now/...) also resolve to their venue when encountered. The URL mode is selected by Mode above.",
            "default": [
              {
                "url": "https://www.fresha.com/a/rachel-french-hairdresser-sydney-24-30-springfield-avenue-u0by2m6f"
              },
              {
                "url": "https://www.fresha.com/p/suha-alsayah-5784524"
              },
              {
                "url": "https://www.fresha.com/lp/en/bt/spas/in/au-sydney"
              },
              {
                "url": "https://www.fresha.com/de/a/rachel-french-hairdresser-sydney-24-30-springfield-avenue-u0by2m6f"
              }
            ],
            "items": {
              "type": "object",
              "required": [
                "url"
              ],
              "properties": {
                "url": {
                  "type": "string",
                  "title": "URL of a web page",
                  "format": "uri"
                }
              }
            }
          },
          "fetchDetails": {
            "title": "Full profile enrichment",
            "type": "boolean",
            "description": "Fetch each listing's full profile: description, contact number, opening hours, service menu with prices and durations, packages, team members, photo gallery, Instagram, amenities and review highlights. Adds the detail-enrichment charge per listing. Without it you still get the listing card: name, rating, review count, badges, address, geo and photos.",
            "default": true
          },
          "fetchReviews": {
            "title": "Collect client reviews",
            "type": "boolean",
            "description": "Attach client reviews to each listing: rating, text, date, author, salon reply and review photos, plus the 1-5 star distribution. Adds the review-enrichment charge per listing. First page is included; deeper pages are walked automatically.",
            "default": true
          },
          "maxReviewsPerListing": {
            "title": "Max reviews per listing",
            "type": "integer",
            "description": "Stop collecting reviews for a listing after this many. 0 means no limit (walk every available page).",
            "default": 20
          },
          "reviewSorting": {
            "title": "Review order",
            "enum": [
              "LATEST",
              "BEST",
              "WORST",
              "RELEVANCE"
            ],
            "type": "string",
            "description": "Order in which reviews are collected.",
            "default": "LATEST"
          },
          "maxItems": {
            "title": "Max items",
            "type": "integer",
            "description": "Stop after this many listing records (reviews attached to a record do not count as items). Run stops gracefully at the cap.",
            "default": 20
          },
          "proxy": {
            "title": "Proxy connection",
            "type": "object",
            "description": "Datacenter proxies are the default and work on every plan (measured working on every endpoint of this site). Residential proxies are recommended for the most reliable results on large or frequent runs. If the connection is refused, the actor escalates automatically through backup connections, including residential ones; that residential traffic is billed to your Apify plan when it engages. The run always uses a proxy unless you explicitly turn it off here or supply your own proxy URLs.",
            "default": {
              "useApifyProxy": true
            }
          },
          "resumeFromRunId": {
            "title": "Resume from run ID",
            "type": "string",
            "description": "ID of an interrupted run to continue. The run picks up after the last listing already saved and reuses its key-value state. Leave empty for a fresh run."
          },
          "incrementalMode": {
            "title": "Incremental mode",
            "type": "boolean",
            "description": "Compare listings against the previous run for the same scope (state key) and only emit NEW or UPDATED rows (plus optional EXPIRED tombstones). Unchanged listings are skipped and not charged. A listing previously marked EXPIRED that is found again is emitted as REAPPEARED with its original firstSeenAt. What drives UPDATED: every emitted listing field except the exclusions below and run bookkeeping (scrapedAt, changeType, changedFields, firstSeenAt, lastSeenAt) - e.g. the services menu (names, prices, durations), rating, reviewsCount, address and coordinates, images, amenities, social links. Never compared, so they never mark a row UPDATED on their own (they rotate or change on their own without the listing changing): portfolioImages, recentReviewers, openingStatus, openingStatusDetails, closed, workingHours, instagramMediaCount, groupingScore, topReviewedServices, reviewTeamMembers. Review order is ignored too: reviews are compared as an orderless set of id, rating, text, date and reply. Extend the exclusions with ignoreFieldsForChanges.",
            "default": false
          },
          "stateKey": {
            "title": "State key",
            "type": "string",
            "description": "Name of the remembered baseline for incremental mode. Defaults to a hash of the current scope (mode, query, location, URL set), so two runs with the same settings compare against each other automatically. Change it to track a different baseline."
          },
          "emitUnchanged": {
            "title": "Emit unchanged records",
            "type": "boolean",
            "description": "In incremental mode, also emit rows classified UNCHANGED (handy for full snapshots or debugging). Unchanged rows still carry changeType so you can filter.",
            "default": false
          },
          "emitExpired": {
            "title": "Emit expired tombstones",
            "type": "boolean",
            "description": "In incremental mode, emit an EXPIRED row for listings present in the baseline but missing from this run. Tombstones respect Max items.",
            "default": false
          },
          "ignoreFieldsForChanges": {
            "title": "Ignore fields when detecting changes (incremental mode only)",
            "type": "array",
            "description": "Extra dataset fields that must NOT mark a record UPDATED when they change (rotation noise like image CDNs). Sensible defaults are built in; anything added here is appended.",
            "items": {
              "type": "string"
            }
          },
          "mcpConnectors": {
            "title": "Pipe results into your apps (optional)",
            "type": "array",
            "description": "Optionally send results into the apps you already use, via Model Context Protocol (MCP) connectors. Authorize one under Apify → Settings → API & Integrations, then select it here. Notion gets a rich page-per-item export; other connectors get a best-effort write/digest. Leave empty to skip; never changes the dataset output. Supported: Notion (https://mcp.notion.com/mcp), Linear (https://mcp.linear.app/sse), Airtable (https://mcp.airtable.com/mcp), Apify (https://mcp.apify.com).",
            "default": [],
            "items": {
              "type": "string"
            }
          },
          "notionParentPageUrl": {
            "title": "Notion parent page (Notion connector only)",
            "type": "string",
            "description": "URL (or id) of the Notion page under which item pages are created. Required to enable the Notion export; ignored by other connectors.",
            "default": ""
          },
          "maxNotifyListings": {
            "title": "Max items to export per connector",
            "minimum": 1,
            "maximum": 1000,
            "type": "integer",
            "description": "Cap on items written to each connector per run. Does not affect the dataset.",
            "default": 50
          }
        }
      },
      "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
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}