Selling scraping as a service means finding clients, writing proposals and doing custom work for each one. That is still a job. The better-leveraged version is to build the scraper once, list it on a marketplace where people are already searching for “Google Maps scraper” or “TikTok scraper”, and charge every user per result, automatically, for as long as you keep the scraper working.
That marketplace is the Apify Store. It lists thousands of community-built scrapers (Apify calls them “Actors”). Users run them on Apify’s cloud, Apify bills the user, and you get 80% of the revenue minus the compute your Actor burns. You never touch a customer, an invoice or a server.
The grey part: many of the best-selling Actors scrape sites whose terms of service forbid scraping (Google Maps, Instagram, TikTok, Amazon, regional marketplaces). US courts have repeatedly said scraping public, logged-out data is not hacking, but the platforms still fight back with IP bans, lawsuits and takedown requests. This guide covers how the money works, a working pay-per-event Actor in Python, how to pick a niche that is not already saturated, real margins, and where the line is.
How the money works
Apify supports a few monetization models. This guide uses pay-per-event (PPE): you define billable events (for example “one product scraped”) and set a price per event. The user sets a maximum spend per run, Apify charges them as your code reports events, and you get paid monthly.
Your cut, per Apify’s developer terms at the time of writing:
- 80% of what users pay, Apify keeps 20%
- minus the platform usage (compute, proxies, storage) your Actor consumed during paid runs
- only runs by users on paid Apify plans count. Runs by free-plan users do not show up in your revenue at all. An Actor whose costs are higher than its revenue in a month counts as $0, not as a negative number
Payouts go out monthly, around the 11th. Apify retired its old “rental” model in 2026, so pay-per-event and pay-per-result are the paid options now.
That second line is where most people lose money. A browser-based scraper (Playwright, Puppeteer) burns 10 to 50 times more compute than a plain HTTP scraper. If you charge $2 per 1,000 results and your headless Chrome eats $1.70 of compute for those 1,000, you are working for free. Build HTTP-first whenever the site allows it.
Example math for the HTTP-only Actor in this guide:
| Line | Per 1,000 results |
|---|---|
| Price charged to user | $1.50 |
| Apify 20% fee | -$0.30 |
| Compute (256 MB, ~30 s per 1,000 results, ~0.002 CU at $0.20/CU) | under -$0.001 |
| Proxy (datacenter, often not needed for this target) | $0 to -$0.05 |
| Your net | about $1.15 to $1.20 |
These numbers are illustrative, not measured. Check Apify’s pricing page for the current compute unit price ($0.20/CU on Free and Starter, lower on bigger plans as of October 2026). It changes by plan, but at these volumes compute rounds to almost nothing for an HTTP scraper.
To net $1,000/month at $1.20 per 1,000, you need roughly 830,000 paid results a month across all your users. Realistically that takes a few heavy users: a lead-gen agency pulling tens of thousands of listings per city, or a price-monitoring team scraping thousands of SKUs every day. Most revenue comes from a handful of power users, not from hobbyists, and finding even those few takes months.
What you need
| Item | Cost | Notes |
|---|---|---|
| Apify account | Free | The free plan includes a monthly usage credit (about $5 at the time of writing). Enough to build and test. Paying users’ runs are billed to them, not you |
| Apify paid plan | Optional, Starter is $19/month (October 2026) | Only if your own testing outgrows the free credit or you need more proxy |
| Node.js 20+ | Free | Required for the Apify CLI (v1.x) |
| Python 3.10+ | Free | Apify Python SDK |
| Residential proxies | $8/GB on Free/Starter, $7 to $7.50/GB on bigger plans (October 2026) | Only for targets that block datacenter IPs. This is the cost that kills margins |
| PayPal or bank account | Free | For monthly payouts (PayPal or wire transfer). Check Apify’s payout settings for your country |
| Time | One weekend for the first Actor | Plus maintenance whenever the target site changes |
No inventory, no ad spend, no clients. The capital here is your time and the patience to wait for Store traffic.
Step 1: Pick a niche that is not already eaten
Do not build “Google Maps Scraper” number 41. Open the Apify Store and look at each candidate niche through four filters:
- Demand. Every Actor page shows how many users it has. If the top Actor for a site has thousands of monthly users, there is demand.
- Weak incumbents. Look at the rating and the Issues tab. An Actor with thousands of users, a 3-star rating and open issues saying “returns empty results since last week” is an opening.
- No coverage at all. Search for big regional sites. Indonesian and Southeast Asian marketplaces, job boards and property portals (Tokopedia, Shopee, Lazada, Jobstreet, Rumah123, OLX) have far fewer quality Actors than US sites, and agencies outside the region want that data. Being a local is an unfair advantage: you understand the site, the language and the categories.
- HTTP-friendly. Open the site, open DevTools, go to the Network tab, filter by Fetch/XHR, and scroll. If the data arrives as JSON from an internal API, you can scrape it with plain HTTP requests: cheap, fast, high margin. If everything is rendered behind heavy anti-bot JavaScript, your margins will be thin and your maintenance heavy.
The example below scrapes Shopify stores. Every Shopify store exposes a public /products.json endpoint, so it is a clean, low-risk target to learn the full publish-and-charge flow. Shopify Actors already exist in the Store and Shopify rate-limits these endpoints, so treat this as a template: swap in your niche target once the pipeline works.
Step 2: Install the CLI and create the Actor
npm install -g apify-cli
apify login # paste your API token from Console > Settings > Integrations
apify create shopify-price-feed --template python-start
cd shopify-price-feed
echo "httpx>=0.27" >> requirements.txt
The template gives you this layout:
.actor/actor.json # Actor metadata
.actor/input_schema.json # Input form users see in the Console
src/main.py # Your code
Dockerfile
requirements.txt
Step 3: Define the input
Replace .actor/input_schema.json. This becomes the form users fill in on your Store page, so keep it simple:
{
"title": "Shopify Price Feed",
"type": "object",
"schemaVersion": 1,
"properties": {
"domains": {
"title": "Store domains",
"type": "array",
"description": "Shopify store domains, e.g. allbirds.com",
"editor": "stringList",
"prefill": ["allbirds.com"]
},
"maxItemsPerStore": {
"title": "Max variants per store",
"type": "integer",
"default": 1000,
"minimum": 1
},
"proxyConfiguration": {
"title": "Proxy",
"type": "object",
"editor": "proxy",
"default": { "useApifyProxy": false }
}
},
"required": ["domains"]
}
Step 4: The scraper with per-result charging
Replace src/main.py:
import asyncio
import httpx
from apify import Actor
def clean_domain(raw: str) -> str:
return raw.strip().replace("https://", "").replace("http://", "").strip("/")
async def scrape_store(client, domain, max_items):
"""Yield one flat row per product variant from /products.json."""
page, count, retries = 1, 0, 0
while count < max_items:
r = await client.get(
f"https://{domain}/products.json",
params={"limit": 250, "page": page},
)
if r.status_code == 429 and retries < 3:
# Shopify rate-limits busy IPs, including cloud ranges. Back off and retry.
retries += 1
Actor.log.warning(f"{domain}: rate limited, waiting 10 s")
await asyncio.sleep(10)
continue
if r.status_code != 200:
Actor.log.warning(f"{domain}: HTTP {r.status_code} on page {page}")
return
products = r.json().get("products", [])
if not products:
return
for p in products:
url = "https://" + domain + "/products/" + p["handle"]
for v in p.get("variants", []):
yield {
"store": domain,
"product_id": p["id"],
"title": p["title"],
"vendor": p.get("vendor"),
"product_type": p.get("product_type"),
"variant_id": v["id"],
"variant_title": v.get("title"),
"sku": v.get("sku"),
"price": v.get("price"),
"compare_at_price": v.get("compare_at_price"),
"available": v.get("available"),
"url": url,
"updated_at": p.get("updated_at"),
}
count += 1
if count >= max_items:
return
page += 1
async def main():
async with Actor:
inp = await Actor.get_input() or {}
domains = [clean_domain(d) for d in inp.get("domains", []) if d.strip()]
max_items = int(inp.get("maxItemsPerStore", 1000))
proxy_url = None
proxy_cfg = await Actor.create_proxy_configuration(
actor_proxy_input=inp.get("proxyConfiguration")
)
if proxy_cfg:
proxy_url = await proxy_cfg.new_url()
headers = {"User-Agent": "Mozilla/5.0 (compatible; price-feed/1.0)"}
async with httpx.AsyncClient(
proxy=proxy_url, headers=headers, timeout=30, follow_redirects=True
) as client:
for domain in domains:
async for row in scrape_store(client, domain, max_items):
# Save the row and bill one event for it in a single call.
result = await Actor.push_data(row, charged_event_name="variant-scraped")
# Stop when the user hits their spend cap.
if result.event_charge_limit_reached:
Actor.log.info("User spend limit reached, stopping.")
return
Actor.log.info(f"Done: {domain}")
Two details that matter for money:
- Charge together with the data.
push_data(..., charged_event_name=...)saves the row and bills for it in one call, so users are never billed for results they did not get. Users who are billed for missing results leave 1-star reviews. (Actor.charge(), available since SDK 2.3, is for billable events that are not dataset rows.) - Respect the spend limit. Users set a max cost per run. Once it is hit, the SDK silently stops charging and stops saving data, but your code keeps running unless you check
event_charge_limit_reachedand stop. Scraping past it burns compute you will not be paid for.
This matches the Apify Python SDK 4.x pay-per-event docs as of October 2026. If the SDK has moved on, check the pay-per-event section of the Apify docs.
Rate limits are real. While this article was being fact-checked, /products.json returned HTTP 429 to a cloud server for several large stores. On Apify you may get the same from datacenter IPs. The retry above handles short bursts. If a store keeps returning 429, turn on Apify Proxy in the input, and remember that it adds proxy cost.
Step 5: Test locally, then push
mkdir -p storage/key_value_stores/default
echo "{\"domains\": [\"allbirds.com\"], \"maxItemsPerStore\": 50}" \
> storage/key_value_stores/default/INPUT.json
apify run
ls storage/datasets/default | head # one JSON file per result
apify push # builds and deploys to your Apify account
Locally, nothing is billed. To simulate pay-per-event, run ACTOR_TEST_PAY_PER_EVENT=true apify run. Every charge is written to storage/datasets/charging-log/ at a dummy $1 per event, because the real prices live on the platform. Run the Actor once in the Apify Console after pushing and check the run’s Usage tab: that number, divided by results, is your real cost per result. Do this before you set a price.
Step 6: Publish and set the price
In the Apify Console, open your Actor, then:
- Publication: give it a title people actually search for (“Shopify Products and Prices Scraper”, not “price-feed-v2”), a category, and an icon.
- README: this is your sales page and your SEO. Apify Store pages get indexed by Google, so “[site name] scraper” searches can land on your page. Include: what data you get (with a sample JSON row), use cases, price per 1,000 results, and a short FAQ.
- Monetization: choose pay-per-event, create the event
variant-scrapedwith a title and description, and set the price. Many HTTP scrapers on the Store charge somewhere between $0.50 and $5 per 1,000 results. Niche or hard targets justify more. - Publish. The Store has quality and publishing rules, and price changes on a published Actor only take effect after a notice period. Set your price carefully from the start.
Pricing rule of thumb: measure your cost per 1,000 results, then charge at least 5 to 10 times that. If a competitor is cheaper, compete on reliability and data quality, not price. Power users switch to whoever does not break.
Honest cost breakdown
| Item | Month 1 | Ongoing |
|---|---|---|
| Apify plan | $0 (free credit) | $0 to $19 |
| Your own test runs | Covered by free credit | Covered, unless you test browser scrapers heavily |
| Proxies during paid runs | Deducted from your revenue | $0 for easy targets, 20 to 60% of revenue for hard ones on residential |
| Platform fee | 20% of revenue | 20% of revenue |
| Cash out of pocket | $0 | $0 to $19/month |
The real costs are time (a weekend per Actor, plus a few hours whenever a target changes its site) and waiting. Store traffic takes weeks to build.
Realistic earnings
Be clear-eyed here. The Store is a power-law market:
- Most published Actors earn close to nothing. Generic, duplicate or unmaintained scrapers get a few trial runs and die.
- A decent niche Actor with a good README and fast bug fixes typically takes 1 to 3 months to get its first steady paying users. Early income is often tens of dollars a month, not hundreds.
- A portfolio of several maintained Actors in under-served niches is how individual developers usually end up making meaningful money here. There is no public, independent data on typical earnings, so do not plan around a specific number. Apify’s marketing highlights top creators with large monthly payouts. Those are the top of the curve, often backed by years of maintenance. Treat them as a ceiling, not a forecast.
- What moves you up the curve: one or two “anchor” users (an agency or SaaS that builds a product on top of your Actor) can be half your revenue. Make your output clean, stable and well-documented and they will stay for years.
Bottom line: $0 startup cost, slow ramp, compounding upside. Plan for several months of building before judging it. If you need money next week, this is the wrong play.
Risks: legal, bans and platform risk
The legal backdrop (US, which is where most Store customers are).
- hiQ Labs v. LinkedIn (9th Circuit, 2022): scraping publicly accessible data is likely not “unauthorized access” under the Computer Fraud and Abuse Act. But hiQ still lost on breach of contract later that year, because it had used logged-in accounts and fake profiles, and settled. Lesson: public data, logged out.
- Meta v. Bright Data (N.D. Cal., January 2024): the court found Meta’s terms did not prohibit Bright Data from scraping public, logged-out data. Meta dropped the case. X Corp v. Bright Data was dismissed in May 2024, with the judge holding that X’s contract claims were preempted by copyright law.
None of this makes scraping “legal” everywhere. It means the riskiest things are well known:
- Scraping behind a login. Once you accept terms to get an account, you are bound by them. Never build Actors that need user accounts on a platform that bans scraping, and never ask users for their passwords.
- Personal data. Names, emails and phone numbers of private people fall under GDPR in the EU and Indonesia’s UU PDP (Law 27/2022), fully in force since October 2024. Scraping business listings is one thing. Scraping and selling individual contact lists is where real legal exposure starts.
- Copyrighted content. Republishing full articles, images or videos is a different legal problem from extracting prices and facts.
- Bypassing technical blocks aggressively. Solving CAPTCHAs at scale and evading authentication walls moves you from “they do not like it” toward the territory lawyers write letters about.
Platform risk.
- Takedowns. Apify can unpublish an Actor after a complaint from the target site. Do not put all your revenue on one target.
- Site changes. The target redesigns, your Actor returns empty results, users churn and leave bad reviews. Set up a scheduled daily test run in Apify with an alert when the result count drops to zero.
- IP bans. The target blocks Apify datacenter IPs. Residential proxies often fix it but cost about $7 to $8 per GB on Apify, which can wipe out most of your margin. Price hard targets accordingly from day one.
- Rule changes. Apify sets the revenue share and the Store rules. They can change both. Diversify: keep your scrapers portable so you can also run them for direct clients.
Reality check: what does not work
- Cloning a top Actor and pricing it lower. Users of the top Google Maps or Instagram scrapers do not switch to save $0.20 per 1,000. They switch when the incumbent breaks. Compete on niches and reliability.
- Browser scrapers for everything. If Playwright is your default, your compute bill eats your margin. Find the JSON endpoint first.
- Lazy READMEs. A two-line description with no sample output does not convert. Your README is the only salesperson you have.
- Publish and forget. Unmaintained Actors break within months and the Issues tab becomes a public record of it. The “passive” part is billing, not maintenance.
- Charging for nothing. Billing a start fee and then returning zero results is the fastest way to get reviews that kill an Actor.
- Scraping personal data “because it is public”. Public does not mean free to resell under privacy law. Stick to business, product, price and listing data.
- Expecting money in week one. Store discovery is slow. The first months are building the portfolio.
Start today
- Create a free Apify account, install Node.js 20+, then the CLI (
npm install -g apify-cli). - Build and run the Shopify Actor above locally. It takes under an hour and teaches you the whole publish-and-charge loop.
- Spend one evening on the Store hunting niches: high user counts, bad ratings, or regional sites with no good Actor at all.
- Open your niche target in DevTools and look for the JSON API behind the page.
- Ship your first niche Actor this week, price it at 5 to 10 times your measured cost, and set up a daily test run.
- Then build the next one. The money is in the portfolio, not in any single scraper.