# Acquire.com Startup Marketplace Scraper (`crawlerbros/acquire-scraper`) Actor

Scrape public startup acquisition listings from Acquire.com with titles, asking price, annual revenue/profit, category, and descriptions. HTTP-only via the public sitemap + SSR listing pages; no login required.

- **URL**: https://apify.com/crawlerbros/acquire-scraper.md
- **Developed by:** [Crawler Bros](https://apify.com/crawlerbros) (community)
- **Categories:** Automation, Developer tools, Other
- **Stats:** 53 total users, 12 monthly users, 100.0% runs succeeded, 1 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 1,000 results

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Acquire.com Startup Marketplace Scraper

Scrape public startup acquisition listings from **[Acquire.com](https://acquire.com)** — headline, category, asking price, annual revenue, annual profit, description, derived revenue / profit multiples, and canonical URL. HTTP-only; **no login, no cookies, no browser**.

### What this scrapes

Acquire's marketplace has two surfaces:

| Surface                                 | URL shape                       | Auth | Scrapable? |
|-----------------------------------------|----------------------------------|------|------------|
| **Public SEO listing page**            | `/startup/<slug>`               | No   | Yes        |
| Internal SPA marketplace (SPA with filters) | `/startups/<id>` / `/startups?categories=...` | Yes (login required) | No |

This actor targets the public listing pages — the same URLs Acquire exposes to Google via `robots.txt` and `sitemap.xml`. The auth-gated SPA variant (asking-price history, customers, churn, GA analytics, tech stack, etc.) is **not** accessible without an authenticated Acquire account and is intentionally out of scope.

### Output (per listing)

| Field               | Always present? | Description |
|---------------------|:---------------:|-------------|
| `type`              | Yes | `"acquire_listing"` |
| `url`               | Yes | Canonical `/startup/<slug>` URL |
| `id`                | Yes | 10-character short ID from the slug |
| `scrapedAt`         | Yes | ISO-8601 UTC timestamp |
| `listingHeadline`   | Yes | Listing title (e.g. "Turnkey Calendly Alternative — 500+ Users …") |
| `listingType`       | When category known | Always `"startup_acquisition"` |
| `category`          | When present | `SaaS`, `Ecommerce`, `Content`, `Services`, `Mobile Apps`, etc. |
| `askingPrice`       | When present | USD integer |
| `priceCurrency`     | When present | ISO currency from JSON-LD `offers.priceCurrency` (e.g. `"USD"`) |
| `revenueAnnual`     | When present | USD integer |
| `profitAnnual`      | When present | USD integer |
| `revenueMultiple`   | When derivable | `askingPrice / revenueAnnual` |
| `profitMultiple`    | When derivable | `askingPrice / profitAnnual` |
| `about`             | When present | Full description (long form, with bullet points) |
| `status`            | When known | `"available"` or `"sold"` |
| `image`             | When present | Preview image URL from JSON-LD `image` / `og:image` |
| `brand`             | When present | Brand name from JSON-LD `brand.name` (typically `"Acquire.com"`) |
| `source`            | When present | Site name from `og:site_name` (typically `"Acquire.com"`) |
| `canonicalUrl`      | When present | Canonical URL from JSON-LD `url` / `<link rel="canonical">` / `og:url` |
| `pageType`          | When present | Open Graph `og:type` (typically `"website"`) |

Fields are omitted rather than emitted as `null` — if you see a key, the value is real.

If zero listings match your filters, a single `acquire_blocked` sentinel row is emitted so runs always exit 0.

### Input

| Field | Type | Description |
|---|---|---|
| `urls` | string\[] | Direct `/startup/<slug>` URLs. Overrides sitemap / filter search. |
| `categories` | string\[] | Filter by category (e.g. `SaaS`, `Ecommerce`). |
| `minPrice` | integer | Minimum asking price (USD). |
| `maxPrice` | integer | Maximum asking price (USD). |
| `minAnnualRecurringRevenue` | integer | Minimum annual revenue (USD). |
| `locations` | string\[] | Location substring filter (case-insensitive). |
| `verifiedBusiness` | boolean | Only return listings whose copy mentions "verified". Default `false`. |
| `sortBy` | enum | `newest` (default), `oldest`, `price-low`, `price-high`. Based on sitemap `lastmod`. |
| `maxItems` | integer | 1 – 500. Default 3. |
| `proxyConfiguration` | object | Apify proxy. Default Residential US. |

### How it works

1. Fetch the public sitemap at `https://app.acquire.com/sitemap.xml` (≈1,300 listing URLs with `<lastmod>`).
2. Sort entries by `lastmod` (newest / oldest) or over-fetch + sort by price (for `price-low` / `price-high`).
3. For each `/startup/<slug>` URL, fetch the server-rendered page via `curl_cffi` with Chrome-131 TLS impersonation. Proxy rotates per retry.
4. Parse the `application/ld+json` `Product` schema + the hidden `#ssr-content` block (`<h1>`, `<p>`, `<dl>` with Category / Asking Price / Annual Revenue / Annual Profit) + meta tags (`article:section`, `og:description`).
5. Apply post-filters (category, price bounds, annual revenue, locations, verified).
6. Emit up to `maxItems`; emit a sentinel on zero matches.

### FAQ

**Do I need to log into Acquire?** No — we only scrape the public pages exposed to search engines.

**Can you scrape churn, customer count, tech stack, GA analytics, team size?** No — those live behind the authenticated SPA (`/startups/<id>`) and are not part of the public SSR payload. We only emit fields we can populate.

**Why does `/startups/<id>` not work?** The app's internal route is JS-rendered and redirects unauthenticated visitors to `/signin`. The public `/startup/<slug>` route (note the singular) is the one Acquire ships for crawlers.

**What counts as "verified"?** The public SSR pages don't expose a structured verified flag. When `verifiedBusiness = true` we keep listings whose headline or description literally mentions "verified".

**Why does `sortBy` use `lastmod`?** The sitemap is the only cross-category public index we have — `lastmod` is the closest proxy to listing freshness. Price sorts over-fetch and re-sort in-process.

### Reliability notes

- Residential US proxy is the default — Acquire's origin is polite to datacenter IPs too, but residential keeps long-running daily tests consistent.
- Per-retry proxy rotation (fresh session\_id each attempt) to shake off any transient 403 / rate limit.
- Sentinel emitted on zero pushes so daily Apify test runs always exit 0.

# Actor input Schema

## `urls` (type: `array`):

Direct Acquire.com listing URLs. Accepts canonical /startup/<slug> (preferred, public SSR pages) or app /startups/<id> URLs. When provided, overrides category / filter search.

## `categories` (type: `array`):

Filter listings by Acquire category. Matches the listing's `Category` field (case-insensitive). Leave empty for all categories.

## `minPrice` (type: `integer`):

Only return listings with asking price >= this value.

## `maxPrice` (type: `integer`):

Only return listings with asking price <= this value.

## `locations` (type: `array`):

Optional country / region substrings to match against the listing's location field (case-insensitive).

## `minAnnualRecurringRevenue` (type: `integer`):

Only return listings with annual revenue >= this value.

## `maxAnnualRecurringRevenue` (type: `integer`):

Only return listings with annual revenue <= this value.

## `customers` (type: `integer`):

Only return listings whose `Customers` count is >= this value. Parsed from the SSR summary (e.g. '1.2K' -> 1200).

## `minAge` (type: `integer`):

Only return listings where the business is at least this many years old (derived from the Founded / Date Founded field).

## `maxAge` (type: `integer`):

Only return listings where the business is at most this many years old.

## `minRevenueMultiple` (type: `integer`):

Only return listings where askingPrice / annualRevenue >= this value.

## `maxRevenueMultiple` (type: `integer`):

Only return listings where askingPrice / annualRevenue <= this value.

## `minProfitMultiple` (type: `integer`):

Only return listings where askingPrice / annualProfit >= this value.

## `maxProfitMultiple` (type: `integer`):

Only return listings where askingPrice / annualProfit <= this value.

## `minTTMGrossRevenue` (type: `integer`):

Trailing-twelve-month gross revenue minimum. Applied against the listing's annual revenue when TTM-specific data isn't exposed on the public SSR page.

## `maxTTMGrossRevenue` (type: `integer`):

Trailing-twelve-month gross revenue maximum.

## `minTTMNetProfit` (type: `integer`):

Trailing-twelve-month net profit minimum. Applied against the listing's annual profit when TTM-specific data isn't exposed on the public SSR page.

## `maxTTMNetProfit` (type: `integer`):

Trailing-twelve-month net profit maximum.

## `minAnnualGrowthRate` (type: `integer`):

Only return listings whose annual growth rate percentage is >= this value. Parsed from the SSR `Growth Rate` / `Annual Growth` field when present.

## `maxAnnualGrowthRate` (type: `integer`):

Only return listings whose annual growth rate percentage is <= this value.

## `recastFinancials` (type: `boolean`):

When checked, only return listings whose SSR `Financials` section indicates seller-recast financials.

## `connectedMetrics` (type: `boolean`):

When checked, only return listings whose SSR summary shows connected analytics (e.g. Google Analytics, Stripe).

## `underAdvisory` (type: `boolean`):

When checked, only return listings the SSR flags as being under Acquire advisory support.

## `hideUnderOffer` (type: `boolean`):

When checked, skip listings whose status is `Under Offer`.

## `verifiedBusiness` (type: `boolean`):

When checked, only return listings Acquire flags as verified.

## `sortBy` (type: `string`):

How listings from the public sitemap are traversed. `newest`/`oldest` use the sitemap `lastmod`; `price-low`/`price-high` sort after filtering.

## `maxItems` (type: `integer`):

Maximum number of listings to return.

## `proxyConfiguration` (type: `object`):

Apify proxy. Residential US recommended for reliable SSR fetches; datacenter often works too.

## Actor input object example

```json
{
  "urls": [],
  "categories": [],
  "locations": [],
  "recastFinancials": false,
  "connectedMetrics": false,
  "underAdvisory": false,
  "hideUnderOffer": false,
  "verifiedBusiness": false,
  "sortBy": "newest",
  "maxItems": 3,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "US"
  }
}
```

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "urls": [],
    "categories": [],
    "locations": [],
    "maxItems": 3,
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ],
        "apifyProxyCountry": "US"
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("crawlerbros/acquire-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "urls": [],
    "categories": [],
    "locations": [],
    "maxItems": 3,
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
        "apifyProxyCountry": "US",
    },
}

# Run the Actor and wait for it to finish
run = client.actor("crawlerbros/acquire-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "urls": [],
  "categories": [],
  "locations": [],
  "maxItems": 3,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "US"
  }
}' |
apify call crawlerbros/acquire-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,crawlerbros/acquire-scraper"
        }
    }
}

```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/JC1FIw2qHM78ocHBd/builds/evxSniFcg6kXo7k1E/openapi.json
