{
  "openapi": "3.0.1",
  "info": {
    "title": "Apple App Store Developer Leads Scraper | Emails & Phones",
    "description": "$5 per 1,000. One row per App Store DEVELOPER, never per app: the publisher's EU trader contact block - legal entity, registered address, phone, email, D-U-N-S - plus website, support URL, portfolio size, ratings and tenure. Find them by keyword, category chart, developer ID or app ID.",
    "version": "0.1",
    "x-build-id": "ypvJKgCIIRijw7vwD"
  },
  "servers": [
    {
      "url": "https://api.apify.com/v2"
    }
  ],
  "paths": {
    "/acts/scrapersdelight~appstore-developer-leads-scraper/run-sync-get-dataset-items": {
      "post": {
        "operationId": "run-sync-get-dataset-items-scrapersdelight-appstore-developer-leads-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/scrapersdelight~appstore-developer-leads-scraper/runs": {
      "post": {
        "operationId": "runs-sync-scrapersdelight-appstore-developer-leads-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/scrapersdelight~appstore-developer-leads-scraper/run-sync": {
      "post": {
        "operationId": "run-sync-scrapersdelight-appstore-developer-leads-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": {
          "country": {
            "title": "App Store storefront",
            "enum": [
              "ie",
              "de",
              "fr",
              "es",
              "it",
              "nl",
              "be",
              "at",
              "pl",
              "se",
              "dk",
              "fi",
              "pt",
              "cz",
              "gr",
              "hu",
              "ro",
              "sk",
              "si",
              "bg",
              "hr",
              "ee",
              "lv",
              "lt",
              "lu",
              "mt",
              "cy",
              "us",
              "gb",
              "ca",
              "au",
              "nz",
              "ch",
              "no",
              "is",
              "jp",
              "kr",
              "cn",
              "hk",
              "tw",
              "sg",
              "my",
              "th",
              "id",
              "ph",
              "vn",
              "in",
              "ae",
              "sa",
              "il",
              "tr",
              "za",
              "br",
              "mx",
              "ar",
              "cl",
              "co",
              "ru",
              "ua"
            ],
            "type": "string",
            "description": "The Apple storefront to scrape. THIS IS THE MOST IMPORTANT SETTING IN THE ACTOR. Apple publishes the developer's trader contact block - legal entity name, postal address, phone number, email, D-U-N-S number - only on EU storefronts, under EU Digital Services Act Article 30. MEASURED 2026-09-07 on one app id across four storefronts: the block is populated on 'ie' and 'de' and NULL on 'us' and 'gb' (the UK is not in the EU). Re-measured three times on 25-developer US batches: trader_* filled 0 of 25 every time. Default 'ie', the English-language EU storefront. On a non-EU storefront you still get the developer, the portfolio economics, support_url, privacy_policy_url and developer_website, but no email or phone from Apple.",
            "default": "ie"
          },
          "discoveryMode": {
            "title": "How to find developers",
            "enum": [
              "search",
              "chart",
              "developerIds",
              "appIds"
            ],
            "type": "string",
            "description": "Keyword search = run App Store searches and take the publisher behind every app that comes back (MEASURED: 'crm' on ie returns 179 apps published by 162 distinct developers). Category charts = walk the top-free / top-paid / top-grossing chart of a category (Apple serves 100 entries per chart). Developer IDs / App IDs = you already know who you want. Start URLs below are honoured in EVERY mode, so you can mix a search with a hand-picked list.",
            "default": "search"
          },
          "searchTerms": {
            "title": "Search keywords",
            "type": "array",
            "description": "One App Store search per keyword; the publishers behind every result are merged and de-duplicated on developer id. Used only in 'Keyword search' mode. Apple caps a single search at about 200 apps, so use several narrow terms rather than one broad one - the number of terms is not capped. MEASURED 2026-09-07 through the Apify proxy: 'crm' on ie = 179 apps / 162 developers, 'invoice' on ie = 186 / 169, 'crm' on us = 198 / 193. Leave this empty with no ids or URLs either and the run falls back to the documented sample search so it still returns rows instead of failing. If the run's own timeout expires before every keyword has been read, the log prints INCOMPLETE DISCOVERY and names the keywords that were never searched - it is never reported as though Apple had nothing.",
            "items": {
              "type": "string"
            }
          },
          "searchLimitPerTerm": {
            "title": "Max apps per keyword",
            "minimum": 1,
            "maximum": 200,
            "type": "integer",
            "description": "How many apps to ask Apple for per keyword before collapsing them to developers. Apple's own ceiling is 200 and it often returns slightly fewer (179 for 'crm' on ie). Lower it to sample a term cheaply.",
            "default": 200
          },
          "chartGenres": {
            "title": "Chart categories",
            "type": "array",
            "description": "App Store categories whose charts to walk. Used only in 'Category charts' mode. MEASURED 2026-09-07: ie / Business / top-free returned 100 entries published by 92 distinct developers; ie / Finance / top-paid 97 entries / 76 developers. Multiply by the chart types below - 26 categories x 3 charts is roughly 6,600 developers per storefront.",
            "items": {
              "type": "string",
              "enum": [
                "6000",
                "6001",
                "6002",
                "6003",
                "6004",
                "6005",
                "6006",
                "6007",
                "6008",
                "6009",
                "6010",
                "6011",
                "6012",
                "6013",
                "6014",
                "6015",
                "6016",
                "6017",
                "6018",
                "6020",
                "6021",
                "6022",
                "6023",
                "6024",
                "6025",
                "6026",
                "6027"
              ],
              "enumTitles": [
                "Business (6000)",
                "Weather (6001)",
                "Utilities (6002)",
                "Travel (6003)",
                "Sports (6004)",
                "Social Networking (6005)",
                "Reference (6006)",
                "Productivity (6007)",
                "Photo & Video (6008)",
                "News (6009)",
                "Navigation (6010)",
                "Music (6011)",
                "Lifestyle (6012)",
                "Health & Fitness (6013)",
                "Games (6014)",
                "Finance (6015)",
                "Entertainment (6016)",
                "Education (6017)",
                "Books (6018)",
                "Medical (6020)",
                "Magazines & Newspapers (6021)",
                "Catalogs (6022)",
                "Food & Drink (6023)",
                "Shopping (6024)",
                "Stickers (6025)",
                "Developer Tools (6026)",
                "Graphics & Design (6027)"
              ]
            },
            "default": [
              "6000"
            ]
          },
          "chartTypes": {
            "title": "Chart types",
            "type": "array",
            "description": "Which chart of each category to read. Top-grossing is the money list: those publishers have a working paid product. MEASURED: Apple returns 100 entries per chart even when a higher limit is requested, so this actor never claims 200.",
            "items": {
              "type": "string",
              "enum": [
                "topfreeapplications",
                "toppaidapplications",
                "topgrossingapplications"
              ],
              "enumTitles": [
                "Top free",
                "Top paid",
                "Top grossing"
              ]
            },
            "default": [
              "topfreeapplications"
            ]
          },
          "chartLimit": {
            "title": "Chart depth",
            "minimum": 1,
            "maximum": 200,
            "type": "integer",
            "description": "How deep into each chart to read. Apple's hard ceiling is 100 entries per chart regardless of what is requested - asking for more does not fail, it just returns 100.",
            "default": 100
          },
          "developerIds": {
            "title": "Developer IDs",
            "type": "array",
            "description": "Apple developer (artist) ids - the digits after /id in an apps.apple.com/<cc>/developer/... URL, e.g. 932493381 for Revolut Ltd. Used in 'Developer IDs' mode. Their whole storefront catalogue is read, so the portfolio lookup always runs for these even if 'Fetch the full portfolio' is off: it is the only way to learn anything about a developer given nothing but an id.",
            "items": {
              "type": "string"
            }
          },
          "appIds": {
            "title": "App IDs",
            "type": "array",
            "description": "App Store app ids - the digits after /id in an apps.apple.com/<cc>/app/... URL, e.g. 932493382. Used in 'App IDs' mode. Each app is resolved to its publisher and you get the DEVELOPER row, not the app row. Ids are looked up 50 at a time. An id that is not on the chosen storefront is named in the log and skipped.",
            "items": {
              "type": "string"
            }
          },
          "startUrls": {
            "title": "Start URLs (any mode)",
            "type": "array",
            "description": "Paste apps.apple.com app pages or developer pages. Honoured in EVERY discovery mode, so you can bolt a hand-picked list onto a keyword search or a chart sweep. A /developer/...id<digits> URL adds that publisher; an /app/...id<digits> URL adds the publisher behind that app. Anything else is named in the log and skipped rather than silently dropped.",
            "items": {
              "type": "object",
              "required": [
                "url"
              ],
              "properties": {
                "url": {
                  "type": "string",
                  "title": "URL of a web page",
                  "format": "uri"
                }
              }
            }
          },
          "extraCountries": {
            "title": "Also discover on these storefronts",
            "type": "array",
            "description": "Run the same discovery on additional storefronts and merge the results. Developers are de-duplicated across storefronts on developer id - the first storefront that surfaced a developer is the one used for their portfolio and contact call, and the row's storefront field says which. Useful for an EU sweep (de, fr, it, es) in one run. Leave empty for a single-storefront run.",
            "items": {
              "type": "string",
              "enum": [
                "ie",
                "de",
                "fr",
                "es",
                "it",
                "nl",
                "be",
                "at",
                "pl",
                "se",
                "dk",
                "fi",
                "pt",
                "cz",
                "gr",
                "hu",
                "ro",
                "sk",
                "si",
                "bg",
                "hr",
                "ee",
                "lv",
                "lt",
                "lu",
                "mt",
                "cy",
                "us",
                "gb",
                "ca",
                "au",
                "nz",
                "ch",
                "no",
                "is",
                "jp",
                "kr",
                "cn",
                "hk",
                "tw",
                "sg",
                "my",
                "th",
                "id",
                "ph",
                "vn",
                "in",
                "ae",
                "sa",
                "il",
                "tr",
                "za",
                "br",
                "mx",
                "ar",
                "cl",
                "co",
                "ru",
                "ua"
              ],
              "enumTitles": [
                "Ireland (ie) - EU, publishes trader contact",
                "Germany (de) - EU, publishes trader contact",
                "France (fr) - EU, publishes trader contact",
                "Spain (es) - EU, publishes trader contact",
                "Italy (it) - EU, publishes trader contact",
                "Netherlands (nl) - EU, publishes trader contact",
                "Belgium (be) - EU, publishes trader contact",
                "Austria (at) - EU, publishes trader contact",
                "Poland (pl) - EU, publishes trader contact",
                "Sweden (se) - EU, publishes trader contact",
                "Denmark (dk) - EU, publishes trader contact",
                "Finland (fi) - EU, publishes trader contact",
                "Portugal (pt) - EU, publishes trader contact",
                "Czechia (cz) - EU, publishes trader contact",
                "Greece (gr) - EU, publishes trader contact",
                "Hungary (hu) - EU, publishes trader contact",
                "Romania (ro) - EU, publishes trader contact",
                "Slovakia (sk) - EU, publishes trader contact",
                "Slovenia (si) - EU, publishes trader contact",
                "Bulgaria (bg) - EU, publishes trader contact",
                "Croatia (hr) - EU, publishes trader contact",
                "Estonia (ee) - EU, publishes trader contact",
                "Latvia (lv) - EU, publishes trader contact",
                "Lithuania (lt) - EU, publishes trader contact",
                "Luxembourg (lu) - EU, publishes trader contact",
                "Malta (mt) - EU, publishes trader contact",
                "Cyprus (cy) - EU, publishes trader contact",
                "United States (us) - NOT EU, no trader contact",
                "United Kingdom (gb) - NOT EU, no trader contact",
                "Canada (ca) - NOT EU, no trader contact",
                "Australia (au) - NOT EU, no trader contact",
                "New Zealand (nz) - NOT EU, no trader contact",
                "Switzerland (ch) - NOT EU, no trader contact",
                "Norway (no) - NOT EU, no trader contact",
                "Iceland (is) - NOT EU, no trader contact",
                "Japan (jp) - NOT EU, no trader contact",
                "South Korea (kr) - NOT EU, no trader contact",
                "China (cn) - NOT EU, no trader contact",
                "Hong Kong (hk) - NOT EU, no trader contact",
                "Taiwan (tw) - NOT EU, no trader contact",
                "Singapore (sg) - NOT EU, no trader contact",
                "Malaysia (my) - NOT EU, no trader contact",
                "Thailand (th) - NOT EU, no trader contact",
                "Indonesia (id) - NOT EU, no trader contact",
                "Philippines (ph) - NOT EU, no trader contact",
                "Vietnam (vn) - NOT EU, no trader contact",
                "India (in) - NOT EU, no trader contact",
                "United Arab Emirates (ae) - NOT EU, no trader contact",
                "Saudi Arabia (sa) - NOT EU, no trader contact",
                "Israel (il) - NOT EU, no trader contact",
                "Turkey (tr) - NOT EU, no trader contact",
                "South Africa (za) - NOT EU, no trader contact",
                "Brazil (br) - NOT EU, no trader contact",
                "Mexico (mx) - NOT EU, no trader contact",
                "Argentina (ar) - NOT EU, no trader contact",
                "Chile (cl) - NOT EU, no trader contact",
                "Colombia (co) - NOT EU, no trader contact",
                "Russia (ru) - NOT EU, no trader contact",
                "Ukraine (ua) - NOT EU, no trader contact"
              ]
            },
            "default": []
          },
          "maxDevelopers": {
            "title": "Max developers (rows) to deliver",
            "minimum": 0,
            "type": "integer",
            "description": "Hard stop on DELIVERED rows, and therefore on your bill, since one row is one developer-scraped charge. It counts rows that survived your filters, not candidates discovered. 0 means no cap: everything discovery found is enriched and delivered. Start small.",
            "default": 50
          },
          "fetchContacts": {
            "title": "Fetch the App Store contact block",
            "type": "boolean",
            "description": "Makes the extra App Store call that returns the EU DSA trader block (legal name, address, phone, email, D-U-N-S) plus the support URL, developer website and privacy-policy URL. This is what the actor is FOR, so leave it on. Turning it off means no contact-enriched charge is ever made, and trader_*, support_url and privacy_policy_url all come back null with contact_complete = false and contact_incomplete_reason saying you switched it off.",
            "default": true
          },
          "fetchPortfolio": {
            "title": "Fetch the full portfolio",
            "type": "boolean",
            "description": "Reads the developer's whole catalogue on the storefront so portfolio_app_count, portfolio_total_ratings, portfolio_avg_rating, first_release_date and newest_release_date describe the DEVELOPER rather than the one app you found them through. Costs one extra request per developer and is not separately charged. Switch it off and those fields still ship, computed only over the apps discovery happened to surface, with portfolio_complete = false and the reason spelled out on the row. Apple caps this lookup at 200 apps per developer; a developer who fills that ceiling also gets portfolio_complete = false.",
            "default": true
          },
          "contactRetryApps": {
            "title": "Apps to try per developer for the trader block",
            "minimum": 1,
            "maximum": 5,
            "type": "integer",
            "description": "A developer declares their trader details per app. If the first app carries no trader block, try this many of their apps in total (next-most-rated first) before giving up. MEASURED 2026-09-07 over 100 developers on the ie storefront: a second app cost 29 extra requests and recovered ZERO extra trader blocks - a developer's DSA declaration is consistent across their catalogue - so the default is 1. Raise it if you suspect a publisher declared on only some apps. Extra attempts cost requests, never an extra charge: a developer is billed once for enrichment however many apps it took.",
            "default": 1
          },
          "contactFallbackCountries": {
            "title": "Fallback storefronts for the trader block",
            "type": "array",
            "description": "When the chosen storefront returns no trader block for a developer, retry the contact call on these storefronts. Only EU storefronts publish the block, so listing 'us' or 'gb' here can never help and the log says so. The main use is a non-EU primary storefront: discover on 'us', then pull the EU-published trader contact from 'ie' or 'de'. Costs one extra request per fallback per developer that needed it, and still just one enrichment charge. The row's contact_storefront field names the storefront the contact actually came from.",
            "items": {
              "type": "string",
              "enum": [
                "ie",
                "de",
                "fr",
                "es",
                "it",
                "nl",
                "be",
                "at",
                "pl",
                "se",
                "dk",
                "fi",
                "pt",
                "cz",
                "gr",
                "hu",
                "ro",
                "sk",
                "si",
                "bg",
                "hr",
                "ee",
                "lv",
                "lt",
                "lu",
                "mt",
                "cy",
                "us",
                "gb",
                "ca",
                "au",
                "nz",
                "ch",
                "no",
                "is",
                "jp",
                "kr",
                "cn",
                "hk",
                "tw",
                "sg",
                "my",
                "th",
                "id",
                "ph",
                "vn",
                "in",
                "ae",
                "sa",
                "il",
                "tr",
                "za",
                "br",
                "mx",
                "ar",
                "cl",
                "co",
                "ru",
                "ua"
              ],
              "enumTitles": [
                "Ireland (ie) - EU, publishes trader contact",
                "Germany (de) - EU, publishes trader contact",
                "France (fr) - EU, publishes trader contact",
                "Spain (es) - EU, publishes trader contact",
                "Italy (it) - EU, publishes trader contact",
                "Netherlands (nl) - EU, publishes trader contact",
                "Belgium (be) - EU, publishes trader contact",
                "Austria (at) - EU, publishes trader contact",
                "Poland (pl) - EU, publishes trader contact",
                "Sweden (se) - EU, publishes trader contact",
                "Denmark (dk) - EU, publishes trader contact",
                "Finland (fi) - EU, publishes trader contact",
                "Portugal (pt) - EU, publishes trader contact",
                "Czechia (cz) - EU, publishes trader contact",
                "Greece (gr) - EU, publishes trader contact",
                "Hungary (hu) - EU, publishes trader contact",
                "Romania (ro) - EU, publishes trader contact",
                "Slovakia (sk) - EU, publishes trader contact",
                "Slovenia (si) - EU, publishes trader contact",
                "Bulgaria (bg) - EU, publishes trader contact",
                "Croatia (hr) - EU, publishes trader contact",
                "Estonia (ee) - EU, publishes trader contact",
                "Latvia (lv) - EU, publishes trader contact",
                "Lithuania (lt) - EU, publishes trader contact",
                "Luxembourg (lu) - EU, publishes trader contact",
                "Malta (mt) - EU, publishes trader contact",
                "Cyprus (cy) - EU, publishes trader contact",
                "United States (us) - NOT EU, no trader contact",
                "United Kingdom (gb) - NOT EU, no trader contact",
                "Canada (ca) - NOT EU, no trader contact",
                "Australia (au) - NOT EU, no trader contact",
                "New Zealand (nz) - NOT EU, no trader contact",
                "Switzerland (ch) - NOT EU, no trader contact",
                "Norway (no) - NOT EU, no trader contact",
                "Iceland (is) - NOT EU, no trader contact",
                "Japan (jp) - NOT EU, no trader contact",
                "South Korea (kr) - NOT EU, no trader contact",
                "China (cn) - NOT EU, no trader contact",
                "Hong Kong (hk) - NOT EU, no trader contact",
                "Taiwan (tw) - NOT EU, no trader contact",
                "Singapore (sg) - NOT EU, no trader contact",
                "Malaysia (my) - NOT EU, no trader contact",
                "Thailand (th) - NOT EU, no trader contact",
                "Indonesia (id) - NOT EU, no trader contact",
                "Philippines (ph) - NOT EU, no trader contact",
                "Vietnam (vn) - NOT EU, no trader contact",
                "India (in) - NOT EU, no trader contact",
                "United Arab Emirates (ae) - NOT EU, no trader contact",
                "Saudi Arabia (sa) - NOT EU, no trader contact",
                "Israel (il) - NOT EU, no trader contact",
                "Turkey (tr) - NOT EU, no trader contact",
                "South Africa (za) - NOT EU, no trader contact",
                "Brazil (br) - NOT EU, no trader contact",
                "Mexico (mx) - NOT EU, no trader contact",
                "Argentina (ar) - NOT EU, no trader contact",
                "Chile (cl) - NOT EU, no trader contact",
                "Colombia (co) - NOT EU, no trader contact",
                "Russia (ru) - NOT EU, no trader contact",
                "Ukraine (ua) - NOT EU, no trader contact"
              ]
            },
            "default": []
          },
          "leadAppSelection": {
            "title": "Which app represents the developer",
            "enum": [
              "firstDiscovered",
              "mostRated",
              "newest"
            ],
            "type": "string",
            "description": "Every row carries one lead app - the app that puts the developer in context. 'The app that surfaced them' keeps the meaning of your search rank or chart rank. 'Most rated' picks their biggest app. 'Most recently updated' is the better activity signal. This also decides which app is tried FIRST for the trader block.",
            "default": "firstDiscovered"
          },
          "harvestDescriptionEmails": {
            "title": "Also harvest emails from the app description",
            "type": "boolean",
            "description": "Many small developers put a contact email in the App Store description itself. This pulls them out of text you have already paid for - no extra request, no extra charge - into description_emails, and uses the first one as the row's email when Apple published no trader email. It never invents an address; it only reports what is literally in the text.",
            "default": true
          },
          "excludePersonalNameEmails": {
            "title": "Drop named-individual email addresses",
            "type": "boolean",
            "description": "Some developers register a named person's work mailbox as their trader contact (firstname.lastname@company.com) rather than a role address (support@, info@, hello@). With this on, only role addresses are kept and personal-looking ones are removed from trader_email and description_emails; the count removed is reported per row in personal_emails_redacted. Off by default - the actor delivers what Apple publishes - but this is the switch to use if your outbound programme only wants role mailboxes. Every row also carries email_is_role_address so you can decide downstream instead. MEASURED over 100 delivered rows on 2026-09-08: of the 57 rows carrying an email, 28 were role addresses and 29 looked like a named person. The test is a word match on the local part - it catches support@, info@, hallo@ and glued forms like appservices@ or dsalesmobilesupport@, but it cannot tell you that eb@handwerkerpro.com is a person. It is a sorting aid, not a verified fact.",
            "default": false
          },
          "includeTraderPhone": {
            "title": "Include the trader phone number",
            "type": "boolean",
            "description": "Off blanks trader_phone on every row. The number is published by Apple under DSA Article 30 and is often a mobile, so some buyers prefer not to store it at all.",
            "default": true
          },
          "includeTraderAddress": {
            "title": "Include the trader postal address",
            "type": "boolean",
            "description": "Off blanks trader_address, trader_address_lines and trader_address_country on every row. Sole-trader developers frequently register a home address.",
            "default": true
          },
          "requireTraderContact": {
            "title": "Only developers with a trader contact",
            "type": "boolean",
            "description": "Keep only rows that carry a trader email, phone or postal address from Apple's EU register. MEASURED on ie: about 60-68% of developers qualify - the rest declared isTrader = false (governments, some non-EU corporates) and publish only a name. Applied to the finished row, so nothing it removes is delivered OR charged. On a non-EU storefront this filter removes everything, and the log says so.",
            "default": false
          },
          "requireEmail": {
            "title": "Only developers with an email address",
            "type": "boolean",
            "description": "Keep only rows whose email field is populated, from Apple's trader register or, failing that, from the app description. Applied after the personal-email switch above, so turning both on keeps only role addresses.",
            "default": false
          },
          "requireWebsite": {
            "title": "Only developers with a website",
            "type": "boolean",
            "description": "Keep only rows with a developer_website. That field's MEASURED fill is high but not total - about 88% on crm / ie and 56% on invoice / ie.",
            "default": false
          },
          "excludeFreemailLeads": {
            "title": "Drop free-mail leads (gmail, icloud, ...)",
            "type": "boolean",
            "description": "Removes rows whose email is on a consumer mail host - a signal for a hobbyist rather than a company. Every row also carries email_is_freemail and email_domain if you would rather sort it yourself.",
            "default": false
          },
          "minPortfolioApps": {
            "title": "Min apps in the portfolio",
            "minimum": 0,
            "type": "integer",
            "description": "Keep only developers publishing at least this many apps on the storefront. 2 or 3 filters out one-app hobby projects; a high number finds app studios and agencies."
          },
          "maxPortfolioApps": {
            "title": "Max apps in the portfolio",
            "minimum": 0,
            "type": "integer",
            "description": "Keep only developers publishing at most this many apps. Use it to exclude the giants (Zoho publishes 163 apps on ie, Microsoft 78) when you are selling to small studios."
          },
          "minPortfolioRatings": {
            "title": "Min total ratings across the portfolio",
            "minimum": 0,
            "type": "integer",
            "description": "Traction floor: the sum of every rating count across the developer's apps. Filters out developers whose apps nobody uses."
          },
          "minAverageRating": {
            "title": "Min average rating (ratings-weighted)",
            "minimum": 1,
            "maximum": 5,
            "type": "integer",
            "description": "Keep only developers whose portfolio average rating - weighted by rating count, not a plain mean of stars - is at least this. 1 to 5."
          },
          "onlyPaidDevelopers": {
            "title": "Only developers who sell a paid app",
            "type": "boolean",
            "description": "Keep only developers with at least one paid app on the storefront - proof they already charge money for software.",
            "default": false
          },
          "categoryIds": {
            "title": "Only these app categories",
            "type": "array",
            "description": "Keep only developers with at least one app in these App Store categories. Applied BEFORE enrichment when discovery already surfaced an app, so a developer it removes costs you no requests at all - and applied AGAIN on the finished row against the developer's portfolio categories, so it still filters when you supplied bare developer IDs or /developer/ start URLs and there was no discovered app to match. Either way, a developer it removes is never delivered and never charged.",
            "items": {
              "type": "string",
              "enum": [
                "6000",
                "6001",
                "6002",
                "6003",
                "6004",
                "6005",
                "6006",
                "6007",
                "6008",
                "6009",
                "6010",
                "6011",
                "6012",
                "6013",
                "6014",
                "6015",
                "6016",
                "6017",
                "6018",
                "6020",
                "6021",
                "6022",
                "6023",
                "6024",
                "6025",
                "6026",
                "6027"
              ],
              "enumTitles": [
                "Business (6000)",
                "Weather (6001)",
                "Utilities (6002)",
                "Travel (6003)",
                "Sports (6004)",
                "Social Networking (6005)",
                "Reference (6006)",
                "Productivity (6007)",
                "Photo & Video (6008)",
                "News (6009)",
                "Navigation (6010)",
                "Music (6011)",
                "Lifestyle (6012)",
                "Health & Fitness (6013)",
                "Games (6014)",
                "Finance (6015)",
                "Entertainment (6016)",
                "Education (6017)",
                "Books (6018)",
                "Medical (6020)",
                "Magazines & Newspapers (6021)",
                "Catalogs (6022)",
                "Food & Drink (6023)",
                "Shopping (6024)",
                "Stickers (6025)",
                "Developer Tools (6026)",
                "Graphics & Design (6027)"
              ]
            },
            "default": []
          },
          "newestReleaseAfter": {
            "title": "Newest app released after",
            "type": "string",
            "description": "ISO date, e.g. 2024-01-01. Keep only developers who have shipped a NEW app since then - an activity signal that separates live studios from dormant accounts. Needs the portfolio lookup to be meaningful."
          },
          "firstReleaseAfter": {
            "title": "First app released after",
            "type": "string",
            "description": "ISO date. Keep only developers who arrived on the App Store after this date - the young-company filter."
          },
          "firstReleaseBefore": {
            "title": "First app released before",
            "type": "string",
            "description": "ISO date. Keep only developers who have been on the App Store since before this date - the established-company filter. Combine with the one above for a tenure window."
          },
          "excludeDeveloperIds": {
            "title": "Never return these developers",
            "type": "array",
            "description": "Suppression list. Accepts developer ids or full apps.apple.com developer URLs. Applied before any request is spent on them, so existing customers and competitors cost you nothing.",
            "items": {
              "type": "string"
            }
          },
          "includePortfolioApps": {
            "title": "Include the app list on each row",
            "type": "boolean",
            "description": "Adds portfolio_apps - a nested array of the developer's apps (id, name, URL, category, price, rating, rating count, release and update dates), most-rated first. Off keeps the row flat and CSV-friendly; the portfolio_* totals are always present either way.",
            "default": false
          },
          "portfolioAppsCap": {
            "title": "Max apps listed per row",
            "minimum": 1,
            "maximum": 200,
            "type": "integer",
            "description": "How many apps the nested list may hold. The portfolio totals always cover every app that was read; portfolio_apps_listed and portfolio_apps_withheld on each row say how many of them made it into the array, so a capped list can never be mistaken for a complete one.",
            "default": 25
          },
          "includeLeadAppDescription": {
            "title": "Include the lead app's description text",
            "type": "boolean",
            "description": "Adds lead_app_description - the full App Store description, often a couple of thousand characters. Useful for classifying what the developer actually sells; heavy in a CSV.",
            "default": false
          },
          "dedupeAcrossRuns": {
            "title": "Skip developers delivered by earlier runs",
            "type": "boolean",
            "description": "Remembers every developer id delivered, in a NAMED key-value store that survives between runs, and skips them next time. Turns a scheduled run into a new-developers-only feed and stops you paying twice for the same lead. The skipped count is reported in the log.",
            "default": false
          },
          "proxyConfiguration": {
            "title": "Proxy",
            "type": "object",
            "description": "Apify DATACENTER proxy is enough and is the default. MEASURED 2026-09-07 through groups-BUYPROXIES94952: 62 of 62 developers over 124 requests at concurrency 4, 100% success, zero blocks. The only wall Apple puts up here is HTTP 429, which is per exit IP and cleared by the built-in retry on a fresh session. RESIDENTIAL works too but costs more for no measured gain. Running with no proxy at all measured 15 of 20 successes from a single home IP.",
            "default": {
              "useApifyProxy": true
            }
          },
          "maxConcurrency": {
            "title": "Parallel requests",
            "minimum": 1,
            "maximum": 10,
            "type": "integer",
            "description": "How many developers to enrich at once, each on its own pinned proxy session. 4 is the measured sweet spot: 25 developers in 25.7 s with zero failures. Push it higher and Apple starts answering 429 - the retry handles it, but the run gets slower, not faster.",
            "default": 4
          },
          "maxRequestRetries": {
            "title": "Retries per request",
            "minimum": 1,
            "maximum": 8,
            "type": "integer",
            "description": "Attempts per HTTP request. The first rides the worker's pinned proxy session; every retry mints a FRESH session with exponential backoff, because a 429 belongs to the exit IP and a new IP starts on a full budget. MEASURED: 5 of 62 developers needed at least one retry and all 62 succeeded.",
            "default": 4
          },
          "requestTimeoutSecs": {
            "title": "Request timeout (seconds)",
            "minimum": 5,
            "maximum": 120,
            "type": "integer",
            "description": "Per-request timeout. Apple's responses here run from 13 KB to 1.6 MB and normally answer in well under a second, so 30 s is generous. The timeout is also clamped to the time the run has left, so a retry chain can never outlive the run itself. If that clamp is what makes a request fail, the run says so in those words - it is never reported as an Apple or a proxy problem.",
            "default": 30
          }
        }
      },
      "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
                  }
                }
              },
              "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
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}