# Bolt Food Restaurant & Menu Scraper (`teodor_banea/bolt-food-restaurant-menu-scraper`) Actor

Scrape Bolt Food restaurants, stores and full menus with prices across 260 cities in 16 countries. Search by keyword, list every venue delivering to an address, or pull full menus from venue URLs. Every menu row carries its venue, city and rating. Clean JSON, no browser.

- **URL**: https://apify.com/teodor\_banea/bolt-food-restaurant-menu-scraper.md
- **Developed by:** [Banea Teodor](https://apify.com/teodor_banea) (community)
- **Categories:** Automation, E-commerce, Developer tools
- **Stats:** 3 total users, 2 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: 5.00 out of 5 stars

## Pricing

from $1.80 / 1,000 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?

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

## Bolt Food Restaurant & Menu Scraper

Extract restaurants, grocery stores, full menus and live prices from **Bolt Food** — Bolt's
food-delivery platform — across **260 cities in 16 countries**. Search venues by keyword, pull the
complete list of everything that delivers to an address, or hand it venue URLs and get every menu
item with its price. Output is flat, typed JSON, ready for a spreadsheet or a pricing pipeline.

This is the **first and only Bolt Food scraper on the Apify Store**. Bolt Food is the delivery
market leader in several Central and Eastern European countries, and until now its pricing data was
not available anywhere as a self-serve feed.

### Why use this Actor?

- **A market nobody else covers.** Wolt, Glovo and Uber Eats scrapers are everywhere. Bolt Food —
  the leader in Estonia, Latvia, Lithuania, Slovakia and a strong number two in Czechia, Romania and
  Ukraine — had zero coverage before this Actor.
- **Real prices, not estimates.** Every menu item comes with its exact price in major currency
  units, the currency code, the pre-promotion price when something is discounted, and Bolt's own
  formatted price string.
- **Address-level accuracy.** Bolt Food shows a different venue list depending on where you are
  ordering to. Give the Actor a city, a street address, or exact coordinates and you get precisely
  the venues that deliver to that point — including the delivery fee and ETA *for that address*.
- **Fast and cheap.** No browser. The Actor talks to Bolt Food's own JSON API, so a full city venue
  list is a single HTTP request and a complete restaurant menu is one more. A measured run pulled
  **60 venues with their full menus — 3,107 priced items — in 61 requests and 5 seconds**, with no
  proxy and nothing retried.
- **A schema that matches our Wolt scraper.** Venue and menu fields use the same names and units as
  the [Wolt Restaurant & Menu Scraper](https://apify.com/store), so you can stack both platforms in
  one table and compare prices per dish without writing a mapping layer.

### Coverage

Every city below was verified against the live API on 2026-08-24 and returned a real venue feed.

| Country | Cities | Examples |
|---|---|---|
| Czechia | 59 | Prague, Brno, Ostrava, Plzen, Olomouc … |
| Romania | 44 | Bucharest, Cluj-Napoca, Timisoara, Iasi, Brasov … |
| Ukraine | 37 | Kyiv, Lviv, Kharkiv, Odesa, Dnipro … |
| Slovakia | 27 | Bratislava, Kosice, Nitra, Zilina, Presov … |
| Portugal | 21 | Lisbon, Porto, Braga, Coimbra, Aveiro … |
| Latvia | 14 | Riga, Jurmala, Liepaja, Daugavpils, Jelgava … |
| Lithuania | 13 | Vilnius, Kaunas, Klaipeda, Siauliai, Panevezys … |
| Poland | 12 | Warsaw, Krakow, Gdansk, Wroclaw, Poznan … |
| Estonia | 10 | Tallinn, Tartu, Narva, Pärnu, Kohtla-Järve … |
| Azerbaijan | 8 | Baku, Ganja, Sumqayit, Quba, Qabala … |
| Cyprus | 4 | Nicosia, Limassol, Larnaca, Paphos |
| Kenya | 3 | Nairobi, Nakuru … |
| Bulgaria | 2 | Sofia, Plovdiv |
| Georgia | 2 | Tbilisi, Batumi |
| Ghana | 2 | Accra, Kumasi |
| Malta | 2 | Valletta, Gozo |

City names are matched ignoring case and accents, so `bucuresti`, `Bucureşti` and `Bucharest` all
work. A name that is not a Bolt Food city stops the run immediately and suggests the closest
matches, so you never pay for a run aimed at the wrong place.

### The five modes

The mode says exactly what a run produces — there is no separate "also get menus" toggle to find.

| Mode | What you give it | What you get |
|---|---|---|
| **Search by keyword → full menus** *(default)* | Search terms + a location | Every menu item of every venue matching those terms |
| **All venues in a city → full menus** | A location | Every menu item of every venue delivering there |
| **Specific venue URLs → full menus** | Bolt Food venue page URLs | Every menu item of those venues |
| **Search by keyword → venue list only** | Search terms + a location | One row per matching venue, with ratings, fees and ETAs |
| **All venues in a city → venue list only** | A location | One row per venue delivering there (300–600 in a big city) |

The three menu modes are full crawls: they walk the venue listing, then each venue's menu, and return
**one row per menu item — each row carrying the venue it came from**, including its city, country,
address and rating, plus the search term that surfaced it. So a single CSV export answers "what does
every pizza place in Bucharest charge, and how good are they" with no joining.

The two venue-list modes stop at the listing and return one row per venue, with the full venue
record: coordinates, rating count, price tier, delivery fee, ETA, distance and venue type. Use those
when the venues themselves are what you are after.

### What does it cost?

Pricing is pay-per-result: you are charged for starting a run and for each result. The current rates
are shown on this Actor's page in the Apify Store. Every row in the dataset is one result, so what
you see is what you pay for — there is no hidden second output.

Nothing is capped by default, so a run returns everything it finds. Use these to size a run:

| What you want | Settings | Results |
|---|---|---|
| Quick look | Search `pizza`, Bucharest, max 10 venues | 10 venues |
| All venues in a city | Discovery, Prague | ~590 venues |
| Menus of 25 restaurants | Discovery + menus, max 25 venues | ~3,000 items |
| One competitor's full menu | Venue URLs, 1 URL | ~120 items |
| Weekly price tracking, 10 venues | Venue URLs, 10 URLs, weekly | ~1,200 items |

Set **Maximum results** if you want a hard ceiling on how many results a run can produce — it is the
most reliable way to bound a run. **Maximum venues** and **Maximum menu items per venue** narrow it
further. All three are optional and default to no limit.

### Input

| Field | Type | Description |
|---|---|---|
| `mode` | select | `searchMenus` (default), `discoveryMenus`, `venueUrls`, `search`, `discovery` |
| `searchQueries` | string\[] | Keywords, e.g. `["pizza", "sushi"]` (search modes) |
| `venueUrls` | URL\[] | Bolt Food venue pages (`venueUrls` mode) |
| `city` | string | Bolt Food city name, e.g. `Bucharest` |
| `address` | string | Optional street address inside that city, e.g. `Piata Victoriei` |
| `latitude` / `longitude` | string | Exact delivery point; overrides `city` and `address` |

| `expandCatalogs` | boolean | Walk into supermarket category pages (see *Stores vs restaurants*) |
| `language` | string | Language for venue and menu text, e.g. `en-US`, `ro`, `pl` |
| `maxVenues` | integer | Optional cap on venues scraped; `0` (default) = no limit |
| `maxMenuItemsPerVenue` | integer | Optional cap per venue; `0` (default) = the whole menu |
| `maxItemsTotal` | integer | Optional hard ceiling on billed results; `0` (default) = no limit |
| `maxConcurrency`, `maxRequestsPerMinute` | integer | Speed limits; the defaults suit every mode |

#### Example input

```json
{
    "mode": "searchMenus",
    "searchQueries": ["pizza", "sushi"],
    "city": "Bucharest",
    "address": "Piata Victoriei"
}
```

That scrapes the full menu of every pizza and sushi venue delivering to that address. Add
`"maxVenues": 25` or `"maxItemsTotal": 3000` if you want to bound it.

### Output

Menu items and venues each have a fixed set of fields — a field with no value is `null` rather than
missing, so every row has the same shape and CSV exports never shift columns.

#### Menu item

```json
{
    "venueId": "20499",
    "venueSlug": "20499-burger-king-piata-victoriei",
    "venueName": "Burger King Piața Victoriei",
    "url": "https://food.bolt.eu/en/325-bucharest/p/20499-burger-king-piata-victoriei/",
    "venueCity": "Bucharest",
    "venueCountry": "RO",
    "venueAddress": "Piața Victoriei 1",
    "venueRating": 4.29,
    "searchQuery": "burger",
    "itemId": "5629505651337892",
    "productId": "20499_CG_3751__ON_4__P_17600041",
    "name": "Summer Crunch Double Beef Meal",
    "description": "Double beef burger, fries and a drink",
    "categoryId": "4785074699661226",
    "categoryName": "Limited Offers",
    "categoryPath": "Limited Offers",
    "price": 53.9,
    "originalPrice": null,
    "isDiscounted": false,
    "currency": "RON",
    "priceFormatted": "53,90 lei",
    "availability": "in_stock",
    "isAvailable": true,
    "isPopular": true,
    "sellingUnit": null,
    "optionGroupCount": 5,
    "optionCount": 14,
    "tags": [],
    "imageUrl": "https://images.bolt.eu/store/2025/2025-03-18/31373193.jpeg",
    "language": "en-US",
    "scrapedAt": "2026-08-24T07:30:30.743Z"
}
```

#### Venue

```json
{
    "venueId": "3769",
    "slug": "3769-sara-green-victoriei",
    "name": "Sara Green - Victoriei",
    "url": "https://food.bolt.eu/en/325-bucharest/p/3769-sara-green-victoriei/",
    "description": "Restaurant Awards 2021 Verified Vegan",
    "city": "Bucharest",
    "cityId": 325,
    "country": "RO",
    "currency": "RON",
    "address": "Bd Lascar Catargiu 43A",
    "latitude": 44.450402,
    "longitude": 26.089393,
    "ratingScore": 4.88,
    "ratingVolume": 12570,
    "priceRange": 2,
    "online": true,
    "delivers": true,
    "takeaway": true,
    "deliveryEstimateMinutes": 10,
    "deliveryEstimateRange": "10-15",
    "deliveryBasePrice": 4.99,
    "distanceKm": 0.41,
    "tags": [],
    "phone": "+40774962246",
    "website": "https://www.saragreen.ro",
    "imageUrl": "https://images.bolt.eu/store/2020/2020-08-31/f9ed1df9.jpeg",
    "menuLayout": "simple",
    "isNew": false,
    "source": "discovery",
    "searchQuery": null,
    "scrapedAt": "2026-08-24T07:29:45.015Z"
}
```

#### Where things land

Everything goes to the run's **default dataset**, and that is the only dataset the Actor writes.
Each row there is one billed result. Which row type you get depends on the mode: menu items in the
three menu modes, venue records in the two venue-list modes. Never a mix, so one export is always
one shape.

Failed requests are reported in the run log with their URL, label and reason.

Prices are always decimal numbers in **major** currency units (`53.9` RON, `140` KES) with the
currency in a separate field — never a formatted string you have to parse. Ratings are on Bolt
Food's 1–5 star scale, with the exact average and the number of ratings behind it.

### Tips and tricks

- **Need the venue detail as well as the menus?** Run a venue-list mode for the venue records and a
  menu mode for the items, then join them on `venueId`. Menu rows already carry the venue's name,
  city, country, address and rating, so most price work needs only the menu run.
- **Track prices over time with `productId`.** It is Bolt's stable per-product identifier and
  survives menu rebuilds, unlike `itemId`. Join weekly runs on `venueId` + `productId` to get a
  clean price history per dish.
- **Use several search terms.** Bolt Food's search covers the whole city in one request and returns
  every match at once, so `["pizza", "burger", "sushi", "vegan"]` is four requests and very broad
  coverage. Venues are de-duplicated across queries, so you are never billed twice for one venue.
- **Compare addresses, not just cities.** Running the same city with two different `address` values
  shows which venues and fees change across neighbourhoods — that difference is the product for
  delivery-market analysis.
- **Keep the first run small.** Leave the prefilled caps alone, look at the output, then scale up.
- **Set `maxItemsTotal` deliberately.** It is the hard ceiling on your bill. Leaving it at `0`
  (unlimited) applies a 20,000-result safety cap and logs a warning.

#### Stores vs restaurants

Restaurant menus arrive complete in a single request. Supermarkets and other grid-layout stores are
different: Bolt returns only one sample product per top-level category, with the rest behind
category pages. Turn on **Expand large store catalogs** to walk into those pages — up to 60 extra
requests per store. When it is off, the run finishes with a warning naming the venues whose catalogs
were only partial, so a partial result is never presented as a complete one.

### Integrations

Connect the output to Google Sheets, Airtable, Slack, Zapier or Make from the Actor's
**Integrations** tab, or pull it straight from the API:

```
https://api.apify.com/v2/datasets/<DATASET_ID>/items?format=csv&clean=true
```

Schedule the Actor weekly in venue-URL mode for a competitor price monitor, or run discovery mode
monthly to track which venues enter and leave a market.

### Notes and limits

- Only public data is collected — the same venue, menu and price information any visitor sees on
  `food.bolt.eu`. No login, no personal data, and no paths that `food.bolt.eu/robots.txt` disallows.
- Bolt Food rate-limits per client identity rather than per IP, and the Actor gives every single
  request (and every retry) its own, so runs do not trip the limit and do not need a proxy to avoid
  it. Measured on 2026-08-24 from one IP with no proxy: 600 requests at 126/s, none rejected.
- Runs always go through Apify Proxy datacenter IPs, which the API serves normally (verified under
  load: 31 requests, none rejected). There is nothing to configure. Residential proxies are not used
  — they are billed by bandwidth, Bolt Food does not require them, and the per-result price covers
  datacenter traffic.
- Menus reflect what is orderable at the moment of the run. Items outside their serving window come
  back with `availability` set accordingly and `isAvailable: false`.
- Bolt Food gates its API on a web-app version string. If an update ever breaks every request at
  once, that is the cause, and it is a one-line fix in the Actor — please open an issue and it will
  be updated quickly.

### Support

Found a bug or need another field? Open an issue on the Actor's **Issues** tab. Feature requests
that make the output more useful for price monitoring are especially welcome.

# Actor input Schema

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

The first three modes crawl all the way to the menus and return one row per menu item, each row carrying the venue it came from. The last two stop at the venue list. Whichever rows a mode produces are the ones you are charged for.

## `searchQueries` (type: `array`):

Enter one or more search terms (e.g. "pizza", "sushi", "burger"). Bolt Food searches the whole city and returns every match at once, so a handful of queries covers a lot of ground. Only used in "Search venues by keyword" mode.

## `venueUrls` (type: `array`):

Enter Bolt Food venue page URLs. The full menu with prices is extracted for each one. Only used in "Venue URLs → full menus" mode.

## `city` (type: `string`):

Enter the Bolt Food city to scrape (e.g. "Bucharest", "Tallinn", "Prague"). All 260 Bolt Food cities across 16 countries are recognized by name, ignoring case and accents. If the name is not a Bolt Food city, the run stops and suggests the closest matches.

## `address` (type: `string`):

Optional street address inside the city, e.g. "Piata Victoriei". Bolt Food results depend on where you order to, so this narrows the venue list to what actually delivers to that spot. Requires City.

## `latitude` (type: `string`):

Advanced: exact latitude to deliver to. Set together with Longitude, this overrides City and Delivery address. Bolt Food results are per-address, so a precise point gives you exactly the venues that deliver there.

## `longitude` (type: `string`):

Advanced: exact longitude to deliver to. Set together with Latitude, this overrides City and Delivery address.

## `expandCatalogs` (type: `boolean`):

Supermarkets and other grid-layout stores hold thousands of products behind category pages, and Bolt only returns one sample product per category up front. Turn this on to walk into those categories (up to 60 extra requests per store). Restaurants never need it, and it can multiply your result count — consider setting "Maximum results" alongside it.

## `language` (type: `string`):

Language code for venue and menu texts, e.g. "en-US", "ro", "pl", "et". Bolt falls back to the venue's own language when a translation is missing.

## `maxMenuItemsPerVenue` (type: `integer`):

Optional cap on menu items per venue. Leave at 0 to get every venue's full menu.

## `maxVenues` (type: `integer`):

Optional cap on how many venues are scraped (and how many menus are fetched when menus are on). Leave at 0 to take every venue that delivers to the location — a large city feed holds 300–600.

## `maxItemsTotal` (type: `integer`):

Optional hard ceiling on billed results (venues, or menu items when menus are on). Leave at 0 for no limit. This is the single most reliable way to bound what a run can cost.

## `maxConcurrency` (type: `integer`):

How many requests run in parallel. The default suits every mode; there is rarely a reason to change it.

## `maxRequestsPerMinute` (type: `integer`):

Throttle the overall request rate. Raise it for very large runs, or lower it to be gentler on the target.

## Actor input object example

```json
{
  "mode": "searchMenus",
  "searchQueries": [
    "pizza"
  ],
  "venueUrls": [
    {
      "url": "https://food.bolt.eu/en/325-bucharest/p/3769-sara-green-victoriei/"
    }
  ],
  "city": "Bucharest",
  "address": "Piata Victoriei",
  "latitude": "44.4268",
  "longitude": "26.1025",
  "expandCatalogs": false,
  "language": "en-US",
  "maxMenuItemsPerVenue": 0,
  "maxVenues": 0,
  "maxItemsTotal": 0,
  "maxConcurrency": 10,
  "maxRequestsPerMinute": 300
}
```

# Actor output Schema

## `results` (type: `string`):

Menu items with prices, or venue records, depending on the mode. Menu rows carry the venue they belong to, including its city, country, address and rating. Every field is present on every row, so no cleaning step is needed to get a stable shape.

# 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 = {
    "searchQueries": [
        "pizza"
    ],
    "venueUrls": [
        {
            "url": "https://food.bolt.eu/en/325-bucharest/p/3769-sara-green-victoriei/"
        }
    ],
    "city": "Bucharest"
};

// Run the Actor and wait for it to finish
const run = await client.actor("teodor_banea/bolt-food-restaurant-menu-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 = {
    "searchQueries": ["pizza"],
    "venueUrls": [{ "url": "https://food.bolt.eu/en/325-bucharest/p/3769-sara-green-victoriei/" }],
    "city": "Bucharest",
}

# Run the Actor and wait for it to finish
run = client.actor("teodor_banea/bolt-food-restaurant-menu-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 '{
  "searchQueries": [
    "pizza"
  ],
  "venueUrls": [
    {
      "url": "https://food.bolt.eu/en/325-bucharest/p/3769-sara-green-victoriei/"
    }
  ],
  "city": "Bucharest"
}' |
apify call teodor_banea/bolt-food-restaurant-menu-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,teodor_banea/bolt-food-restaurant-menu-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/wz54cDwnwCLMpGBmO/builds/xf7jkekZeJiekNidS/openapi.json
