{
  "openapi": "3.0.1",
  "info": {
    "title": "Google Play Reviews & Play Store Scraper – Ratings, Dates",
    "description": "Google Play reviews and app details by package ID or app-name search. Filter by star rating, keyword, reply status, helpfulness votes or date range, so you are only charged for the rows you want. Country/language targeting, HTTP-only: no browser, no proxy, no login. Includes Google Play ratings.",
    "version": "0.1",
    "x-build-id": "VE3sXaZqIaUGN0p75"
  },
  "servers": [
    {
      "url": "https://api.apify.com/v2"
    }
  ],
  "paths": {
    "/acts/fetchsmith~google-play-reviews-scraper/run-sync-get-dataset-items": {
      "post": {
        "operationId": "run-sync-get-dataset-items-fetchsmith-google-play-reviews-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/fetchsmith~google-play-reviews-scraper/runs": {
      "post": {
        "operationId": "runs-sync-fetchsmith-google-play-reviews-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/fetchsmith~google-play-reviews-scraper/run-sync": {
      "post": {
        "operationId": "run-sync-fetchsmith-google-play-reviews-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",
        "properties": {
          "appIds": {
            "title": "App IDs or Play Store URLs",
            "type": "array",
            "description": "Google Play package names (e.g. com.spotify.music) OR full Play Store URLs (e.g. https://play.google.com/store/apps/details?id=com.spotify.music&hl=en) — paste either, the package name is extracted from the URL's ?id= param.",
            "items": {
              "type": "string"
            }
          },
          "searchTerms": {
            "title": "Search terms",
            "type": "array",
            "description": "Alternative to App IDs: search terms; the top matching app(s) for each term will have their reviews fetched. If \"genres\" is set, the top 5 matches per term are checked and the first genre-matching one is used instead of blindly taking the top hit.",
            "items": {
              "type": "string"
            }
          },
          "country": {
            "title": "Country code",
            "type": "string",
            "description": "Play Store country code (affects app availability and review language).",
            "default": "us"
          },
          "language": {
            "title": "Language code",
            "type": "string",
            "description": "Language for reviews and app details.",
            "default": "en"
          },
          "sort": {
            "title": "Sort order",
            "enum": [
              "NEWEST",
              "RATING",
              "HELPFULNESS"
            ],
            "type": "string",
            "description": "Review sort order. Note: the fetch cap (maxReviewsPerApp) is applied to this sort order BEFORE ratingFilter, so RATING sort fills the cap with the highest-rated reviews first — hunting for low ratings with ratingFilter/maxScore returns 0 rows even when low-rated reviews exist, and raising maxReviewsPerApp does NOT help (verified 2026-09-25: on Spotify even the 5000 maximum kept 0 rows, all 5000 fetched were 5★). Use NEWEST (the default) when filtering for specific star ratings.",
            "default": "NEWEST"
          },
          "maxReviewsPerApp": {
            "title": "Max reviews per app",
            "minimum": 1,
            "maximum": 5000,
            "type": "integer",
            "description": "Fetch cap per app, counted before rating/keyword/appVersion/date filters are applied — a low cap combined with a narrow filter can miss matches deeper in the feed (see README FAQ). Raising it cannot help when sort=RATING excludes the star ratings you want; switch to NEWEST instead.",
            "default": 100
          },
          "includeAppDetails": {
            "title": "Include app details record",
            "type": "boolean",
            "description": "Also push one extra item per app with title, developer, rating, installs, price, description.",
            "default": true
          },
          "genres": {
            "title": "App genres",
            "type": "array",
            "description": "Only scrape apps whose Google Play genre is in this list; every other app is skipped before a single review is fetched, so you are not charged for it. Accepts the Play genre id (\"GAME_STRATEGY\", \"EDUCATION\"), the display name (\"Strategy\", \"Music & Audio\"), or the shorthand \"GAME\" for any game genre — all case-insensitive. Most useful as a guard on \"searchTerms\": with this set, a term's top 5 search hits are checked (instead of just 1) and the first genre-matching one is used, so a wrong-genre top hit no longer zeroes out that term. Needs one app-details lookup per app, so it works even with \"includeAppDetails\" off. If the genre cannot be read, the app is skipped rather than scraped unfiltered.",
            "default": [],
            "items": {
              "type": "string"
            }
          },
          "maxResults": {
            "title": "Max total results",
            "minimum": 1,
            "maximum": 20000,
            "type": "integer",
            "description": "Hard cap across all apps and app-detail records.",
            "default": 500
          },
          "minScore": {
            "title": "Minimum star rating",
            "minimum": 1,
            "maximum": 5,
            "type": "integer",
            "description": "Only keep reviews with a star rating >= this (1-5)."
          },
          "maxScore": {
            "title": "Maximum star rating",
            "minimum": 1,
            "maximum": 5,
            "type": "integer",
            "description": "Only keep reviews with a star rating <= this (1-5)."
          },
          "ratingFilter": {
            "title": "Exact star ratings",
            "type": "array",
            "description": "Only keep reviews whose star rating is one of these values, e.g. [1, 2, 5]. Use this instead of Minimum/Maximum when you want a non-contiguous set (Min/Max can only express a range). Applied on top of Min/Max if both are set.",
            "default": [],
            "items": {
              "type": "string"
            }
          },
          "keyword": {
            "title": "Keyword filter",
            "type": "string",
            "description": "Only keep reviews whose title or text contains this word/phrase (case-insensitive). For several words, use \"Keyword filters (any of)\" below. Leading/trailing spaces are ignored. Accented letters are Unicode-normalized before matching, so \"café\" matches regardless of which Unicode form (composed or decomposed) you typed it in."
          },
          "keywords": {
            "title": "Keyword filters (any of)",
            "type": "array",
            "description": "Only keep reviews whose title or text contains AT LEAST ONE of these words/phrases (case-insensitive). Combined with the single Keyword filter using AND if both are set.",
            "default": [],
            "items": {
              "type": "string"
            }
          },
          "appVersions": {
            "title": "App versions",
            "type": "array",
            "description": "Only keep reviews written against one of these app versions, e.g. [\"9.0.30.567\"]. Note: Google Play leaves the version null on many reviews — those are dropped when this filter is set.",
            "default": [],
            "items": {
              "type": "string"
            }
          },
          "minThumbsUp": {
            "title": "Minimum thumbs up",
            "minimum": 0,
            "type": "integer",
            "description": "Only keep reviews with at least this many \"helpful\" votes (thumbs up) from other users. Google Play always reports this as 0 on a review with no votes, never null, so no reviews are dropped for missing data the way an app-version filter can drop null versions."
          },
          "replyFilter": {
            "title": "Developer reply",
            "enum": [
              "any",
              "hasReply",
              "noReply"
            ],
            "type": "string",
            "description": "\"Has a reply\" finds reviews the developer already responded to (e.g. to audit response quality). \"No reply\" finds reviews still waiting for a response — the common support-queue use case.",
            "default": "any"
          },
          "minReviewLength": {
            "title": "Minimum review text length",
            "minimum": 1,
            "type": "integer",
            "description": "Only keep reviews whose text is at least this many characters long. Filters out low-effort one-word/emoji-only reviews. A review with no text at all (title-only, or a bare star rating) is always dropped once this is set, since it can never meet a length floor above 0."
          },
          "sinceDate": {
            "title": "Since date",
            "type": "string",
            "description": "Only keep reviews posted on/after this date (ISO 8601, e.g. 2026-01-01)."
          },
          "untilDate": {
            "title": "Until date",
            "type": "string",
            "description": "Only keep reviews posted on/before this date (ISO 8601, e.g. 2026-06-01)."
          },
          "watchLabel": {
            "title": "Watch label (only new reviews since last run)",
            "type": "string",
            "description": "Optional. Name a saved watch (e.g. \"my-app-alerts\") and this run returns ONLY reviews not delivered under that same label and filter set before, instead of the same top-of-feed reviews every time. The first run for a label is a free baseline: it records the reviews that already exist (walking at least 1000 per app, up to 5000 total) and returns zero rows. Run it again later — on a schedule, typically — to get only what is new. The baseline is kept in your own Apify account (a named key-value store \"fetchsmith-google-play-reviews-watch\"), keyed by label plus a fingerprint of apps/search terms/country/language/sort and every rating, keyword, app-version and date filter, so changing any of those starts a fresh baseline instead of hiding newly-matching reviews as \"already seen\". Use sort=NEWEST. App-detail records are only returned on a run where that app actually has a new review, so a run with nothing new costs nothing."
          },
          "watchEvents": {
            "title": "Watch events to report",
            "type": "array",
            "description": "Optional. Only report these kinds of change in watch mode — e.g. pick \"Star rating edited\" alone to be alerted only when an existing reviewer downgrades or upgrades their score, and never pay for anything else. Leave empty for all four. Google Play lets a reviewer edit a review in place (same review id), and lets a developer add or delete a reply at any time — without these events an already-delivered review that later drops from 5★ to 1★, or that finally gets a reply, is invisible to a watch forever. Change events compare against the star rating and reply state recorded the last time the review was delivered, so they only start firing on the second run after a baseline created before this feature existed. Ignored when \"Watch label\" is empty.",
            "items": {
              "type": "string",
              "enum": [
                "new",
                "scoreChanged",
                "developerReplied",
                "replyRemoved"
              ],
              "enumTitles": [
                "New review",
                "Star rating edited (reviewer changed their own score)",
                "Developer replied (reply newly added)",
                "Developer reply removed"
              ]
            },
            "default": []
          },
          "webhookUrl": {
            "title": "Webhook URL (notify on completion)",
            "type": "string",
            "description": "Optional. An http(s) URL to POST a small JSON summary to when the run finishes — reviews pushed, watch-label new count if Watch label is set, and the run's dataset ID so you can fetch the results. A convenience for callers who want a completion ping without setting up an Apify platform webhook (which needs separate Console/API configuration per Task, not per run). Best-effort: a failed or slow webhook is logged as a warning and never fails the run or affects charging — it fires after every review has already been pushed and charged. Leave empty to skip."
          }
        }
      },
      "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
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}