By the end of this tutorial, you will have a short Python script that turns a Turo search around any map point into structured rows: year, make and model, vehicle type, average daily price with its currency, rating, completed trips, the host's Turo page and a Google Maps link to the approximate pickup spot.
We use Python and the Apify Actor Turo Car Rental Scraper (reventadata/turo-scraper), an unofficial tool that reads the same public search endpoint a browser does. You will set a search point, run the Actor, and filter rows by make, vehicle type and daily price. The row shown below is a sample from a capture dated 2026-10-12, parsed with the Actor's own parser.
⏱️ Time estimate: one sitting: install one package, set one token, run three short Python blocks.
Prerequisites
To follow this tutorial, you will need:
-
Python 3.11+ (
apify-client3.3.0 requires it). - The Apify API client for Python.
- An Apify account and its API token, which sits on the API & Integrations page in Apify Console.
Install the client:
pip install "apify-client~=3.0"
The code reads the token from an environment variable:
export APIFY_API_TOKEN="your_apify_token_here"
What you'll achieve
You will turn one Turo search into one row per car. Each row carries the vehicle id, url and title (2023 Ford Escape), make, model, year, vehicleType, dailyPrice and currency, rating, completedTrips, the isAllStarHost and isNewListing flags, hostId and hostUrl, city, state, latitude and longitude, distance from your search point, Turo's tag labels and the first photo.
Note: Turo's search results carry a host id, not a host name, so rows have no host name.
Example use case: Suppose you want SUVs near downtown Los Angeles under a daily budget. You would set the search point, vehicleTypes: ["SUV"] and maxPrice, and every saved row would be an SUV whose average daily price is at or below your bound.
Step 1: Access Turo Car Rental Scraper
Go to the Turo Car Rental Scraper page on Apify Store. The README lists every input and output field. In this tutorial we drive the Actor from Python, so everything after this step happens in code.
Step 2: Configure the search point
The two required inputs are latitude and longitude: the centre of the search. Turo searches the area around that point. For downtown Los Angeles that is 34.0549 and -118.2426, the Actor's prefill.
Here is the setup. It searches around that point and saves at most 10 cars:
import os
from apify_client import ApifyClient
# 1. Initialize ApifyClient with the token from the environment
client = ApifyClient(os.environ["APIFY_API_TOKEN"])
# 2. Define the search: one map point, at most 10 cars
run_input = {
"latitude": 34.0549,
"longitude": -118.2426,
"maxItems": 10,
}
💡 Tip: the Actor sends the point to Turo with the country set to US, so pick a point in the United States.
Each run makes one search request and keeps up to maxItems cars (default 100) from what Turo returns. There are no pages to walk, so a larger maxItems cannot add cars beyond that one response.
Step 3: Add optional filters
| Parameter | Type | What it keeps |
|---|---|---|
makes |
array | Only these makes, e.g. ["Ford", "Tesla"]. Sent to Turo and re-checked on each car, ignoring case and accents. |
vehicleTypes |
array | Only these Turo types, e.g. ["SUV"]. Same handling as makes. |
minPrice |
number | Cars whose average daily price is at least this. |
maxPrice |
number | Cars whose average daily price is at most this. |
maxItems |
integer | Cap on saved rows (minimum 1, default 100). |
When minPrice or maxPrice is set, a car without a daily price is dropped. Prices are compared in the currency Turo returns (USD in the capture).
Three example combinations:
-
LA SUVs: the prefill point plus
vehicleTypes: ["SUV"]. -
Teslas only:
makes: ["Tesla"], no price bound. -
Budget band:
minPrice: 30,maxPrice: 60.
⚠️ A missing
latitudeorlongitude, or a negative price bound, is refused before the run starts. A latitude outside -90..90, a longitude outside -180..180, orminPriceabovemaxPricestops the run with an error at the start.
Step 4: Run the Actor
Now start the run with client.actor("reventadata/turo-scraper").call(run_input=run_input). The call starts the Actor on Apify and waits for it to finish before returning the run:
# 3. Start the Actor run and wait for it to finish
print("Starting Turo extraction...")
run = client.actor("reventadata/turo-scraper").call(run_input=run_input)
if run is None or run.status != "SUCCEEDED":
status = run.status if run else "unknown"
raise SystemExit(f"Run did not succeed (status: {status}).")
# 4. Fetch the saved cars
items = client.dataset(run.default_dataset_id).list_items().items
print(f"Retrieved {len(items)} cars.\n")
Behind the scenes:
- The Actor builds Turo's search request for your point (an area search sorted by relevance, daily rentals) with your makes and types, and sends it through Bright Data's Web Unlocker, the route that reached Turo for the Actor's published example tasks on 2026-10-13.
- It checks that the answer is a
vehicleslist; a body without one counts as a failed fetch, even when it came back with HTTP 200. - It applies your filters, skips a car id it has already saved, and stops at
maxItems.
Step 5: View and download results
Loop over the rows to print the parts you need:
for item in items:
print(item["title"], "-", item.get("vehicleType"))
print(f" Daily price: {item.get('dailyPrice')} {item.get('currency')}")
print(f" Rating: {item.get('rating')} over {item.get('completedTrips')} trips")
print(f" Where: {item.get('city')}, {item.get('state')}")
print(f" Map: {item.get('googleMapsUrl')}")
print(f" Listing: {item['url']}")
print("-" * 40)
Here is one row, a sample from a capture dated 2026-10-12 (a Los Angeles search) parsed with the Actor's own parser. A saved row also carries scrapedAt, added at save time:
{
"id": "3905389",
"title": "2023 Ford Escape",
"url": "https://turo.com/us/en/vehicle/3905389",
"make": "Ford",
"model": "Escape",
"year": 2023,
"vehicleType": "SUV",
"dailyPrice": 39.42,
"currency": "USD",
"rating": 5.0,
"completedTrips": 6,
"isAllStarHost": true,
"isNewListing": false,
"hostId": 42612460,
"hostUrl": "https://turo.com/us/en/drivers/42612460",
"seoCategory": "SUV",
"city": "Los Angeles",
"state": "CA",
"country": "US",
"latitude": 34.07593356671111,
"longitude": -118.25088431942467,
"distance": 1.5274172055900106,
"distanceUnit": "MILES",
"googleMapsUrl": "https://www.google.com/maps/search/?api=1&query=34.07593356671111,-118.25088431942467",
"tags": [
"Unlimited miles"
],
"imageUrl": "https://images.turo.com/media/vehicle/images/9RjG2t7cSw-VjnV5orp5IA.jpg"
}
Apify datasets export as JSON, CSV, XML, Excel, HTML table, RSS or JSONL.
Understanding your results
The fields that matter most:
-
dailyPriceandcurrency: Turo's own average daily price for the car, passed through as a number (39.42USDin the sample). The Actor does not compute it. -
ratingandcompletedTrips: read them together. The sample car shows5.0over6trips. -
isAllStarHost: Turo's All-Star Host badge.isNewListing: Turo's new-listing flag. -
hostIdandhostUrl: the host's public Turo page,turo.com/us/en/drivers/<id>. There is no host name. -
latitude,longitudeandgoogleMapsUrl: the car's approximate pickup point as Turo reports it, and a Google Maps link the Actor builds from those two numbers. -
distanceanddistanceUnit: how far that point is from your search centre, in Turo's unit (MILESin the sample, about 1.5 miles). -
vehicleTypeandseoCategory: Turo's type label and its category, bothSUVin the sample. -
tags: Turo's tag labels, such asUnlimited miles. -
urlandimageUrl: the car's page on turo.com and its first photo.
Pro tips
-
Move the point, not the count. One run is one search around one point. To cover a whole metro, run several points (airport, downtown, suburbs) and merge the rows on
id. -
Know where each filter runs.
makesandvehicleTypesgo into Turo's own search request and are re-checked on each car. Price bounds are applied only by the Actor, to each car after the response arrives. -
Use the type labels Turo uses.
vehicleTypesmatches Turo's label, ignoring case and accents. CheckvehicleTypein a first run before filtering on it. -
Track prices over time. Schedule the same input daily and keep
id,dailyPriceandscrapedAt; the rows become a price history per car. -
Start small. Keep
maxItems: 10until your downstream code handles the fields, then raise it. -
Fetch every row of a large dataset.
list_items()makes one API call;client.dataset(run.default_dataset_id).iterate_items()keeps calling until it has every item.
Troubleshooting
-
Run FAILED with zero rows: a response with no
vehicleslist endsFAILED; an over-strict filter that still returns cars endsSUCCEEDEDwith an empty dataset and a status message saying so. Widen the price band or check yourmakesspelling. -
Run stops at once: the coordinates are out of range, or
minPriceis abovemaxPrice. Fewer rows than expected: read the log. It prints
Search returned N vehicle(s)., oneRejected N: <reason>line per filter (make not requested,vehicle type not requested,no daily price,daily price below minPrice,daily price above maxPrice), thenPushed N row(s).. From Python:client.log(run.id).get().Run waits to start: pass
wait_for_resources=Truetocall()and the client retries the start while your account lacks memory or a free run slot.
For help, use the Actor's Store page.
Pricing and next steps
For current pricing and details, see the Store page.
To repeat a search, save the input as a task and add an Apify schedule, which runs an Actor or task on a cron expression (0 8 * * * is every day at 8 AM). To react when a run finishes, add a webhook on ACTOR.RUN.SUCCEEDED; its payload carries the run object, including defaultDatasetId.
Which cities or car types are you planning to track on Turo? Drop a comment! 💬
Top comments (0)