# Turo Scraper - Vehicle Search, Trip Quotes and Hosts (`abotapi/turo-scraper`) Actor

Collect Turo vehicles by location and trip dates or listing URLs. Get make, model, year, ratings, photos, dated price estimates, vehicle details and public host profiles, with resume and recurring change detection.

- **URL**: https://apify.com/abotapi/turo-scraper.md
- **Developed by:** [Abot API](https://apify.com/abotapi) (community)
- **Features:** Limited permissions
- **Stats:** 12 total users, 3 monthly users, 65.9% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.20 / 1,000 vehicle results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
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?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
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.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## 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.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Turo Scraper: Vehicle Listings, Trip Quotes & Host Profiles

Turo Scraper turns Turo into structured vehicle data. Search by location and trip dates, or paste direct vehicle listing links, to collect make, model, year, rating, public location, photos and a dated price estimate for your exact trip. Turn on full details to add specifications, delivery options, owner information and the public host profile. Export to JSON, CSV or Excel, or pull results straight into your app through the API.

### Why This Scraper?

- **Two ways in.** Search by location, make and trip dates with filters and sorting, or paste direct links (vehicle, city, make, type or search pages).
- **Dated trip quotes.** Each vehicle is joined to a Turo-estimated quote for your exact trip dates, not just the generic search-card daily average, so `tripTotal` and `quotedDailyPrice` reflect the dates you asked for.
- **Reviews, filters and sorting.** Each row can carry its newest guest reviews (up to `maxReviews`), and you can narrow by make, year, rating, fuel type, vehicle type and price window, then sort by price or distance (Turo applies the sort across its whole result set).
- **Full details on demand.** Switch on specifications, delivery options, owner information and the public host profile per run, and pay the detail surcharge only for records where a host profile was actually fetched.
- **Resume without repaying.** Continue one interrupted run by its run ID and skip vehicles it already returned, instead of recollecting from scratch.
- **Built for recurring monitoring.** Incremental mode returns only new and updated vehicles on repeat runs against the same search (and, with **Emit expired listings** on, vehicles that disappear or return), so scheduled comparisons don't reprocess everything each time.
- **Fails loudly when blocked.** If a location search or a pasted listing link can't be read, the run stops with an error instead of quietly returning an empty dataset that looks like "no vehicles found."

### Use Cases

- **Car-sharing market research:** compare vehicle types, ratings and pricing by city or make to benchmark host supply against your own fleet.
- **Dynamic pricing analysis:** pull dated quotes across many vehicles to build a competitor pricing baseline for a specific trip window.
- **Fleet and demand monitoring:** schedule Incremental mode runs to catch new listings, price changes, or vehicles that disappear from a market.
- **Host outreach and lead lists:** collect public host profiles and completed-trip counts to identify active hosts worth contacting.
- **Travel and rental comparison tools:** feed live vehicle availability and dated pricing into your own booking or comparison app.

### Data You Get

> Sample shape: values are illustrative placeholders, not from a live record.

| Field | Example |
| --- | --- |
| `id` | `"101"` |
| `url` | Turo link to the vehicle listing |
| `title` | `"2020 Tesla Model 3"` |
| `make` / `model` | `"Tesla"` / `"Model 3"` |
| `year` | `2020` |
| `vehicleType` | `"CAR"` |
| `rating` | `4.8` |
| `completedTrips` | `126` |
| `hostId` | `1101` |
| `city` / `state` / `country` | `"Los Angeles"` / `"CA"` / `"US"` |
| `latitude` / `longitude` | `34.0522` / `-118.2437` |
| `averageDailyPrice` | `{ "amount": 35, "currency": "USD" }` (search-card estimate) |
| `tripTotal` / `currency` | `117` / `"USD"` (dated quote for the requested trip) |
| `quotedDailyPrice` | `39` (dated quote, before taxes and checkout add-ons) |
| `quoteStatus` | `"complete"` or `"unavailable"` |
| `startDateTime` / `endDateTime` | requested trip window used for the quote |
| `detailStatus` / `hostStatus` | `"complete"`, `"not_requested"`, or `"unavailable"` |
| `images` | array of photo URLs |
| `location` | structured place object; precision noted when known |
| `reviews` / `reviewsReturned` / `reviewsStatus` | newest guest reviews on the row, how many, and `"complete"`, `"not_requested"` or `"unavailable"` |
| `scopeId` / `scrapedAt` | baseline scope hash and collection timestamp |

With full details enabled (the default), each record also carries `description` (the host's listing text), `owner` (the public owner block), `basicCarDetails` (specifications such as seat count), `host` (the separate public host profile: bio and verification badges), `quote` (the full dated quote object, including `nonIncludedLineItems` and `pricingDisplay`), `searchVehicle` (the raw public search card) and `detail` (the raw public vehicle response), so nothing public is lost. Every row also carries a `coverage` object reporting how the search behind it went (returned count, source total, whether it was a complete scan). In Incremental mode, rows also carry `changeType` (`NEW`, `UPDATED`, `UNCHANGED`, `REAPPEARED`, or `EXPIRED`), `changedFields`, `firstSeenAt` and `lastSeenAt`.

### How to Use

1. Pick a **mode**: `search` (locations and trip dates) or `url` (paste direct vehicle listing links).
2. For search mode, set `locations`, and optionally `makes`, `vehicleType`, `fuelType` and `minTotalPrice`/`maxTotalPrice` to narrow the search, plus `minYear`, `maxYear` and `minRating` to filter what comes back before the output limit. Pick an order with `sortBy`.
3. Set trip dates with `startDateTime` and `endDateTime`, or leave both empty for the default three-day trip starting seven days out. Turn `fetchDetails` on or off, set `maxReviews` (0 skips reviews) and `maxItems`, then click **Start**.
4. Download the dataset as JSON, CSV or Excel, or read it through the API.

**Filtered search with a fixed trip window:**

```json
{
  "mode": "search",
  "locations": ["Chicago, Illinois"],
  "makes": ["Tesla"],
  "minYear": 2020,
  "minRating": 4.5,
  "startDateTime": "2026-11-10T10:00:00",
  "endDateTime": "2026-11-13T10:00:00",
  "maxItems": 15
}
```

**Electric SUVs under a price, cheapest first:**

```json
{
  "mode": "search",
  "locations": ["Denver, Colorado"],
  "vehicleType": ["SUV"],
  "fuelType": ["ELECTRIC"],
  "maxTotalPrice": 400,
  "sortBy": "daily_price_low_to_high",
  "maxReviews": 0,
  "maxItems": 30
}
```

**Paste specific vehicle links:**

```json
{
  "mode": "url",
  "urls": [
    "https://turo.com/us/en/car-rental/united-states/los-angeles-ca/tesla/model-3/101"
  ],
  "maxReviews": 20,
  "fetchDetails": true
}
```

**Recurring monitoring of one market:**

```json
{
  "mode": "search",
  "locations": ["Miami, Florida"],
  "startDateTime": "2026-11-10T10:00:00",
  "endDateTime": "2026-11-13T10:00:00",
  "maxItems": 0,
  "incrementalMode": true,
  "stateKey": "miami-monitor",
  "emitExpired": true,
  "ignoreFieldsForChanges": ["rating", "completedTrips"]
}
```

#### Run it from your code

Python:

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("abotapi/turo-scraper").call(run_input={"mode": "search", "locations": ["Los Angeles, California"], "maxItems": 20})
for vehicle in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(vehicle["title"], vehicle["tripTotal"], vehicle["currency"])
```

JavaScript:

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

const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });
const run = await client.actor('abotapi/turo-scraper').call({ mode: 'search', locations: ['Los Angeles, California'], maxItems: 20 });
const { items } = await client.dataset(run.defaultDatasetId).listItems();
```

Or connect it to Make, Zapier, n8n, Google Sheets or webhooks from the **Integrations** tab.

#### URL mode shapes

URL mode accepts these Turo links (United States pages only), each tried live:

| Link shape | Example |
|---|---|
| City page | `https://turo.com/us/en/car-rental/united-states/los-angeles-ca` |
| City + make page | `https://turo.com/us/en/car-rental/united-states/los-angeles-ca/tesla` |
| Vehicle-type page | `https://turo.com/us/en/suv-rental/united-states/los-angeles-ca` |
| Country + make page | `https://turo.com/us/en/car-rental/united-states/tesla` |
| Vehicle page | `https://turo.com/us/en/car-rental/united-states/los-angeles-ca/chevrolet/sonic/3864730` |
| Search link | `https://turo.com/us/en/search?location=Los%20Angeles%2C%20CA` (optionally with `makes`, `types`, `startDate`, `endDate`) |

Listing pages return Turo's first result page for your trip dates, at most 200 vehicles each (URL mode does not run the per-vehicle-type searches); a country-wide page is capped there (the run reports the cap as incomplete, so no vehicle is ever marked expired from it). The make in a link must be one Turo lists (for example `mercedes-benz` becomes Mercedes-Benz and `land-rover` becomes Land Rover) and the city must carry its two-letter state (`los-angeles-ca`). Pages for other countries, unknown makes or unrecognisable slugs are skipped with a warning in the log, never guessed. Search-mode inputs (`makes`, `vehicleType`, `fuelType`, price window, `minYear`/`maxYear`/`minRating`, `startDateTime`/`endDateTime`) apply to search mode only. In URL mode a link's own query is read for exactly these parameters: `location`, `makes` (names or slugs, comma separated or repeated), `types` (`CAR`, `SUV`, `MINIVAN`, `TRUCK`, `VAN`, `CARGO_MINIVAN`, `BOX_TRUCK`) and `startDate`/`endDate` with optional `startTime`/`endTime` (`MM/DD/YYYY` or `YYYY-MM-DD`, `HH:MM`). They are applied to that link only: the search is filtered to those makes and types and quoted for the link's trip dates, which replace your run-level dates for that link (a vehicle link's dates apply to its quote the same way). A make or type in the link combined with a make or type page path must overlap. Location-only view parameters (`country`, `region`, `latitude`, `longitude`, `placeId`, `locationType`, `deliveryLocationType`, `defaultZoomLevel`, `isMapSearch`, `itemsPerPage`, `useDefaultMaximumDistance`, `pickupType`, `age`) are ignored. Tracking parameters (`utm_*`, `gclid`, `fbclid`, `ref`) are ignored too. A link that carries any other query parameter (for example `sortType`, `fuelTypes`, `minYear`, `maxYear`, `features`, `transmission`, a price limit), an unknown make or type, or only one of the two dates is skipped with a warning in the log rather than answered with unfiltered rows; use search mode for those filters. Behaviour change: the `urls` field now has a default, so a URL-mode run with the field untouched scrapes one example of each shape (up to `maxItems`) instead of failing on an empty list.

#### Turo's own limits and `maxItems`

Turo caps a single search at around 200 vehicles, even when its reported total is larger, and cannot be paged past that. Searches without a `vehicleType` filter work around this with the per-vehicle-type searches described under `vehicleType`, which can still leave a very large city incomplete. `makes` narrows the search itself at the source; `minYear`, `maxYear` and `minRating` are applied locally to what Turo returned, before `maxItems` is enforced, so a narrow filter can return fewer records than `maxItems` while other matching vehicles remain outside what was fetched. Narrow your `locations` or add a make filter for more targeted coverage. `maxItems: 0` removes this actor's own limit but does not make a run exhaustive.

#### Filters, sort and reviews

- **`vehicleType`, `fuelType`, `makes`:** narrow the search at the source, so Turo's 200-vehicle single-search cap applies to the narrowed set. When a search with no `vehicleType` hits that cap and `maxItems` is larger than the 200 rows it returned (or a local filter such as `minYear`, `maxYear`, `minRating` or a total-price window is set), the actor runs one extra search per vehicle type (car, SUV, minivan, truck, van, cargo minivan, box truck) and merges them by vehicle ID. The first search keeps its order at the top. When `maxItems` is already covered by the first 200 rows, the extra searches are skipped. If one of the extra searches fails, the rows gathered so far are kept and the location is reported as incomplete.
- **`age`:** accepted for compatibility only. Turo's search does not take a renter age, so it does not change the results.
- **`minTotalPrice` / `maxTotalPrice`:** keep vehicles whose dated trip total (before taxes) falls in the window.
- **`sortBy`:** sent to Turo, which orders its whole result set: `relevance` (default), `daily_price_low_to_high`, `daily_price_high_to_low` and `distance_away` (nearest first to the search point). The cheapest or priciest vehicles in a city are therefore returned first even when the city has more than 200 matches.
- **Reviews:** `maxReviews` (default 20, at most 1000 per vehicle, 0 skips) returns each vehicle's newest guest reviews on the same row, with the host reply when there is one. `reviewsStatus` tells you whether they were fetched.

#### Resume and Incremental mode

- **Resume** (`resumeFromRunId`) continues one interrupted run with the same query, dates and detail settings: it skips vehicle IDs already returned there, so you don't pay twice. The trip dates are part of that scope, so a Resume with `startDateTime`/`endDateTime` left empty (which roll forward to seven days out from whenever the run starts) can fail the scope check on a later day; set explicit dates for a Resume you expect to run more than once.
- **Incremental mode** (`incrementalMode`) is for recurring runs over the same scope (mode, locations or URLs, trip dates, filters, `fetchDetails` and `maxReviews`; a different `maxReviews` gets its own baseline instead of re-billing the old one). Choose fields to ignore with `ignoreFieldsForChanges` (dotted paths such as `host.name`): they stay in the output but never trigger `UPDATED` or appear in `changedFields`, and changing the list never causes a one-time wave of `UPDATED` rows. Each vehicle is classified `NEW`, `UPDATED` (with `changedFields`), or `UNCHANGED` (suppressed and not billed unless `emitUnchanged` is on); with `emitExpired` on and a clean complete scan, a vehicle can also classify `EXPIRED` or, on a later such run, `REAPPEARED`. An existing baseline cannot be combined with `resumeFromRunId`. Trip dates are part of the scope too, so leaving `startDateTime`/`endDateTime` empty (they roll forward daily) starts a fresh baseline on every run and prevents `EXPIRED`/`REAPPEARED` from ever firing; set explicit dates for recurring monitoring.
- `EXPIRED` rows, and the `REAPPEARED` classification that can follow them, only ever come from a run that scanned its whole scope without hitting `maxItems`, without a resume, and without any quote, detail or host lookup failing along the way, and only when `emitExpired` was turned on for that scan. A capped, resumed, partial or degraded run never marks anything expired, so one bad run can't wipe out your baseline; the next clean run re-evaluates those vehicles instead.
- Baselines are always kept separate per search scope; `stateKey` only gives a baseline a memorable name so you can track several searches independently. Avoid scheduling overlapping runs against the same key, since state is committed only after a run finishes normally.

#### Output views

The dataset has four ready-made views, also linked from the run's output tab: **Vehicles** (core fields, price quote and change type), **Details and hosts** (description, specifications, owner, host profile, images, coverage), **Reviews** (rating, text, date, reviewer, host reply) and **Changes (incremental)** (change type, changed fields, first and last seen).

### Send results into your apps (MCP connectors)

Optionally pipe the scraped results into the apps you already use, via Model Context Protocol (MCP) connectors. This is an extra delivery step **after** the scrape: the Apify dataset is never changed.

**What gets written to the connector:** a condensed, human-readable **summary** of each record, not the full JSON. Each vehicle becomes one entry with a **title** and its key fields flattened to plain text. The **complete record always stays in the Apify dataset**.

1. Authorize a connector once under **Apify → Settings → Integrations** (Notion, Linear, Airtable, or Apify).
2. Select it in the **"Pipe results into your apps"** input field. (If the picker is empty, you haven't authorized a connector yet.)
3. For **Notion**, also set `notionParentPageUrl` to the page where items should be created.

The connection is mediated by Apify's MCP proxy, so this actor never sees your third-party credentials. Leave the field empty to skip.

### Input Parameters

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `mode` | string | `search` | `search` (locations and trip dates) or `url` (paste vehicle listing links). |
| `locations` | array | no default (prefill: `["Los Angeles, California"]`) | Search mode only. City, airport, or place names. |
| `makes` | array | no default | Search mode only. Vehicle make names, such as `Tesla`. |
| `minYear` | integer | no default | Search mode only. Applied to returned vehicles before the output limit. |
| `maxYear` | integer | no default | Search mode only. Applied to returned vehicles before the output limit. |
| `minRating` | number | no default | Search mode only. Excludes unrated vehicles; applied before the output limit. |
| `fuelType` | array | no default | Search mode only. `GAS`, `HYBRID`, `ELECTRIC` or `DIESEL`. |
| `vehicleType` | array | no default | Search mode only. `CAR`, `SUV`, `MINIVAN`, `TRUCK`, `VAN`, `CARGO_MINIVAN` or `BOX_TRUCK`. |
| `minTotalPrice` | integer | no default | Search mode only. Drop vehicles whose quoted trip total (before taxes) is lower. |
| `maxTotalPrice` | integer | no default | Search mode only. Drop vehicles whose quoted trip total (before taxes) is higher. |
| `age` | integer | `30` | Renter age, 18 to 55. Accepted for compatibility; Turo's search does not use it, so it does not change results. |
| `sortBy` | string | `relevance` | `relevance`, `daily_price_low_to_high`, `daily_price_high_to_low` or `distance_away`. |
| `urls` | array | one example per supported shape (5 links) | URL mode only. Vehicle pages, city pages, city + make pages, vehicle-type pages (`suv-rental`), country + make pages and `/search?location=` links. |
| `startDateTime` | string | no default | Local ISO trip start, both modes. Omit together with `endDateTime` for a three-day trip starting in seven days. |
| `endDateTime` | string | no default | Local ISO trip end, after `startDateTime`. Must be supplied together with the start. |
| `maxReviews` | integer | `20` | Guest reviews returned on each vehicle row (rating, text, date, reviewer, host reply), newest first. `0` skips reviews; at most 1000 per vehicle. Charges one detail-enrichment event per 10 reviews delivered (rounded up). |
| `fetchDetails` | boolean | `true` | Return full specifications and the public host profile. Charges one detail-enrichment event per returned record with a successfully fetched host profile. |
| `maxItems` | integer | `20` | Maximum returned records. `0` removes this actor's output limit; Turo's own per-location cap still applies. |
| `proxy` | object | Residential (Apify Proxy) | Connection settings. The default is Apify's Residential proxy group; keep it, or supply your own proxy URLs. If the chosen connection cannot be used, the run continues on a backup connection. |
| `resumeFromRunId` | string | no default | Continue one interrupted run with the same query, dates and detail settings. |
| `incrementalMode` | boolean | `false` | Remember a baseline and return only new and changed vehicles on repeat runs. A vehicle is UPDATED when any scraped vehicle field changes (price, rating, trips, location, images, details, host); scrape timestamps, status flags and the duplicate search-card copies of images and daily price are never compared. |
| `ignoreFieldsForChanges` | array | `[]` | Incremental mode only. Field names (dotted paths like `host.name` for nested keys) that never trigger UPDATED or appear in `changedFields`; they stay in the output. Changing this list never causes a one-time wave of UPDATED rows. |
| `stateKey` | string | no default | Optional name for a baseline. Use different keys for different searches. |
| `emitUnchanged` | boolean | `false` | Also return (and bill) unchanged vehicles in Incremental mode. |
| `emitExpired` | boolean | `false` | Also return (and bill) vehicles no longer found, only after a complete scan in Incremental mode. |
| `mcpConnectors` | array | no default | Optional: send a summary of each record to apps you authorized under Integrations. |
| `notionParentPageUrl` | string | no default | Notion connector only: page under which records are created. |
| `maxNotifyListings` | integer | `50` | Cap on items written to each connector per run. Does not affect the dataset. |

### Output Example

> Sample shape: values are illustrative placeholders, not from a live record.

```json
{
  "id": "101",
  "url": "https://turo.com/us/en/car-rental/united-states/los-angeles-ca/tesla/model-3/101",
  "title": "2020 Tesla Model 3",
  "make": "Tesla",
  "model": "Model 3",
  "year": 2020,
  "vehicleType": "CAR",
  "rating": 4.8,
  "completedTrips": 126,
  "hostId": 1101,
  "city": "Los Angeles",
  "state": "CA",
  "country": "US",
  "latitude": 34.0522,
  "longitude": -118.2437,
  "location": {
    "city": "Los Angeles",
    "country": "US",
    "latitude": 34.0522,
    "longitude": -118.2437,
    "precision": { "level": "APPROXIMATE" }
  },
  "images": ["https://images.turo.com/media/vehicle/images/sample1.jpg"],
  "averageDailyPrice": { "amount": 35, "currency": "USD" },
  "tripTotal": 117,
  "currency": "USD",
  "quotedDailyPrice": 39,
  "quote": {
    "totalTripPrice": { "amount": 117, "currencyCode": "USD" },
    "vehicleDailyPriceAfterDiscount": { "amount": 39, "currencyCode": "USD" },
    "nonIncludedLineItems": [{ "type": "PROTECTION" }]
  },
  "quoteStatus": "complete",
  "startDateTime": "2026-11-10T10:00:00",
  "endDateTime": "2026-11-13T10:00:00",
  "detailStatus": "complete",
  "hostStatus": "complete",
  "basicCarDetails": { "numberOfSeats": 5 },
  "owner": { "id": 1101, "name": "Public Host" },
  "host": { "driver": { "id": 1101 }, "bio": "Public biography", "verifications": { "emailVerified": true } },
  "coverage": { "query": "Los Angeles, California", "returned": 18, "totalHits": 18, "complete": true, "reason": "source_end" },
  "scopeId": "8f2a1c4d9b3e7f60a1c2d3e4",
  "scrapedAt": "2026-11-05T02:00:00+00:00"
}
```

### Plan Requirement

**Connection.** The default Connection setting uses Apify's Residential proxy group, so paid Apify plans need no setup. In our checks Turo's data endpoints also answered over datacenter addresses, so when the residential connection is unavailable the run falls back to a backup connection and continues. Apify free plans do not include residential proxies; on a free plan the run uses the backup connection, or you can supply your own proxy URLs in the Connection field or upgrade at https://apify.com/pricing.

What you will see in the run log when the default connection is not available:

- *"The selected connection is unavailable to this run. Apify free plans do not include residential proxies. Continuing on a backup connection; some pages may still be unavailable."* The run continues on a backup connection, and logs *"Served over the backup connection (the default connection was unavailable)."*
- If nothing could be read, the run fails with one of: *"The proxy connection was refused for this account, so no page could be read. Your Apify plan does not include proxy access; a paid plan is required"*; *"No results: residential proxies are unavailable on this run (Apify free plans do not include them), and the backup connection could not load the requested Turo pages"*; or *"Could not load any results page (the requests failed), so nothing was collected. This is a connection problem, not an empty search. Please try again later."* The last one means a connection problem, not an empty search.

Vehicle results, optional detail enrichment and reviews are billed as pay-per-event usage; see the actor's **Pricing** tab for current rates before running at scale.

### FAQ

#### How much does it cost?

You pay per vehicle record returned, plus one optional detail-enrichment event for each record where full details were requested and a public host profile was actually fetched. Reviews add one detail-enrichment event per 10 reviews delivered on a returned record (rounded up; a record with 11 reviews is 2 events), charged after that record is saved; this reuses the existing detail-enrichment event, listed on the **Pricing** tab. The default `maxReviews` of 20 means up to 2 such events per vehicle. Set `maxReviews` to 0 to avoid it. Suppressed unchanged rows in Incremental mode and failed enrichment attempts are not billed. The **Pricing** tab shows current rates; use **Maximum vehicles** (`maxItems`) to cap the cost of any run.

#### Is it legal to scrape Turo?

This actor collects only publicly available vehicle listing data. You are responsible for how you use it: follow Turo's terms and the laws that apply to you, and get legal advice if you plan commercial redistribution. Listing facts such as make, model and year are generally not protected, but photos and host-written text may carry separate rights.

#### Can I track new or changed listings on a schedule?

Yes. Schedule the actor from the **Schedules** tab and turn on **Incremental mode**. Each run then returns only new and updated vehicles for the same search scope, and unchanged vehicles are not billed unless you opt in; with **Emit expired listings** also on and a clean complete scan, expired and reappeared vehicles are included too. Keep the trip dates fixed (don't leave them empty) so the scope stays the same run to run.

#### Why did my run return fewer vehicles than `maxItems`?

Turo caps each single search around 200 vehicles regardless of how many it reports in total; search mode adds per-vehicle-type searches when needed (see `vehicleType`) but a very large city can still be incomplete. `makes`, `vehicleType` and `fuelType` narrow the search at the source; `minYear`, `maxYear` and `minRating` are applied locally before `maxItems`, so a narrow filter can leave matching vehicles outside what was returned. Widen the filters or add more locations for broader coverage.

#### Why did my run fail instead of returning an empty dataset?

A run fails when any location search or pasted listing link can't be read, such as a removed vehicle link, or when the input itself is invalid (for example an unreadable Resume ID, or Resume combined with an existing Incremental baseline). A location with no matching address returns zero vehicles for that location normally, without failing the run. If only some per-vehicle detail or quote lookups fail partway through, you still get the base records that were found, with their status fields marked accordingly.

#### Can I use it with AI agents or MCP?

Yes. Call it from any Apify integration or MCP client, and use the connector field to push results into Notion, Linear or Airtable.

### 🔗 Want more car data?

Pair this actor with these related scrapers from the same team:

<table>
<tr><td>🚗 <a href="https://apify.com/abotapi/easyauto123-cars-scraper"><b>EasyAuto123 Scraper</b></a><br>Scrape EasyAuto123 vehicle listings into clean structured data. Extract prices, VINs...</td><td>🚗 <a href="https://apify.com/abotapi/cars-com-scraper"><b>Cars.com Scraper</b></a><br>Scrape cars.com listings by make, ZIP and radius. 90+ fields per car: price, MSRP...</td></tr>
<tr><td>🚗 <a href="https://apify.com/abotapi/mobile-de-scraper"><b>mobile.de Scraper</b></a><br>Scrape mobile.de vehicle listings with 30+ fields including price, mileage, registration...</td><td>🚗 <a href="https://apify.com/abotapi/autotrader-com-scraper"><b>Autotrader US</b></a><br>Scrape US car listings from Autotrader by make, model, ZIP, price, year, body style...</td></tr>
<tr><td>🚗 <a href="https://apify.com/abotapi/autotrader"><b>AutoTrader UK Scraper</b></a><br>Pull structured vehicle listings from autotrader.co.uk at scale. Search by filters or use...</td><td>🏷️ <a href="https://apify.com/abotapi/tutti-ch-scraper"><b>Tutti.ch Scraper</b></a><br>Scrape listings from tutti.ch across all 23 categories, including vehicles, property...</td></tr>
</table>

👉 [Browse all abotapi scrapers](https://apify.com/abotapi)

### 💬 Support

- 🐞 **Found a bug or a missing field?** Open a ticket on the [Issues tab](https://apify.com/abotapi/turo-scraper/issues/open). We usually reply within hours.
- 🛠️ **Need another site, extra fields or a private build?** Email <contact@abotapi.com>.
- ⭐ **Enjoying it?** A quick review on the actor page helps other users find it.

# Actor input Schema

## `mode` (type: `string`):

Search locations or load individual vehicle links.

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

Search mode only. City, airport or place names.

## `makes` (type: `array`):

Search mode only. Vehicle make names, such as Tesla.

## `minYear` (type: `integer`):

Search mode only. Applied to returned vehicles before the output limit.

## `maxYear` (type: `integer`):

Search mode only. Applied to returned vehicles before the output limit.

## `minRating` (type: `number`):

Search mode only. Excludes unrated vehicles and applies before the output limit.

## `fuelType` (type: `array`):

Search mode only. Keep only these engine types.

## `vehicleType` (type: `array`):

Search mode only. Keep only these vehicle types.

## `minTotalPrice` (type: `integer`):

Search mode only. Drop vehicles whose quoted trip total (before taxes) is lower.

## `maxTotalPrice` (type: `integer`):

Search mode only. Drop vehicles whose quoted trip total (before taxes) is higher.

## `age` (type: `integer`):

Renter age, 18 to 55. Accepted for compatibility; Turo's search does not use it, so it does not change results.

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

Order of the returned vehicles, applied by Turo across its whole result set: relevance, daily price (low to high or high to low) or distance away (nearest first).

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

URL mode. Turo links to scrape; each is one source. United States pages only. Supported: a vehicle page (read directly), a city page, a city + make page, a vehicle-type page such as suv-rental, a country + make page, or a /search?location=... link. From a link the actor reads location, makes, types and startDate/endDate (+startTime/endTime) and applies them to that link; a link with any other filter parameter (for example sortType, fuelTypes, minYear, maxYear, features, transmission), an unknown make or type, or a single date is skipped with a warning. Listing pages return Turo's first page of results for your trip dates (at most 200 vehicles per page; country-wide pages are capped there). Links for other countries, unknown makes or unrecognisable slugs are skipped with a warning.

## `startDateTime` (type: `string`):

Both modes. Local ISO date and time without a time zone. Set both dates, or omit both for a three-day trip beginning in seven days.

## `endDateTime` (type: `string`):

Both modes. Local ISO date and time after the start. Must be supplied with the trip start.

## `fetchDetails` (type: `boolean`):

Return full specifications and public host profile. One detail-enrichment event is charged after each returned record with a successfully fetched host profile.

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

Default 20. Zero removes the output limit. Turo can still cap each location at 200 vehicles; this does not imply exhaustive coverage.

## `maxReviews` (type: `integer`):

Default 20. Guest reviews returned on each vehicle row, newest first, with the host reply when there is one. Set 0 to skip reviews. At most 1000 are read per vehicle.

## `proxy` (type: `object`):

Default is the Residential proxy group; keep it or bring your own proxy URLs. If the chosen connection cannot be used, the run continues on a backup connection.

## `resumeFromRunId` (type: `string`):

Continue an interrupted run with the same query, dates and detail settings. For recurring comparisons, use incrementalMode instead.

## `incrementalMode` (type: `boolean`):

Remember a baseline and return only changes: NEW, UPDATED (with changedFields), REAPPEARED, and optionally UNCHANGED/EXPIRED. UPDATED is driven by any change to the vehicle data (price, rating, trips, location, images, details, host), compared against the stored record; scrape timestamps, status flags and duplicate copies of the search card images and daily price are never compared. Use 'Ignore fields' to exclude more. Unlike resumeFromRunId, no prior run ID is required. Do not combine with resume when a baseline exists.

## `stateKey` (type: `string`):

Optional baseline name. Use different keys for separate scopes and avoid overlapping runs with the same key.

## `emitUnchanged` (type: `boolean`):

Return and bill unchanged rows in incremental mode.

## `emitExpired` (type: `boolean`):

Return and bill absent rows only after a complete nonempty scan in incremental mode. Capped and partial searches do not expire records.

## `ignoreFieldsForChanges` (type: `array`):

Output field names (dotted paths such as host.name for nested keys) that never make a vehicle UPDATED or appear in changedFields. They stay in the output. Example: images, averageDailyPrice.

## `mcpConnectors` (type: `array`):

Optionally send the scraped results into the apps you already use, via Model Context Protocol (MCP) connectors. Authorize a connector once under Apify -> Settings -> Integrations, then select it here. The connector receives a condensed, human-readable summary per item (title + key fields), not the full JSON; the complete record stays in the dataset. Leave empty to skip. Supported: Notion (https://mcp.notion.com/mcp), Linear (https://mcp.linear.app/sse), Airtable (https://mcp.airtable.com/mcp), Apify (https://mcp.apify.com).

## `notionParentPageUrl` (type: `string`):

URL (or id) of the Notion page under which item pages are created. Required to enable the Notion export; ignored by other connectors.

## `maxNotifyListings` (type: `integer`):

Cap on items written to each connector per run. Does not affect the dataset.

## Actor input object example

```json
{
  "mode": "search",
  "locations": [
    "Los Angeles, California"
  ],
  "age": 30,
  "sortBy": "relevance",
  "urls": [
    "https://turo.com/us/en/car-rental/united-states/los-angeles-ca",
    "https://turo.com/us/en/car-rental/united-states/los-angeles-ca/tesla",
    "https://turo.com/us/en/suv-rental/united-states/los-angeles-ca",
    "https://turo.com/us/en/car-rental/united-states/tesla",
    "https://turo.com/us/en/car-rental/united-states/los-angeles-ca/chevrolet/sonic/3864730"
  ],
  "fetchDetails": true,
  "maxItems": 20,
  "maxReviews": 20,
  "proxy": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  },
  "incrementalMode": false,
  "emitUnchanged": false,
  "emitExpired": false,
  "ignoreFieldsForChanges": [],
  "maxNotifyListings": 50
}
```

# Actor output Schema

## `overview` (type: `string`):

No description

## `detail` (type: `string`):

No description

## `reviews` (type: `string`):

No description

## `changes` (type: `string`):

No description

# 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 = {
    "locations": [
        "Los Angeles, California"
    ],
    "urls": [
        "https://turo.com/us/en/car-rental/united-states/los-angeles-ca",
        "https://turo.com/us/en/car-rental/united-states/los-angeles-ca/tesla",
        "https://turo.com/us/en/suv-rental/united-states/los-angeles-ca",
        "https://turo.com/us/en/car-rental/united-states/tesla",
        "https://turo.com/us/en/car-rental/united-states/los-angeles-ca/chevrolet/sonic/3864730"
    ],
    "proxy": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ]
    },
    "ignoreFieldsForChanges": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("abotapi/turo-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 = {
    "locations": ["Los Angeles, California"],
    "urls": [
        "https://turo.com/us/en/car-rental/united-states/los-angeles-ca",
        "https://turo.com/us/en/car-rental/united-states/los-angeles-ca/tesla",
        "https://turo.com/us/en/suv-rental/united-states/los-angeles-ca",
        "https://turo.com/us/en/car-rental/united-states/tesla",
        "https://turo.com/us/en/car-rental/united-states/los-angeles-ca/chevrolet/sonic/3864730",
    ],
    "proxy": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
    },
    "ignoreFieldsForChanges": [],
}

# Run the Actor and wait for it to finish
run = client.actor("abotapi/turo-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 '{
  "locations": [
    "Los Angeles, California"
  ],
  "urls": [
    "https://turo.com/us/en/car-rental/united-states/los-angeles-ca",
    "https://turo.com/us/en/car-rental/united-states/los-angeles-ca/tesla",
    "https://turo.com/us/en/suv-rental/united-states/los-angeles-ca",
    "https://turo.com/us/en/car-rental/united-states/tesla",
    "https://turo.com/us/en/car-rental/united-states/los-angeles-ca/chevrolet/sonic/3864730"
  ],
  "proxy": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  },
  "ignoreFieldsForChanges": []
}' |
apify call abotapi/turo-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,abotapi/turo-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/efU6JYaK40m4nVtGo/builds/fdOOuKkTYJLmclyX6/openapi.json
