{
  "openapi": "3.0.1",
  "info": {
    "title": "Apple App Store Reviews Scraper — iOS App Reviews & Ratings",
    "description": "Apple App Store reviews by country for an app id, URL or app name, every storefront in one run. One row per review: 1-5 rating, title, text, reviewer, date, developer response. Free app row: App Store ratings. Filters before billing: star rating (negative reviews), since date (new reviews).",
    "version": "1.0",
    "x-build-id": "HRhJrhlbyEcDNO3P4"
  },
  "servers": [
    {
      "url": "https://api.apify.com/v2"
    }
  ],
  "paths": {
    "/acts/kestrel~app-store-reviews-scraper/run-sync-get-dataset-items": {
      "post": {
        "operationId": "run-sync-get-dataset-items-kestrel-app-store-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/kestrel~app-store-reviews-scraper/runs": {
      "post": {
        "operationId": "runs-sync-kestrel-app-store-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/kestrel~app-store-reviews-scraper/run-sync": {
      "post": {
        "operationId": "run-sync-kestrel-app-store-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",
            "uniqueItems": true,
            "type": "array",
            "description": "Apple numeric app ids, e.g. [\"310633997\"] (the digits after /id in any App Store link) — the fastest input, and what an earlier run's app_id gives you. Each id is read on every storefront in countries. An id that is not an app on a storefront costs nothing: its status row says not_found. Use for \"reviews of app id 310633997\".",
            "default": [],
            "items": {
              "type": "string"
            }
          },
          "startUrls": {
            "title": "App Store URLs",
            "uniqueItems": true,
            "type": "array",
            "description": "App Store app pages, e.g. [\"https://apps.apple.com/us/app/whatsapp-messenger/id310633997\"]. Only the id is read from the URL; the country in the URL is NOT used — countries decides which storefronts are read. A URL from another site ends the run before anything is charged. A URL and its id are the same app and are read once.",
            "default": [],
            "items": {
              "type": "string"
            }
          },
          "appNames": {
            "title": "App names",
            "uniqueItems": true,
            "type": "array",
            "description": "App names as typed into App Store search, e.g. [\"Duolingo\", \"Babbel\"]: each is resolved to Apple's top search hit on each storefront, no id hunting. Use for \"reviews of the Duolingo app\". An ambiguous name (\"Weather\") takes the top hit: check app_name on the app row, or pass the id. A name that matches nothing costs nothing; its status row carries the reason.",
            "default": [],
            "items": {
              "type": "string"
            }
          },
          "countries": {
            "title": "Countries (storefronts)",
            "uniqueItems": true,
            "type": "array",
            "description": "Storefront codes, e.g. [\"us\", \"gb\", \"de\", \"jp\"]; default [\"us\"]. Reviews live per storefront, so a global app needs several: each app is read on each one and every row carries country. Known (48): us gb ca au de fr it es nl se no dk fi ch at be ie pt pl cz ru tr il ae sa eg za ng ke br mx ar cl co pe jp kr cn tw hk sg in nz ph id my th vn; any other code ends the run before anything is charged. A review Apple lists on two storefronts is delivered and billed once.",
            "default": [
              "us"
            ],
            "items": {
              "type": "string"
            }
          },
          "maxReviewsPerApp": {
            "title": "Max reviews per app per country",
            "minimum": 0,
            "maximum": 100000,
            "type": "integer",
            "description": "Reviews to read per app AND per storefront, e.g. 200; default 100; 0 = every review Apple pages out (tens of thousands for big apps: cap the run with maxTotalChargeUsd). The main cost control: at most N × $0.004 per app and country. The filters (minRating, maxRating, sinceDate, requireText) apply to these N reads, so 300 with maxRating 2 delivers the 1-2 star reviews among the 300 read — pair it with sort \"most_critical\" so the N read are the critical ones. Apple pages 10 reviews per request.",
            "default": 100
          },
          "sort": {
            "title": "Order",
            "enum": [
              "most_recent",
              "most_helpful",
              "most_critical",
              "most_favorable"
            ],
            "type": "string",
            "description": "\"most_recent\" (default; schedule this), \"most_helpful\" (the App Store's own order), \"most_critical\" (1-star first: the cheap way to a complaints feed), \"most_favorable\" (5-star first). Applied by Apple before maxReviewsPerApp: \"most_critical\" with maxReviewsPerApp 100 reads the 100 most critical reviews of each storefront.",
            "default": "most_recent"
          },
          "minRating": {
            "title": "Only reviews rated at least",
            "minimum": 0,
            "maximum": 5,
            "type": "integer",
            "description": "0 (default) = no lower bound. 1-5 = keep only reviews with at least this many stars, e.g. 4 for praise only. Applied after the reviews are read and before billing: reviews below it are never charged (the status row counts them in filtered). Pair with sort \"most_favorable\".",
            "default": 0
          },
          "maxRating": {
            "title": "Only reviews rated at most",
            "minimum": 0,
            "maximum": 5,
            "type": "integer",
            "description": "0 (default) = no upper bound. 1-5 = keep only reviews with at most this many stars, e.g. 2 for a complaints feed that pays nothing for the happy ones (pair with sort \"most_critical\" so the N read are the critical ones). Applied before billing; must be at least minRating. Use for \"negative reviews\", \"1-star reviews\", \"complaints\".",
            "default": 0
          },
          "sinceDate": {
            "title": "Only reviews on or after",
            "type": "string",
            "description": "Empty (default) = all dates. Keep only reviews on or after this day: \"2026-09-01\", or relative to the run day so a schedule never goes stale: \"7 days\", \"2 weeks\", \"3 months\", \"yesterday\". For a daily new-reviews feed pass \"1 day\" with sort \"most_recent\". Applied before billing; older reviews that were read are not charged. There is no end date and no \"since last run\" memory: dedupe on review_id downstream.",
            "default": ""
          },
          "requireText": {
            "title": "Only reviews with written text",
            "type": "boolean",
            "description": "false (default) = keep every review. true = drop reviews with no written text before billing. Apple's reviews endpoint returns written reviews only (60 of 60 reviews in the recorded pages had text), so this rarely removes anything here; it exists for parity with the Google Play actor.",
            "default": false
          },
          "includeAppRow": {
            "title": "Include an app row",
            "type": "boolean",
            "description": "true (default) = one free app row per app and storefront: app_name, developer, genre, price, version, age_rating, rating_avg, rating_count, the 1-5 star histogram (hist_1 … hist_5) and written_reviews. Never charged. Use for \"App Store rating of app X\" or a daily rating history; false = review rows only.",
            "default": true
          },
          "sessions": {
            "title": "Parallel sessions",
            "minimum": 1,
            "maximum": 16,
            "type": "integer",
            "description": "Parallel proxy sessions (egress IPs), 1-16; default 4. Apple allows about one request a second per IP and answers 10 reviews per request, so 4 sessions read about 40 reviews a second. Raise it for large runs; it changes speed, not cost.",
            "default": 4
          },
          "perIp": {
            "title": "Requests per second per IP",
            "minimum": 0.1,
            "maximum": 3,
            "type": "number",
            "description": "Requests per second per session, 0.1-3; default 1. Apple answers 429 above about 1 per second per IP and the fetcher then backs off, so a higher value earns retries, not speed.",
            "default": 1
          },
          "proxyConfiguration": {
            "title": "Proxy",
            "type": "object",
            "description": "Default: Apify Proxy, datacentre group. Apple's endpoints need no login, but each IP is capped at about one request a second, so a run of more than a few hundred reviews needs several IPs. Small runs work without a proxy.",
            "default": {
              "useApifyProxy": true
            }
          }
        }
      },
      "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
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}